865f398d687b7083ac476e89449a5dc6aac2e320
[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  * Get an empty file structure
34  */
35 static struct file *alloc_file(void)
36 {
37     int i;
38     struct file *file = files;
39
40     for (i = 0; i < MAX_OPEN; i++) {
41         if (!file->fs)
42             return file;
43         file++;
44     }
45
46     return NULL;
47 }
48
49 /*
50  * Close and free a file structure
51  */
52 static inline void free_file(struct file *file)
53 {
54     memset(file, 0, sizeof *file);
55 }
56
57 void _close_file(struct file *file)
58 {
59     if (file->fs)
60         file->fs->fs_ops->close_file(file);
61     free_file(file);
62 }
63
64 /*
65  * Convert between a 16-bit file handle and a file structure
66  */
67
68 void load_config(void)
69 {
70     int err;
71
72     err = this_fs->fs_ops->load_config();
73
74     if (err)
75         printf("ERROR: No configuration file found\n");
76 }
77
78 void pm_mangle_name(com32sys_t *regs)
79 {
80     const char *src = MK_PTR(regs->ds, regs->esi.w[0]);
81     char       *dst = MK_PTR(regs->es, regs->edi.w[0]);
82
83     mangle_name(dst, src);
84 }
85
86 void mangle_name(char *dst, const char *src)
87 {
88     this_fs->fs_ops->mangle_name(dst, src);
89 }
90
91 void getfssec(com32sys_t *regs)
92 {
93     int sectors;
94     bool have_more;
95     uint32_t bytes_read;
96     char *buf;
97     struct file *file;
98     uint16_t handle;
99
100     sectors = regs->ecx.w[0];
101
102     handle = regs->esi.w[0];
103     file = handle_to_file(handle);
104
105     buf = MK_PTR(regs->es, regs->ebx.w[0]);
106     bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more);
107
108     /*
109      * If we reach EOF, the filesystem driver will have already closed
110      * the underlying file... this really should be cleaner.
111      */
112     if (!have_more) {
113         _close_file(file);
114         regs->esi.w[0] = 0;
115     }
116
117     regs->ecx.l = bytes_read;
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 void pm_searchdir(com32sys_t *regs)
142 {
143     char *name = MK_PTR(regs->ds, regs->edi.w[0]);
144     int rv;
145
146     rv = searchdir(name);
147     if (rv < 0) {
148         regs->esi.w[0]  = 0;
149         regs->eax.l     = 0;
150         regs->eflags.l |= EFLAGS_ZF;
151     } else {
152         regs->esi.w[0]  = rv;
153         regs->eax.l     = handle_to_file(rv)->file_len;
154         regs->eflags.l &= ~EFLAGS_ZF;
155     }
156 }
157
158 int searchdir(const char *name)
159 {
160     struct inode *inode = NULL;
161     struct inode *parent = NULL;
162     struct file *file;
163     char *pathbuf = NULL;
164     char *part, *p, echar;
165     int symlink_count = MAX_SYMLINK_CNT;
166
167     if (!(file = alloc_file()))
168         goto err_no_close;
169     file->fs = this_fs;
170
171     /* if we have ->searchdir method, call it */
172     if (file->fs->fs_ops->searchdir) {
173         file->fs->fs_ops->searchdir(name, file);
174
175         if (file->open_file)
176             return file_to_handle(file);
177         else
178             goto err;
179     }
180
181     /* else, try the generic-path-lookup method */
182
183     parent = get_inode(this_fs->cwd);
184     p = pathbuf = strdup(name);
185     if (!pathbuf)
186         goto err;
187
188     do {
189     got_link:
190         if (*p == '/') {
191             put_inode(parent);
192             parent = get_inode(this_fs->root);
193         }
194
195         do {
196             inode = get_inode(parent);
197
198             while (*p == '/')
199                 p++;
200
201             if (!*p)
202                 break;
203
204             part = p;
205             while ((echar = *p) && echar != '/')
206                 p++;
207             *p++ = '\0';
208
209             if (part[0] != '.' || part[1] != '\0') {
210                 inode = this_fs->fs_ops->iget(part, parent);
211                 if (!inode)
212                     goto err;
213                 if (inode->mode == I_SYMLINK) {
214                     char *linkbuf, *q;
215                     int name_len = echar ? strlen(p) : 0;
216                     int total_len = inode->size + name_len + 2;
217                     int link_len;
218
219                     if (!this_fs->fs_ops->readlink ||
220                         --symlink_count == 0       ||      /* limit check */
221                         total_len > MAX_SYMLINK_BUF)
222                         goto err;
223
224                     linkbuf = malloc(total_len);
225                     if (!linkbuf)
226                         goto err;
227
228                     link_len = this_fs->fs_ops->readlink(inode, linkbuf);
229                     if (link_len <= 0) {
230                         free(linkbuf);
231                         goto err;
232                     }
233
234                     q = linkbuf + link_len;
235
236                     if (echar) {
237                         if (link_len > 0 && q[-1] != '/')
238                             *q++ = '/';
239
240                         memcpy(q, p, name_len+1);
241                     } else {
242                         *q = '\0';
243                     }
244
245                     free(pathbuf);
246                     p = pathbuf = linkbuf;
247                     put_inode(inode);
248                     inode = NULL;
249                     goto got_link;
250                 }
251
252                 put_inode(parent);
253                 parent = NULL;
254
255                 if (!echar)
256                     break;
257
258                 if (inode->mode != I_DIR)
259                     goto err;
260
261                 parent = inode;
262                 inode = NULL;
263             }
264         } while (echar);
265     } while (0);
266
267     free(pathbuf);
268     pathbuf = NULL;
269     put_inode(parent);
270     parent = NULL;
271
272     if (!inode)
273         goto err;
274
275     file->inode  = inode;
276     file->offset = 0;
277     file->file_len  = inode->size;
278
279     dprintf("File %s -> %p (inode %p) len %u\n", name, file,
280             inode, inode->size);
281
282     return file_to_handle(file);
283
284 err:
285     if (inode)
286         put_inode(inode);
287     if (parent)
288         put_inode(parent);
289     if (pathbuf)
290         free(pathbuf);
291     _close_file(file);
292 err_no_close:
293     return -1;
294 }
295
296 void close_file(com32sys_t *regs)
297 {
298     uint16_t handle = regs->esi.w[0];
299     struct file *file;
300
301     if (handle) {
302         file = handle_to_file(handle);
303         _close_file(file);
304     }
305 }
306
307 /*
308  * it will do:
309  *    initialize the memory management function;
310  *    set up the vfs fs structure;
311  *    initialize the device structure;
312  *    invoke the fs-specific init function;
313  *    initialize the cache if we need one;
314  *    finally, get the current inode for relative path looking.
315  *
316  */
317 void fs_init(com32sys_t *regs)
318 {
319     static struct fs_info fs;   /* The actual filesystem buffer */
320     uint8_t disk_devno = regs->edx.b[0];
321     uint8_t disk_cdrom = regs->edx.b[1];
322     sector_t disk_offset = regs->ecx.l | ((sector_t)regs->ebx.l << 32);
323     uint16_t disk_heads = regs->esi.w[0];
324     uint16_t disk_sectors = regs->edi.w[0];
325     int blk_shift = -1;
326     struct device *dev = NULL;
327     /* ops is a ptr list for several fs_ops */
328     const struct fs_ops **ops = (const struct fs_ops **)regs->eax.l;
329
330     /* Initialize malloc() */
331     mem_init();
332
333     /* Default name for the root directory */
334     fs.cwd_name[0] = '/';
335
336     while ((blk_shift < 0) && *ops) {
337         /* set up the fs stucture */
338         fs.fs_ops = *ops;
339
340         /*
341          * This boldly assumes that we don't mix FS_NODEV filesystems
342          * with FS_DEV filesystems...
343          */
344         if (fs.fs_ops->fs_flags & FS_NODEV) {
345             fs.fs_dev = NULL;
346         } else {
347             if (!dev)
348                 dev = device_init(disk_devno, disk_cdrom, disk_offset,
349                                   disk_heads, disk_sectors);
350             fs.fs_dev = dev;
351         }
352         /* invoke the fs-specific init code */
353         blk_shift = fs.fs_ops->fs_init(&fs);
354         ops++;
355     }
356     if (blk_shift < 0) {
357         printf("No valid file system found!\n");
358         while (1)
359                 ;
360     }
361     this_fs = &fs;
362
363     /* initialize the cache */
364     if (fs.fs_dev && fs.fs_dev->cache_data)
365         cache_init(fs.fs_dev, blk_shift);
366
367     /* start out in the root directory */
368     if (fs.fs_ops->iget_root) {
369         fs.root = fs.fs_ops->iget_root(&fs);
370         fs.cwd = get_inode(fs.root);
371     }
372 }