source: bootcd/isolinux/syslinux-6.03/core/fs/fs.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 9.9 KB
Line 
1#include <sys/file.h>
2#include <stdio.h>
3#include <stdbool.h>
4#include <string.h>
5#include <unistd.h>
6#include <fcntl.h>
7#include <dprintf.h>
8#include <syslinux/sysappend.h>
9#include "core.h"
10#include "dev.h"
11#include "fs.h"
12#include "cache.h"
13
14/* The currently mounted filesystem */
15__export struct fs_info *this_fs = NULL;                /* Root filesystem */
16
17/* Actual file structures (we don't have malloc yet...) */
18__export struct file files[MAX_OPEN];
19
20/* Symlink hard limits */
21#define MAX_SYMLINK_CNT 20
22#define MAX_SYMLINK_BUF 4096
23
24/*
25 * Get a new inode structure
26 */
27struct inode *alloc_inode(struct fs_info *fs, uint32_t ino, size_t data)
28{
29    struct inode *inode = zalloc(sizeof(struct inode) + data);
30    if (inode) {
31        inode->fs = fs;
32        inode->ino = ino;
33        inode->refcnt = 1;
34    }
35    return inode;
36}
37
38/*
39 * Free a refcounted inode
40 */
41void put_inode(struct inode *inode)
42{
43    while (inode) {
44        struct inode *dead = inode;
45        int refcnt = --(dead->refcnt);
46        dprintf("put_inode %p name %s refcnt %u\n", dead, dead->name, refcnt);
47        if (refcnt)
48            break;              /* We still have references */
49        inode = dead->parent;
50        if (dead->name)
51            free((char *)dead->name);
52        free(dead);
53    }
54}
55
56/*
57 * Get an empty file structure
58 */
59static struct file *alloc_file(void)
60{
61    int i;
62    struct file *file = files;
63
64    for (i = 0; i < MAX_OPEN; i++) {
65        if (!file->fs)
66            return file;
67        file++;
68    }
69
70    return NULL;
71}
72
73/*
74 * Close and free a file structure
75 */
76static inline void free_file(struct file *file)
77{
78    memset(file, 0, sizeof *file);
79}
80
81__export void _close_file(struct file *file)
82{
83    if (file->fs)
84        file->fs->fs_ops->close_file(file);
85    free_file(file);
86}
87
88/*
89 * Find and open the configuration file
90 */
91__export int open_config(void)
92{
93    int fd, handle;
94    struct file_info *fp;
95
96    fd = opendev(&__file_dev, NULL, O_RDONLY);
97    if (fd < 0)
98        return -1;
99
100    fp = &__file_info[fd];
101
102    handle = this_fs->fs_ops->open_config(&fp->i.fd);
103    if (handle < 0) {
104        close(fd);
105        errno = ENOENT;
106        return -1;
107    }
108
109    fp->i.offset = 0;
110    fp->i.nbytes = 0;
111
112    return fd;
113}
114
115__export void mangle_name(char *dst, const char *src)
116{
117    this_fs->fs_ops->mangle_name(dst, src);
118}
119
120size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors)
121{
122    bool have_more;
123    size_t bytes_read;
124    struct file *file;
125
126    file = handle_to_file(*handle);
127    bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more);
128
129    /*
130     * If we reach EOF, the filesystem driver will have already closed
131     * the underlying file... this really should be cleaner.
132     */
133    if (!have_more) {
134        _close_file(file);
135        *handle = 0;
136    }
137
138    return bytes_read;
139}
140
141int searchdir(const char *name, int flags)
142{
143    static char root_name[] = "/";
144    struct file *file;
145    char *path, *inode_name, *next_inode_name;
146    struct inode *tmp, *inode = NULL;
147    int symlink_count = MAX_SYMLINK_CNT;
148
149    dprintf("searchdir: %s  root: %p  cwd: %p\n",
150            name, this_fs->root, this_fs->cwd);
151
152    if (!(file = alloc_file()))
153        goto err_no_close;
154    file->fs = this_fs;
155
156    /* if we have ->searchdir method, call it */
157    if (file->fs->fs_ops->searchdir) {
158        file->fs->fs_ops->searchdir(name, flags, file);
159
160        if (file->inode)
161            return file_to_handle(file);
162        else
163            goto err;
164    }
165
166    /* else, try the generic-path-lookup method */
167
168    /* Copy the path */
169    path = strdup(name);
170    if (!path) {
171        dprintf("searchdir: Couldn't copy path\n");
172        goto err_path;
173    }
174
175    /* Work with the current directory, by default */
176    inode = get_inode(this_fs->cwd);
177    if (!inode) {
178        dprintf("searchdir: Couldn't use current directory\n");
179        goto err_curdir;
180    }
181
182    for (inode_name = path; inode_name; inode_name = next_inode_name) {
183        /* Root directory? */
184        if (inode_name[0] == '/') {
185            next_inode_name = inode_name + 1;
186            inode_name = root_name;
187        } else {
188            /* Find the next inode name */
189            next_inode_name = strchr(inode_name + 1, '/');
190            if (next_inode_name) {
191                /* Terminate the current inode name and point to next */
192                *next_inode_name++ = '\0';
193            }
194        }
195        if (next_inode_name) {
196            /* Advance beyond redundant slashes */
197            while (*next_inode_name == '/')
198                next_inode_name++;
199
200            /* Check if we're at the end */
201            if (*next_inode_name == '\0')
202                next_inode_name = NULL;
203        }
204        dprintf("searchdir: inode_name: %s\n", inode_name);
205        if (next_inode_name)
206            dprintf("searchdir: Remaining: %s\n", next_inode_name);
207
208        /* Root directory? */
209        if (inode_name[0] == '/') {
210            /* Release any chain that's already been established */
211            put_inode(inode);
212            inode = get_inode(this_fs->root);
213            continue;
214        }
215
216        /* Current directory? */
217        if (!strncmp(inode_name, ".", sizeof "."))
218            continue;
219
220        /* Parent directory? */
221        if (!strncmp(inode_name, "..", sizeof "..")) {
222            /* If there is no parent, just ignore it */
223            if (!inode->parent)
224                continue;
225
226            /* Add a reference to the parent so we can release the child */
227            tmp = get_inode(inode->parent);
228
229            /* Releasing the child will drop the parent back down to 1 */
230            put_inode(inode);
231
232            inode = tmp;
233            continue;
234        }
235
236        /* Anything else */
237        tmp = inode;
238        inode = this_fs->fs_ops->iget(inode_name, inode);
239        if (!inode) {
240            /* Failure.  Release the chain */
241            put_inode(tmp);
242            break;
243        }
244
245        /* Sanity-check */
246        if (inode->parent && inode->parent != tmp) {
247            dprintf("searchdir: iget returned a different parent\n");
248            put_inode(inode);
249            inode = NULL;
250            put_inode(tmp);
251            break;
252        }
253        inode->parent = tmp;
254        inode->name = strdup(inode_name);
255        dprintf("searchdir: path component: %s\n", inode->name);
256
257        /* Symlink handling */
258        if (inode->mode == DT_LNK) {
259            char *new_path;
260            int new_len, copied;
261
262            /* target path + NUL */
263            new_len = inode->size + 1;
264
265            if (next_inode_name) {
266                /* target path + slash + remaining + NUL */
267                new_len += strlen(next_inode_name) + 1;
268            }
269
270            if (!this_fs->fs_ops->readlink ||
271                /* limit checks */
272                --symlink_count == 0 ||
273                new_len > MAX_SYMLINK_BUF)
274                goto err_new_len;
275
276            new_path = malloc(new_len);
277            if (!new_path)
278                goto err_new_path;
279
280            copied = this_fs->fs_ops->readlink(inode, new_path);
281            if (copied <= 0)
282                goto err_copied;
283            new_path[copied] = '\0';
284            dprintf("searchdir: Symlink: %s\n", new_path);
285
286            if (next_inode_name) {
287                new_path[copied] = '/';
288                strcpy(new_path + copied + 1, next_inode_name);
289                dprintf("searchdir: New path: %s\n", new_path);
290            }
291
292            free(path);
293            path = next_inode_name = new_path;
294
295            /* Add a reference to the parent so we can release the child */
296            tmp = get_inode(inode->parent);
297
298            /* Releasing the child will drop the parent back down to 1 */
299            put_inode(inode);
300
301            inode = tmp;
302            continue;
303err_copied:
304            free(new_path);
305err_new_path:
306err_new_len:
307            put_inode(inode);
308            inode = NULL;
309            break;
310        }
311
312        /* If there's more to process, this should be a directory */
313        if (next_inode_name && inode->mode != DT_DIR) {
314            dprintf("searchdir: Expected a directory\n");
315            put_inode(inode);
316            inode = NULL;
317            break;
318        }
319    }
320err_curdir:
321    free(path);
322err_path:
323    if (!inode) {
324        dprintf("searchdir: Not found\n");
325        goto err;
326    }
327
328    file->inode  = inode;
329    file->offset = 0;
330
331    return file_to_handle(file);
332
333err:
334    dprintf("serachdir: error seraching file %s\n", name);
335    _close_file(file);
336err_no_close:
337    return -1;
338}
339
340__export int open_file(const char *name, int flags, struct com32_filedata *filedata)
341{
342    int rv;
343    struct file *file;
344    char mangled_name[FILENAME_MAX];
345
346    dprintf("open_file %s\n", name);
347
348    mangle_name(mangled_name, name);
349    rv = searchdir(mangled_name, flags);
350
351    if (rv < 0)
352        return rv;
353
354    file = handle_to_file(rv);
355
356    if (file->inode->mode != DT_REG) {
357        _close_file(file);
358        return -1;
359    }
360
361    filedata->size      = file->inode->size;
362    filedata->blocklg2  = SECTOR_SHIFT(file->fs);
363    filedata->handle    = rv;
364
365    return rv;
366}
367
368__export void close_file(uint16_t handle)
369{
370    struct file *file;
371
372    if (handle) {
373        file = handle_to_file(handle);
374        _close_file(file);
375    }
376}
377
378__export char *fs_uuid(void)
379{
380    if (!this_fs || !this_fs->fs_ops || !this_fs->fs_ops->fs_uuid)
381        return NULL;
382    return this_fs->fs_ops->fs_uuid(this_fs);
383}
384
385/*
386 * it will do:
387 *    initialize the memory management function;
388 *    set up the vfs fs structure;
389 *    initialize the device structure;
390 *    invoke the fs-specific init function;
391 *    initialize the cache if we need one;
392 *    finally, get the current inode for relative path looking.
393 *
394 * ops is a ptr list for several fs_ops
395 */
396__bss16 uint16_t SectorSize, SectorShift;
397
398void fs_init(const struct fs_ops **ops, void *priv)
399{
400    static struct fs_info fs;   /* The actual filesystem buffer */
401    int blk_shift = -1;
402    struct device *dev = NULL;
403
404    /* Default name for the root directory */
405    fs.cwd_name[0] = '/';
406
407    while ((blk_shift < 0) && *ops) {
408        /* set up the fs stucture */
409        fs.fs_ops = *ops;
410
411        /*
412         * This boldly assumes that we don't mix FS_NODEV filesystems
413         * with FS_DEV filesystems...
414         */
415        if (fs.fs_ops->fs_flags & FS_NODEV) {
416            fs.fs_dev = NULL;
417        } else {
418            if (!dev)
419                dev = device_init(priv);
420            fs.fs_dev = dev;
421        }
422        /* invoke the fs-specific init code */
423        blk_shift = fs.fs_ops->fs_init(&fs);
424        ops++;
425    }
426    if (blk_shift < 0) {
427        printf("No valid file system found!\n");
428        while (1)
429                ;
430    }
431    this_fs = &fs;
432
433    /* initialize the cache only if it wasn't already initialized
434     * by the fs driver */
435    if (fs.fs_dev && fs.fs_dev->cache_data && !fs.fs_dev->cache_init)
436        cache_init(fs.fs_dev, blk_shift);
437
438    /* start out in the root directory */
439    if (fs.fs_ops->iget_root) {
440        fs.root = fs.fs_ops->iget_root(&fs);
441        fs.cwd = get_inode(fs.root);
442        dprintf("init: root inode %p, cwd inode %p\n", fs.root, fs.cwd);
443    }
444
445    if (fs.fs_ops->chdir_start) {
446            if (fs.fs_ops->chdir_start() < 0)
447                    printf("Failed to chdir to start directory\n");
448    }
449
450    SectorShift = fs.sector_shift;
451    SectorSize  = fs.sector_size;
452
453    /* Add FSUUID=... string to cmdline */
454    sysappend_set_fs_uuid();
455
456}
Note: See TracBrowser for help on using the repository browser.