Home | History | Annotate | Download | only in fs
      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  */
     27 struct 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  */
     41 void 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  */
     59 static 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  */
     76 static 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 
    120 size_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 
    141 int 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;
    303 err_copied:
    304 	    free(new_path);
    305 err_new_path:
    306 err_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     }
    320 err_curdir:
    321     free(path);
    322 err_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 
    333 err:
    334     dprintf("serachdir: error seraching file %s\n", name);
    335     _close_file(file);
    336 err_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 
    398 void 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 }
    457