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