48856c9ed66c13efe2312fc83fe2e8b4c6dc129c
[profile/ivi/syslinux.git] / core / fs / fs.c
1 #include <stdio.h>
2 #include <stdbool.h>
3 #include <string.h>
4 #include <dprintf.h>
5 #include "fs.h"
6 #include "cache.h"
7
8 /* The currently mounted filesystem */
9 struct fs_info *this_fs = NULL;         /* Root filesystem */
10
11 /* Actual file structures (we don't have malloc yet...) */
12 struct file files[MAX_OPEN];
13
14 /* Symlink hard limits */
15 #define MAX_SYMLINK_CNT 20
16 #define MAX_SYMLINK_BUF 4096
17
18 /*
19  * Get a new inode structure
20  */
21 struct inode *alloc_inode(struct fs_info *fs, uint32_t ino, size_t data)
22 {
23     struct inode *inode = zalloc(sizeof(struct inode) + data);
24     if (inode) {
25         inode->fs = fs;
26         inode->ino = ino;
27         inode->refcnt = 1;
28     }
29     return inode;
30 }
31
32 /*
33  * Free a refcounted inode
34  */
35 void put_inode(struct inode *inode)
36 {
37     while (inode && --inode->refcnt == 0) {
38         struct inode *dead = inode;
39         inode = inode->parent;
40         free(dead);
41     }
42 }
43
44 /*
45  * Get an empty file structure
46  */
47 static struct file *alloc_file(void)
48 {
49     int i;
50     struct file *file = files;
51
52     for (i = 0; i < MAX_OPEN; i++) {
53         if (!file->fs)
54             return file;
55         file++;
56     }
57
58     return NULL;
59 }
60
61 /*
62  * Close and free a file structure
63  */
64 static inline void free_file(struct file *file)
65 {
66     memset(file, 0, sizeof *file);
67 }
68
69 void _close_file(struct file *file)
70 {
71     if (file->fs)
72         file->fs->fs_ops->close_file(file);
73     free_file(file);
74 }
75
76 /*
77  * Convert between a 16-bit file handle and a file structure
78  */
79
80 void load_config(void)
81 {
82     int err;
83
84     err = this_fs->fs_ops->load_config();
85
86     if (err)
87         printf("ERROR: No configuration file found\n");
88 }
89
90 void pm_mangle_name(com32sys_t *regs)
91 {
92     const char *src = MK_PTR(regs->ds, regs->esi.w[0]);
93     char       *dst = MK_PTR(regs->es, regs->edi.w[0]);
94
95     mangle_name(dst, src);
96 }
97
98 void mangle_name(char *dst, const char *src)
99 {
100     this_fs->fs_ops->mangle_name(dst, src);
101 }
102
103 void getfssec(com32sys_t *regs)
104 {
105     int sectors;
106     bool have_more;
107     uint32_t bytes_read;
108     char *buf;
109     struct file *file;
110     uint16_t handle;
111
112     sectors = regs->ecx.w[0];
113
114     handle = regs->esi.w[0];
115     file = handle_to_file(handle);
116
117     buf = MK_PTR(regs->es, regs->ebx.w[0]);
118     bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more);
119
120     /*
121      * If we reach EOF, the filesystem driver will have already closed
122      * the underlying file... this really should be cleaner.
123      */
124     if (!have_more) {
125         _close_file(file);
126         regs->esi.w[0] = 0;
127     }
128
129     regs->ecx.l = bytes_read;
130 }
131
132 void getfsbytes(com32sys_t *regs)
133 {
134     int sectors;
135     bool have_more;
136     uint32_t bytes_read;
137     char *buf;
138     struct file *file;
139     uint16_t handle;
140
141     handle = regs->esi.w[0];
142     file = handle_to_file(handle);
143
144     sectors = regs->ecx.w[0] >> SECTOR_SHIFT(file->fs);
145
146     buf = MK_PTR(regs->es, regs->ebx.w[0]);
147     bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more);
148
149     /*
150      * If we reach EOF, the filesystem driver will have already closed
151      * the underlying file... this really should be cleaner.
152      */
153     if (!have_more) {
154         _close_file(file);
155         regs->esi.w[0] = 0;
156     }
157
158     regs->ecx.l = bytes_read;
159 }
160
161 size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors)
162 {
163     bool have_more;
164     size_t bytes_read;
165     struct file *file;
166
167     file = handle_to_file(*handle);
168     bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more);
169
170     /*
171      * If we reach EOF, the filesystem driver will have already closed
172      * the underlying file... this really should be cleaner.
173      */
174     if (!have_more) {
175         _close_file(file);
176         *handle = 0;
177     }
178
179     return bytes_read;
180 }
181
182 void pm_searchdir(com32sys_t *regs)
183 {
184     char *name = MK_PTR(regs->ds, regs->edi.w[0]);
185     int rv;
186
187     rv = searchdir(name);
188     if (rv < 0) {
189         regs->esi.w[0]  = 0;
190         regs->eax.l     = 0;
191         regs->eflags.l |= EFLAGS_ZF;
192     } else {
193         regs->esi.w[0]  = rv;
194         regs->eax.l     = handle_to_file(rv)->inode->size;
195         regs->eflags.l &= ~EFLAGS_ZF;
196     }
197 }
198
199 int searchdir(const char *name)
200 {
201     struct inode *inode = NULL;
202     struct inode *parent = NULL;
203     struct file *file;
204     char *pathbuf = NULL;
205     char *part, *p, echar;
206     int symlink_count = MAX_SYMLINK_CNT;
207
208     if (!(file = alloc_file()))
209         goto err_no_close;
210     file->fs = this_fs;
211
212     /* if we have ->searchdir method, call it */
213     if (file->fs->fs_ops->searchdir) {
214         file->fs->fs_ops->searchdir(name, file);
215
216         if (file->inode)
217             return file_to_handle(file);
218         else
219             goto err;
220     }
221
222     /* else, try the generic-path-lookup method */
223
224     parent = get_inode(this_fs->cwd);
225     p = pathbuf = strdup(name);
226     if (!pathbuf)
227         goto err;
228
229     do {
230     got_link:
231         if (*p == '/') {
232             put_inode(parent);
233             parent = get_inode(this_fs->root);
234         }
235
236         do {
237             inode = get_inode(parent);
238
239             while (*p == '/')
240                 p++;
241
242             if (!*p)
243                 break;
244
245             part = p;
246             while ((echar = *p) && echar != '/')
247                 p++;
248             *p++ = '\0';
249
250             if (part[0] == '.' && part[1] == '.' && part[2] == '\0') {
251                 if (inode->parent) {
252                     put_inode(parent);
253                     parent = get_inode(inode->parent);
254                     put_inode(inode);
255                     inode = NULL;
256                     if (!echar) {
257                         /* Terminal double dots */
258                         inode = parent;
259                         parent = inode->parent ?
260                             get_inode(inode->parent) : NULL;
261                     }
262                 }
263             } else if (part[0] != '.' || part[1] != '\0') {
264                 inode = this_fs->fs_ops->iget(part, parent);
265                 if (!inode)
266                     goto err;
267                 if (inode->mode == DT_LNK) {
268                     char *linkbuf, *q;
269                     int name_len = echar ? strlen(p) : 0;
270                     int total_len = inode->size + name_len + 2;
271                     int link_len;
272
273                     if (!this_fs->fs_ops->readlink ||
274                         --symlink_count == 0       ||      /* limit check */
275                         total_len > MAX_SYMLINK_BUF)
276                         goto err;
277
278                     linkbuf = malloc(total_len);
279                     if (!linkbuf)
280                         goto err;
281
282                     link_len = this_fs->fs_ops->readlink(inode, linkbuf);
283                     if (link_len <= 0) {
284                         free(linkbuf);
285                         goto err;
286                     }
287
288                     q = linkbuf + link_len;
289
290                     if (echar) {
291                         if (link_len > 0 && q[-1] != '/')
292                             *q++ = '/';
293
294                         memcpy(q, p, name_len+1);
295                     } else {
296                         *q = '\0';
297                     }
298
299                     free(pathbuf);
300                     p = pathbuf = linkbuf;
301                     put_inode(inode);
302                     inode = NULL;
303                     goto got_link;
304                 }
305
306                 inode->parent = parent;
307                 parent = NULL;
308
309                 if (!echar)
310                     break;
311
312                 if (inode->mode != DT_DIR)
313                     goto err;
314
315                 parent = inode;
316                 inode = NULL;
317             }
318         } while (echar);
319     } while (0);
320
321     free(pathbuf);
322     pathbuf = NULL;
323     put_inode(parent);
324     parent = NULL;
325
326     if (!inode)
327         goto err;
328
329     file->inode  = inode;
330     file->offset = 0;
331
332     return file_to_handle(file);
333
334 err:
335     put_inode(inode);
336     put_inode(parent);
337     if (pathbuf)
338         free(pathbuf);
339     _close_file(file);
340 err_no_close:
341     return -1;
342 }
343
344 int open_file(const char *name, struct com32_filedata *filedata)
345 {
346     int rv;
347     struct file *file;
348     char mangled_name[FILENAME_MAX];
349
350     mangle_name(mangled_name, name);
351     rv = searchdir(mangled_name);
352
353     if (rv < 0)
354         return rv;
355
356     file = handle_to_file(rv);
357
358     if (file->inode->mode != DT_REG) {
359         _close_file(file);
360         return -1;
361     }
362
363     filedata->size      = file->inode->size;
364     filedata->blocklg2  = SECTOR_SHIFT(file->fs);
365     filedata->handle    = rv;
366
367     return rv;
368 }
369
370 void pm_open_file(com32sys_t *regs)
371 {
372     int rv;
373     struct file *file;
374     const char *name = MK_PTR(regs->es, regs->esi.w[0]);
375     char mangled_name[FILENAME_MAX];
376
377     mangle_name(mangled_name, name);
378     rv = searchdir(mangled_name);
379     if (rv < 0) {
380         regs->eflags.l |= EFLAGS_CF;
381     } else {
382         file = handle_to_file(rv);
383         regs->eflags.l &= ~EFLAGS_CF;
384         regs->eax.l = file->inode->size;
385         regs->ecx.w[0] = SECTOR_SIZE(file->fs);
386         regs->esi.w[0] = rv;
387     }
388 }
389
390 void close_file(uint16_t handle)
391 {
392     struct file *file;
393
394     if (handle) {
395         file = handle_to_file(handle);
396         _close_file(file);
397     }
398 }
399
400 void pm_close_file(com32sys_t *regs)
401 {
402     close_file(regs->esi.w[0]);
403 }
404
405 /*
406  * it will do:
407  *    initialize the memory management function;
408  *    set up the vfs fs structure;
409  *    initialize the device structure;
410  *    invoke the fs-specific init function;
411  *    initialize the cache if we need one;
412  *    finally, get the current inode for relative path looking.
413  */
414 __bss16 uint16_t SectorSize, SectorShift;
415
416 void fs_init(com32sys_t *regs)
417 {
418     static struct fs_info fs;   /* The actual filesystem buffer */
419     uint8_t disk_devno = regs->edx.b[0];
420     uint8_t disk_cdrom = regs->edx.b[1];
421     sector_t disk_offset = regs->ecx.l | ((sector_t)regs->ebx.l << 32);
422     uint16_t disk_heads = regs->esi.w[0];
423     uint16_t disk_sectors = regs->edi.w[0];
424     uint32_t maxtransfer = regs->ebp.l;
425     int blk_shift = -1;
426     struct device *dev = NULL;
427     /* ops is a ptr list for several fs_ops */
428     const struct fs_ops **ops = (const struct fs_ops **)regs->eax.l;
429
430     /* Initialize malloc() */
431     mem_init();
432
433     /* Default name for the root directory */
434     fs.cwd_name[0] = '/';
435
436     while ((blk_shift < 0) && *ops) {
437         /* set up the fs stucture */
438         fs.fs_ops = *ops;
439
440         /*
441          * This boldly assumes that we don't mix FS_NODEV filesystems
442          * with FS_DEV filesystems...
443          */
444         if (fs.fs_ops->fs_flags & FS_NODEV) {
445             fs.fs_dev = NULL;
446         } else {
447             if (!dev)
448                 dev = device_init(disk_devno, disk_cdrom, disk_offset,
449                                   disk_heads, disk_sectors, maxtransfer);
450             fs.fs_dev = dev;
451         }
452         /* invoke the fs-specific init code */
453         blk_shift = fs.fs_ops->fs_init(&fs);
454         ops++;
455     }
456     if (blk_shift < 0) {
457         printf("No valid file system found!\n");
458         while (1)
459                 ;
460     }
461     this_fs = &fs;
462
463     /* initialize the cache */
464     if (fs.fs_dev && fs.fs_dev->cache_data)
465         cache_init(fs.fs_dev, blk_shift);
466
467     /* start out in the root directory */
468     if (fs.fs_ops->iget_root) {
469         fs.root = fs.fs_ops->iget_root(&fs);
470         fs.cwd = get_inode(fs.root);
471     }
472
473     SectorShift = fs.sector_shift;
474     SectorSize  = fs.sector_size;
475 }