Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / openhackware / src / libfs / core.c
1 /*
2  * <fs.c>
3  *
4  * Open Hack'Ware BIOS file systems management
5  * 
6  * Copyright (c) 2004-2005 Jocelyn Mayer
7  * 
8  *   This program is free software; you can redistribute it and/or
9  *   modify it under the terms of the GNU General Public License V2
10  *   as published by the Free Software Foundation
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *
17  *   You should have received a copy of the GNU General Public License
18  *   along with this program; if not, write to the Free Software
19  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include "bios.h"
25 #include "libfs.h"
26 #undef FS_DPRINTF
27 #define FS_DPRINTF(fmt, args...) do { } while (0)
28
29 static int special_file_get_type (const unsigned char *name)
30 {
31     int ret;
32
33     if (strcmp(name, "root") == 0)
34         ret = FILE_ROOT;
35     else if (strcmp(name, "boot") == 0)
36         ret = FILE_BOOT;
37     else if (strcmp(name, "bootdir") == 0)
38         ret = FILE_BOOTDIR;
39     else
40         ret = FILE_UNKNOWN;
41
42     return ret;
43 }
44
45 void fs_cache_add_inode (inode_t *parent, inode_t *inode)
46 {
47     inode_t **cur;
48
49     if (parent == NULL || inode == NULL)
50         return;
51     FS_DPRINTF("Add inode '%s' to '%s' cache\n", inode->name, parent->name);
52     for (cur = &parent->child; *cur != NULL; cur = &((*cur)->next)) {
53         if (strcmp((*cur)->name, inode->name) == 0) {
54             return;
55         }
56     }
57     *cur = inode;
58 }
59
60 static inode_t *fs_cache_get_inode (inode_t *parent,
61                                     const unsigned char *name)
62 {
63     inode_t *cur, *rec;
64     int dec;
65
66     FS_DPRINTF("Look for '%s' into '%s' cache\n", name, parent->name);
67     if (name == NULL || parent == NULL)
68         return NULL;
69     if (name[0] == '/' && name[1] == '\0')
70         return parent->fs->root;
71     if (is_special_file(name))
72         dec = strlen(FS_SPECIAL) + 2;
73     else
74         dec = 0;
75     for (cur = parent->child; cur != NULL; cur = cur->next) {
76         if (strcmp(cur->name + dec, name + dec) == 0) {
77             cur->refcount++;
78             for (rec = parent; rec != NULL; rec = rec->parent)
79                 rec->refcount++;
80             break;
81         }
82     }
83     cur = NULL;
84
85     return cur;
86 }
87
88 static void fs_cache_put_inode (inode_t *inode)
89 {
90     void (*put_inode)(inode_t *inode);
91     inode_t *cur, **upd;
92
93     if (inode != NULL && --inode->refcount == 0) {
94         if (inode->parent == NULL)
95             return;
96         fs_cache_put_inode(inode->parent);
97         upd = &inode->parent->child;
98         for (cur = *upd; cur != NULL; cur = cur->next) {
99             if (cur == inode) {
100                 (*upd) = cur->next;
101                 put_inode = inode->fs->fs_ops->put_inode;
102                 (*put_inode)(cur);
103                 FS_DPRINTF("Free inode '%s' from '%s' cache\n",
104                            inode->name, inode->parent->name);
105                 free(cur);
106                 return;
107             }
108             upd = &cur;
109         }
110         FS_ERROR("didn't find inode in list !\n");
111     }
112 }
113
114 static inode_t *fs_get_inode (inode_t *parent, const unsigned char *name)
115 {
116     inode_t *(*get_inode)(inode_t *parent, const unsigned char *name);
117     inode_t *cur;
118
119     if (parent == NULL) {
120         FS_ERROR("Invalide inode '%s' (NULL)\n", name);
121         return NULL;
122     } else {
123         if (fs_inode_get_type(parent) != INODE_TYPE_DIR) {
124             FS_ERROR("Try to recurse in a non-directory inode (%d)\n",
125                      parent->flags);
126             return NULL;
127         }
128     }
129     if (is_special_file(name)) {
130         int type;
131         /* Special files */
132         FS_DPRINTF("look for special file '%s'\n",
133                    name + strlen(FS_SPECIAL) + 2);
134         type = special_file_get_type(name + strlen(FS_SPECIAL) + 2);
135         if (type == FILE_UNKNOWN) {
136             FS_ERROR("Unknown special file '%s'\n",
137                      name + strlen(FS_SPECIAL) + 2);
138             return NULL;
139         }
140         cur = (*parent->fs->fs_ops->get_special_inode)(parent->fs, type);
141         FS_DPRINTF("boot file: %p '%s' %p boot dir: %p '%s' %p\n",
142                    parent->fs->bootfile, parent->fs->bootfile->name,
143                    &parent->fs->bootfile,
144                    parent->fs->bootdir, parent->fs->bootdir->name,
145                    &parent->fs->bootdir);
146         switch (type) {
147         case FILE_ROOT:
148             parent->fs->root = cur;
149             cur->parent = NULL;
150             cur->fs = parent->fs;
151             cur->name = strdup("");
152             return cur;
153         case FILE_BOOT:
154             parent->fs->bootfile = cur;
155             break;
156         case FILE_BOOTDIR:
157             parent->fs->bootdir = cur;
158             break;
159         }
160 #if 0
161         parent = cur->parent;
162 #else
163         cur->fs = parent->fs;
164         return cur;
165 #endif
166     } else {
167         FS_DPRINTF("look for file '%s' in %p '%s'\n", name, parent,
168                    parent->name);
169         DPRINTF("look for file '%s' in %p '%s'\n", name, parent,
170                    parent->name);
171         cur = fs_cache_get_inode(parent, name);
172         if (cur != NULL) {
173             FS_DPRINTF("found inode '%s' %p in cache\n", name, cur);
174             DPRINTF("found inode '%s' %p in cache\n", name, cur);
175             return cur;
176         }
177         get_inode = parent->fs->fs_ops->get_inode;
178         cur = (*get_inode)(parent, name);
179         cur->name = strdup(name);
180     }
181     if (cur != NULL) {
182         cur->parent = parent;
183         cur->fs = parent->fs;
184         fs_cache_add_inode(parent, cur);
185         FS_DPRINTF("Inode '%s' in '%s': %d blocs size %d %d\n",
186                    name, parent->name, cur->nb_blocs, cur->size.bloc,
187                    cur->size.offset);
188         DPRINTF("Inode '%s' in '%s': %d blocs size %d %d\n",
189                 name, parent->name, cur->nb_blocs, cur->size.bloc,
190                 cur->size.offset);
191     } else {
192         FS_ERROR("Inode '%s' not found in '%s'\n", name, parent->name);
193     }
194
195     return cur;
196 }
197
198 static inline void fs_put_inode (inode_t *inode)
199 {
200     fs_cache_put_inode(inode);
201 }
202
203 static inode_t *_fs_walk (inode_t *parent, const unsigned char *name)
204 {
205     unsigned char tmpname[MAXNAME_LEN], *sl;
206     inode_t *new, *subdir;
207     
208     FS_DPRINTF("'%s' %p\n", name, parent);
209     DPRINTF("'%s' %p\n", name, parent);
210     for (; *name == '/'; name++)
211         continue;
212     DPRINTF("'%s' %p\n", name, parent);
213     strcpy(tmpname, name);
214     sl = strchr(tmpname, '/');
215     if (sl != NULL) {
216         *sl = '\0';
217         subdir = fs_get_inode(parent, tmpname);
218         if (subdir == NULL)
219             return NULL;
220         new = _fs_walk(subdir, sl + 1);
221     } else {
222         new = fs_get_inode(parent, tmpname);
223     }
224
225     return new;
226 }
227
228 static inode_t *fs_walk (inode_t *parent, const unsigned char *name)
229 {
230     unsigned char tmpname[MAXNAME_LEN];
231     int len;
232     
233     FS_DPRINTF("'%s' %p\n", name, parent);
234     DPRINTF("'%s' %p %p\n", name, parent, parent->fs->root);
235     len = strlen(name);
236     memcpy(tmpname, name, len + 1);
237     if (tmpname[len - 1] == '/')
238         tmpname[--len] = '\0';
239     if (parent == parent->fs->root && tmpname[0] == '\0')
240         return parent->fs->root;
241
242     return _fs_walk(parent, tmpname);
243 }
244
245 static unsigned char *fs_inode_get_path (inode_t *inode)
246 {
247     unsigned char tmpname[MAXNAME_LEN], *pname;
248     int len;
249     inode_t *parent;
250
251     parent = inode->parent;
252     if (parent == NULL || (inode->name[0] == '/' && inode->name[1] == '\0')) {
253         FS_DPRINTF("Reached root node '/'\n");
254         return strdup("/");
255     }
256     FS_DPRINTF("Recurse to root '%s'...\n", inode->name);
257     pname = fs_inode_get_path(parent);
258     FS_DPRINTF("'%s' '%s'\n", pname, inode->name);
259     len = strlen(pname);
260     memcpy(tmpname, pname, len);
261     if (tmpname[len - 1] != '/')
262         tmpname[len++] = '/';
263     strcpy(tmpname + len, inode->name);
264     free(pname);
265     FS_DPRINTF(" => '%s'\n", tmpname);
266
267     return strdup(tmpname);
268 }
269
270 static inline uint32_t fs_map_bloc (inode_t *inode, uint32_t bloc)
271 {
272     FS_DPRINTF("%s: inode %p bloc %d %p %p %p\n", __func__, inode, bloc,
273                inode->fs, inode->fs->fs_ops, inode->fs->fs_ops->map_bloc);
274     return (*inode->fs->fs_ops->map_bloc)(inode, bloc);
275 }
276
277 fs_t *fs_probe (part_t *part, int set_raw)
278 {
279     fs_t *new;
280     inode_t fake_inode;
281     fs_ops_t *fs_ops = NULL;
282     unsigned char *name = NULL;
283     void *private = NULL;
284     uint32_t size = 0;
285     int type = FS_TYPE_UNKNOWN;
286
287     FS_DPRINTF("\n");
288     if (set_raw == 2) {
289         DPRINTF("Check raw only\n");
290         goto raw_only;
291     }
292     DPRINTF("Probe ext2\n");
293     type = fs_ext2_probe(part, &size, &fs_ops, &name, &private);
294     if (type == FS_TYPE_UNKNOWN) {
295         DPRINTF("Probe isofs\n");
296         type = fs_isofs_probe(part, &size, &fs_ops, &name, &private);
297         if (type == FS_TYPE_UNKNOWN) {
298             DPRINTF("Probe HFS\n");
299             type = fs_hfs_probe(part, &size, &fs_ops, &name, &private);
300             if (set_raw) {
301                 DPRINTF("Probe raw\n");
302             raw_only:
303                 type = fs_raw_probe(part, &size, &fs_ops, &name, &private);
304             }
305             if (type == FS_TYPE_UNKNOWN) {
306                 FS_ERROR("FS not identified\n");
307                 return NULL;
308             }
309         }
310     }
311     if (fs_ops == NULL || size == 0) {
312         FS_ERROR("Missing param: %p %d\n", fs_ops, size);
313         return NULL;
314     }
315     new = malloc(sizeof(fs_t));
316     if (new == NULL)
317         return NULL;
318     new->type = type;
319     new->part = part;
320     new->size = size;
321     new->fs_ops = fs_ops;
322     new->name = name;
323     new->private = private;
324     /* Get root inode */
325     memset(&fake_inode, 0, sizeof(inode_t));
326     fake_inode.name = "fake_root";
327     fake_inode.fs = new;
328     fake_inode.refcount = 1;
329     fs_get_inode(&fake_inode, "\0" FS_SPECIAL "\0root");
330     if (new->root == NULL) {
331         FS_ERROR("Didn't find root inode\n");
332         free(new);
333         return NULL;
334     }
335     FS_DPRINTF("fs: %p root: %p root fs: %p\n", new, new->root, new->root->fs);
336     FS_DPRINTF("OK\n");
337
338     return new;
339 }
340
341 dir_t *fs_opendir (fs_t *fs, const unsigned char *name)
342 {
343     inode_t *inode;
344     dir_t *new;
345
346     FS_DPRINTF("'%s'\n", name);
347     inode = fs_walk(fs->root, name);
348     if (inode == NULL)
349         return NULL;
350     new = malloc(sizeof(dir_t));
351     new->inode = inode;
352
353     return new;
354 }
355
356 dirent_t *fs_readdir (dir_t *dir)
357 {
358     void (*put_inode)(inode_t *inode);
359     inode_t *inode;
360
361     inode = fs_get_inode(dir->inode, NULL);
362     if (inode == NULL)
363         return NULL;
364     if (dir->cur == NULL) {
365         dir->cur = malloc(sizeof(dirent_t));
366         dir->cur->dir = dir;
367     } else {
368         put_inode = dir->inode->fs->fs_ops->put_inode;
369         (*put_inode)(dir->cur->inode);
370     }
371     dir->cur->inode = inode;
372     dir->cur->dname = inode->name;
373
374     return dir->cur;
375 }
376
377 unsigned char *fs_get_path (dirent_t *dirent)
378 {
379     return fs_inode_get_path(dirent->inode);
380 }
381
382 void fs_closedir (dir_t *dir)
383 {
384     void (*put_inode)(inode_t *inode);
385
386     if (dir->cur != NULL) {
387         put_inode = dir->inode->fs->fs_ops->put_inode;
388         (*put_inode)(dir->cur->inode);
389         free(dir->cur);
390     }
391     free(dir);
392 }
393
394 inode_t *fs_open (fs_t *fs, const unsigned char *name)
395 {
396     inode_t *inode;
397
398     FS_DPRINTF("'%s'\n", name);
399     inode = fs_walk(fs->root, name);
400     if (inode != NULL)
401         fs_seek(inode, 0, 0);
402
403     return inode;
404 }
405
406 int fs_seek (inode_t *inode, uint32_t bloc, uint32_t pos)
407 {
408     if (inode == NULL || inode->fs == NULL) {
409         ERROR("%s: no inode / fs ! %p %p\n", __func__, inode,
410               inode == NULL ? NULL : inode->fs);
411         return -1;
412     }
413     FS_DPRINTF("%08x %08x\n", bloc, pos);
414     if (part_seek(inode->fs->part, fs_map_bloc(inode, bloc), pos) == -1)
415         return -1;
416     inode->vbloc = bloc;
417     inode->vpos = pos;
418
419     return 0;
420 }
421
422 int fs_read (inode_t *inode, void *buffer, int len)
423 {
424     uint32_t bsize, total;
425     int done, tmp;
426     
427     bsize = part_blocsize(inode->fs->part);
428     total = 0;
429     if (fs_seek(inode, inode->vbloc, inode->vpos) < 0)
430         return -1;
431     for (; len != 0; len -= done) {
432         tmp = bsize - inode->vpos;
433         if (len < tmp)
434             tmp = len;
435         done = part_read(inode->fs->part, buffer, tmp);
436         if (done < 0)
437             return -1;
438         inode->vpos += done;
439         if (inode->vpos >= bsize) {
440             inode->vbloc++;
441             inode->vpos -= bsize;
442         }
443         buffer += done;
444         total += done;
445     }
446
447     return total;
448 }
449
450 int fs_write (inode_t *inode, const void *buffer, unused int len)
451 {
452     uint32_t bsize, total;
453     int done, tmp;
454     
455     bsize = part_blocsize(inode->fs->part);
456     total = 0;
457     for (; len != 0; len -= done) {
458         tmp = bsize - inode->vpos;
459         if (len < tmp)
460             tmp = len;
461         done = part_write(inode->fs->part, buffer, tmp);
462         if (done < 0)
463             return -1;
464         inode->vpos += done;
465         if (inode->vpos >= bsize) {
466             inode->vbloc++;
467             inode->vpos -= bsize;
468             if (fs_seek(inode, inode->vbloc, inode->vpos) < 0)
469                 return -1;
470         }
471         buffer += done;
472         total += done;
473     }
474
475     return total;
476 }
477
478 void fs_close (inode_t *inode)
479 {
480     fs_put_inode(inode);
481 }
482
483 uint32_t fs_inode_get_type (inode_t *inode)
484 {
485     return inode->flags & INODE_TYPE_MASK;
486 }
487
488 uint32_t fs_inode_get_flags (inode_t *inode)
489 {
490     return inode->flags & INODE_FLAG_MASK;
491 }
492
493 uint32_t fs_inode_get_size (inode_t *inode)
494 {
495     DPRINTF("%s: (%d * %d) + %d\n", __func__, inode->size.bloc,
496             part_blocsize(inode->fs->part), inode->size.offset);
497     return (inode->size.bloc * part_blocsize(inode->fs->part)) +
498         inode->size.offset;
499 }
500
501 part_t *fs_part (fs_t *fs)
502 {
503     return fs->part;
504 }
505
506 uint32_t fs_get_type (fs_t *fs)
507 {
508     return fs->type;
509 }
510
511 part_t *fs_inode_get_part (inode_t *inode)
512 {
513     return inode->fs->part;
514 }
515
516 inode_t *fs_get_bootdir (fs_t *fs)
517 {
518     FS_DPRINTF("fs: %p root: %p root fs: %p\n", fs, fs->root, fs->root->fs);
519     if (fs->bootdir == NULL) {
520         fs->bootdir = fs_get_inode(fs->root, "\0" FS_SPECIAL "\0bootdir");
521     }
522     FS_DPRINTF("fs: %p root: %p root fs: %p\n", fs, fs->root, fs->root->fs);
523     FS_DPRINTF("boot file: %p '%s' %p boot dir: %p '%s' %p\n",
524                fs->bootfile, fs->bootfile->name, &fs->bootfile,
525                fs->bootdir, fs->bootdir->name, &fs->bootdir);
526
527     return fs->bootdir;
528 }
529 unsigned char *fs_get_boot_dirname (fs_t *fs)
530 {
531     if (fs->bootdir == NULL) {
532         fs_get_bootdir(fs);
533         if (fs->bootdir == NULL)
534             return NULL;
535     }
536     FS_DPRINTF("boot file: %p '%s' boot dir: %p '%s'\n",
537                fs->bootfile, fs->bootfile->name,
538                fs->bootdir, fs->bootdir->name);
539
540     return fs_inode_get_path(fs->bootdir);
541 }
542
543 inode_t *fs_get_bootfile (fs_t *fs)
544 {
545     FS_DPRINTF("fs: %p root: %p root fs: %p\n", fs, fs->root, fs->root->fs);
546     FS_DPRINTF("boot file: %p '%s' %p boot dir: %p '%s' %p\n",
547                fs->bootfile, fs->bootfile->name, &fs->bootfile,
548                fs->bootdir, fs->bootdir->name, &fs->bootdir);
549     if (fs->bootfile == NULL) {
550         if (fs->bootdir == NULL)
551             fs_get_bootdir(fs);
552         if (fs->bootdir == NULL)
553             return NULL;
554         fs->bootfile = fs_get_inode(fs->bootdir, "\0" FS_SPECIAL "\0boot");
555     }
556     FS_DPRINTF("fs: %p root: %p root fs: %p\n", fs, fs->root, fs->root->fs);
557     FS_DPRINTF("boot file: %p '%s' %p boot dir: %p '%s' %p\n",
558                fs->bootfile, fs->bootfile->name, &fs->bootfile,
559                fs->bootdir, fs->bootdir->name, &fs->bootdir);
560
561     return fs->bootfile;
562 }