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  */
alloc_inode(struct fs_info * fs,uint32_t ino,size_t data)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  */
put_inode(struct inode * inode)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  */
alloc_file(void)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  */
free_file(struct file * file)76 static inline void free_file(struct file *file)
77 {
78     memset(file, 0, sizeof *file);
79 }
80 
_close_file(struct file * file)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  */
open_config(void)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 
mangle_name(char * dst,const char * src)115 __export void mangle_name(char *dst, const char *src)
116 {
117     this_fs->fs_ops->mangle_name(dst, src);
118 }
119 
pmapi_read_file(uint16_t * handle,void * buf,size_t sectors)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 
searchdir(const char * name,int flags)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 
open_file(const char * name,int flags,struct com32_filedata * filedata)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 
close_file(uint16_t handle)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 
fs_uuid(void)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 
fs_init(const struct fs_ops ** ops,void * priv)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