PageRenderTime 62ms CodeModel.GetById 22ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

/filesystems/unixfs/ancientfs/ancientfs_bcpio.c

http://macfuse.googlecode.com/
C | 668 lines | 524 code | 126 blank | 18 comment | 117 complexity | 0044d1f50c208e08f0569b4599c34e91 MD5 | raw file
  1/*
  2 * Ancient UNIX File Systems for MacFUSE
  3 * Amit Singh
  4 * http://osxbook.com
  5 */
  6
  7#include "ancientfs_bcpio.h"
  8#include "unixfs_common.h"
  9
 10#include <errno.h>
 11#include <fcntl.h>
 12#include <stdio.h>
 13#include <stdlib.h>
 14#include <string.h>
 15#include <unistd.h>
 16#include <sys/ioctl.h>
 17#include <sys/stat.h>
 18
 19DECL_UNIXFS("UNIX bcpio", bcpio);
 20
 21struct bcpio_entry {
 22    char   name[UNIXFS_MAXPATHLEN + 1];
 23    char   linktargetname[UNIXFS_MAXPATHLEN + 1];
 24    off_t  daddr;
 25    struct stat stat;
 26};
 27
 28static int ancientfs_bcpio_readheader(int fd, struct bcpio_entry* ce);
 29
 30static int
 31ancientfs_bcpio_readheader(int fd, struct bcpio_entry* ce)
 32{
 33    int nr;
 34    struct bcpio_header _hdr, *hdr = &_hdr;
 35
 36    nr = read(fd, hdr, sizeof(struct bcpio_header));
 37    if (nr != sizeof(struct bcpio_header)) {
 38        if (!nr)
 39            return 1;
 40        if (nr < 0)
 41            return -1;
 42    }
 43
 44    if (fs16_to_host(unixfs->s_endian, hdr->h_magic) != BCPIO_MAGIC) {
 45        fprintf(stderr, "*** fatal error: bad magic in record @ %llu\n",
 46                lseek(fd, (off_t)0, SEEK_CUR));
 47        return -1;
 48    }
 49
 50    memset(ce, 0, sizeof(*ce));
 51
 52    /* nothing to do with h_dev */
 53    ce->stat.st_ino = fs16_to_host(unixfs->s_endian, hdr->h_ino);
 54    ce->stat.st_mode =
 55        ancientfs_bcpio_mode(fs16_to_host(unixfs->s_endian, hdr->h_mode),
 56                            unixfs->s_flags);
 57    ce->stat.st_uid = fs16_to_host(unixfs->s_endian, hdr->h_uid);
 58    ce->stat.st_gid = fs16_to_host(unixfs->s_endian, hdr->h_gid);
 59    ce->stat.st_nlink = fs16_to_host(unixfs->s_endian, hdr->h_nlink);
 60    uint16_t rdev = fs16_to_host(unixfs->s_endian, hdr->h_majmin);
 61    ce->stat.st_rdev = makedev((rdev >> 8) & 255, rdev & 255);
 62
 63    int16_t tmsb16 = fs16_to_host(unixfs->s_endian, hdr->h_mtime_msb16);
 64    int16_t tlsb16 = fs16_to_host(unixfs->s_endian, hdr->h_mtime_lsb16);
 65    ce->stat.st_atime = ce->stat.st_ctime = ce->stat.st_mtime =
 66        (int32_t)(tmsb16 << 16 | (tlsb16 & 0xFFFF));
 67
 68    uint16_t szmsb16 = fs16_to_host(unixfs->s_endian, hdr->h_filesize_msb16);
 69    uint16_t szlsb16 = fs16_to_host(unixfs->s_endian, hdr->h_filesize_lsb16);
 70    ce->stat.st_size = (uint32_t)(szmsb16 << 16 | (szlsb16 & 0xFFFF));
 71
 72    uint16_t namesize = fs16_to_host(unixfs->s_endian, hdr->h_namesize);
 73    if (namesize < 2) {
 74        fprintf(stderr, "*** fatal error: file name too small\n");
 75        return -1;
 76    }
 77
 78    if (namesize > UNIXFS_MAXPATHLEN) {
 79        fprintf(stderr, "*** fatal error: file name too large (%#hx) @ %llu\n",
 80                namesize, lseek(fd, (off_t)0, SEEK_CUR));
 81        return -1;
 82    }
 83
 84    if (read(fd, &ce->name, namesize) != namesize)
 85        return -1;
 86
 87    if (ce->name[0] == '\0' || ce->name[namesize - 1] != '\0') { /* corrupt */
 88        fprintf(stderr, "*** fatal error: file name corrupt @ %llu\n",
 89                lseek(fd, (off_t)0, SEEK_CUR));
 90        return -1;
 91    }
 92
 93    /* header + namesize aligned to 2-byte boundary */
 94
 95    ce->daddr = lseek(fd, (off_t)0, SEEK_CUR);
 96    if (ce->daddr < 0) {
 97        fprintf(stderr, "*** fatal error: cannot read archive\n");
 98        return -1;
 99    }
100    if (ce->daddr & (off_t)1) {
101        ce->daddr++;
102        (void)lseek(fd, (off_t)1, SEEK_CUR);
103    }
104
105    /* ce->daddr now contains the start of data */
106
107    if (!ce->stat.st_size && (namesize == BCPIO_TRAILER_LEN) &&
108        !memcmp(ce->name, BCPIO_TRAILER, BCPIO_TRAILER_LEN))
109        return 1;
110
111    if (!S_ISLNK(ce->stat.st_mode) || !ce->stat.st_size) {
112        off_t dataend = ce->stat.st_size;
113        dataend += (dataend & 1) ? 1 : 0;
114        (void)lseek(fd, dataend, SEEK_CUR); 
115        return 0;
116    }
117
118    /* link */
119
120    if (ce->stat.st_size < 2) {
121        fprintf(stderr, "*** fatal error: link target name too small\n");
122        return -1;
123    }
124
125    if (ce->stat.st_size > UNIXFS_MAXPATHLEN) {
126        fprintf(stderr, "*** fatal error: link target name too large\n");
127        return -1;
128    }
129
130    if (read(fd, ce->linktargetname, ce->stat.st_size) != ce->stat.st_size)
131        return -1;
132
133    if (ce->linktargetname[0] == '\0') {
134        fprintf(stderr, "*** fatal error: link target name corrupt\n");
135        return -1;
136    }
137
138    ce->linktargetname[ce->stat.st_size] = '\0';
139
140    if ((ce->daddr + ce->stat.st_size) & 1)
141        (void)lseek(fd, (off_t)1, SEEK_CUR);
142
143    return 0;
144}
145
146static void*
147unixfs_internal_init(const char* dmg, uint32_t flags, fs_endian_t fse,
148                     char** fsname, char** volname)
149{
150    int fd = -1;
151    if ((fd = open(dmg, O_RDONLY)) < 0) {
152        perror("open");
153        return NULL;
154    }
155
156    int err;
157    fs_endian_t mye, e = UNIXFS_FS_INVALID;
158    struct stat stbuf;
159    struct super_block* sb = (struct super_block*)0;
160    struct filsys* fs = (struct filsys*)0;
161
162    if ((err = fstat(fd, &stbuf)) != 0) {
163        perror("fstat");
164        goto out;
165    }
166
167    if (!S_ISREG(stbuf.st_mode) && !(flags & UNIXFS_FORCE)) {
168        err = EINVAL;
169        fprintf(stderr, "%s is not a bcpio image file\n", dmg);
170        goto out;
171    }
172
173    struct bcpio_header hdr;
174
175    if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
176        fprintf(stderr, "failed to read data from file\n");
177        err = EIO;
178        goto out;
179    }
180
181#ifdef __LITTLE_ENDIAN__
182    mye = UNIXFS_FS_LITTLE;
183    e = UNIXFS_FS_BIG;
184#else
185#ifdef __BIG_ENDIAN__
186    mye = UNIXFS_FS_BIG;
187    e = UNIXFS_FS_LITTLE;
188#else
189#error Endian Problem
190#endif
191#endif
192
193    uint16_t magic = hdr.h_magic;
194    if (magic == BCPIO_MAGIC) {
195        e = mye;
196    } else if (fs16_to_host(e, magic) == BCPIO_MAGIC) {
197        /* needs swap */
198    } else {
199        e = UNIXFS_FS_INVALID;
200    }
201
202    if (e == UNIXFS_FS_INVALID) {
203        fprintf(stderr, "not recognized as a bcpio archive\n");
204        err = EINVAL;
205        goto out;
206    }
207
208    sb = malloc(sizeof(struct super_block));
209    if (!sb) {
210        err = ENOMEM;
211        goto out;
212    }
213
214    assert(sizeof(struct filsys) <= BCBLOCK);
215
216    fs = calloc(1, BCBLOCK);
217    if (!fs) {
218        free(sb);
219        err = ENOMEM;
220        goto out;
221    }
222
223    unixfs = sb;
224
225    unixfs->s_flags = flags;
226    unixfs->s_endian = e;
227    if (e != mye)
228        fs->s_needsswap = 1;
229    unixfs->s_fs_info = (void*)fs;
230    unixfs->s_bdev = fd;
231
232    /* must initialize the inode layer before sanity checking */
233    if ((err = unixfs_inodelayer_init(sizeof(struct bcpio_node_info))) != 0)
234        goto out;
235
236    struct inode* rootip = unixfs_inodelayer_iget((ino_t)ROOTINO);
237    if (!rootip) {
238        fprintf(stderr, "*** fatal error: no root inode\n");
239        abort();
240    }
241
242    rootip->I_mode = S_IFDIR | 0755;
243    rootip->I_uid  = getuid();
244    rootip->I_gid  = getgid();
245    rootip->I_size = 2;
246    rootip->I_atime_sec = rootip->I_mtime_sec = rootip->I_ctime_sec =        time(0);
247
248    struct bcpio_node_info* rootci = (struct bcpio_node_info*)rootip->I_private;
249    rootci->ci_self = rootip;
250    rootci->ci_parent = NULL;
251    rootci->ci_children = NULL;
252    rootci->ci_next_sibling = NULL;
253
254    unixfs_inodelayer_isucceeded(rootip);
255
256    fs->s_fsize = stbuf.st_size / BCBLOCK;
257    fs->s_files = 0;
258    fs->s_directories = 1 + 1 + 1;
259    fs->s_rootip = rootip;
260    fs->s_lastino = ROOTINO;
261
262    lseek(fd, (off_t)0, SEEK_SET); /* rewind archive */
263
264    struct bcpio_entry _ce, *ce = &_ce;
265
266    for (;;) {
267        if ((err = ancientfs_bcpio_readheader(fd, ce)) != 0) {
268            if (err == 1)
269                break;
270            else {
271                fprintf(stderr,
272                        "*** fatal error: cannot read block (error %d)\n", err);
273                err = EIO;
274                goto out;
275            }
276        }
277
278        char* path = ce->name;
279        ino_t parent_ino = ROOTINO;
280        size_t pathlen = strlen(ce->name);
281
282        if ((*path == '.') && ((pathlen == 1) ||
283            ((pathlen == 2) && (*(path + 1) == '/')))) {
284            /* root */
285            rootip->I_mode = ce->stat.st_mode;
286            rootip->I_atime_sec = \
287                rootip->I_mtime_sec = \
288                    rootip->I_ctime_sec = ce->stat.st_mtime;
289            continue;
290        }
291                
292        /* we don't deal with many fancy paths here: just '/' and './' */
293        if (*path == '/')
294            path++;
295        else if (*path == '.' && *(path + 1) == '/')
296            path += 2;
297
298        char *cnp, *term;
299
300        for (cnp = strtok_r(path, "/", &term); cnp;
301            cnp = strtok_r(NULL, "/", &term)) {
302            /* we have { parent_ino, cnp } */
303            struct stat stbuf;
304            int missing = unixfs_internal_namei(parent_ino, cnp, &stbuf);
305            if (!missing) {
306                parent_ino = stbuf.st_ino;
307                if (!term) { /* out of order */
308                    struct inode* dirp = unixfs_inodelayer_iget(parent_ino);
309                    if (!dirp || !dirp->I_initialized) {
310                        fprintf(stderr,
311                                "*** fatal error: inode %llu inconsistent\n",
312                                (ino64_t)parent_ino);
313                        abort();
314                    }
315                    dirp->I_mode = ce->stat.st_mode;
316                    dirp->I_uid = ce->stat.st_uid;
317                    dirp->I_gid = ce->stat.st_gid;
318                    unixfs_inodelayer_iput(dirp);
319                }
320                continue;
321            }
322            struct inode* ip =
323                unixfs_inodelayer_iget((ino_t)(fs->s_lastino + 1));
324            if (!ip) {
325                fprintf(stderr, "*** fatal error: no inode for %llu\n",
326                        (ino64_t)(fs->s_lastino + 1));
327                abort();
328            }
329
330            ip->I_mode  = ce->stat.st_mode;
331            ip->I_uid   = ce->stat.st_uid;
332            ip->I_gid   = ce->stat.st_gid;
333            ip->I_size  = ce->stat.st_size;
334            ip->I_nlink = ce->stat.st_nlink;
335            ip->I_rdev  = ce->stat.st_rdev;
336
337            ip->I_atime_sec = ip->I_mtime_sec = ip->I_ctime_sec =
338                ce->stat.st_mtime;
339
340            struct bcpio_node_info* ci = (struct bcpio_node_info*)ip->I_private;
341
342            size_t namelen = strlen(cnp);
343            ci->ci_name = malloc(namelen + 1);
344            if (!ci->ci_name) {
345                fprintf(stderr, "*** fatal error: cannot allocate memory\n");
346                abort();
347            }
348            memcpy(ci->ci_name, cnp, namelen);
349            ci->ci_name[namelen] = '\0';
350
351            ip->I_daddr[0] = 0;
352
353            if (S_ISLNK(ip->I_mode)) {
354                namelen = strlen(ce->linktargetname);
355                ci->ci_linktargetname = malloc(namelen + 1);
356                if (!ci->ci_name) {
357                    fprintf(stderr,
358                            "*** fatal error: cannot allocate memory\n");
359                    abort();
360                }
361                memcpy(ci->ci_linktargetname, ce->linktargetname, namelen);
362                ci->ci_linktargetname[namelen] = '\0';
363            } else if (S_ISREG(ip->I_mode)) {
364
365                ip->I_daddr[0] = ce->daddr;
366            }
367             
368            ci->ci_self = ip;
369            ci->ci_children = NULL;
370            struct inode* parent_ip = unixfs_internal_iget(parent_ino);
371            parent_ip->I_size += 1;
372            ci->ci_parent = (struct bcpio_node_info*)(parent_ip->I_private);
373            ci->ci_next_sibling = ci->ci_parent->ci_children;
374            ci->ci_parent->ci_children = ci;
375
376            if (term && !S_ISDIR(ip->I_mode)) /* out of order */
377                ip->I_mode = S_IFDIR | 0755;
378
379            if (S_ISDIR(ip->I_mode)) {
380                fs->s_directories++;
381                parent_ino = fs->s_lastino + 1;
382                ip->I_size = 2;
383            } else
384                fs->s_files++;
385
386            fs->s_lastino++;
387
388            //if (ip->I_ino > fs->s_lastino)
389                //fs->s_lastino = ip->I_ino;
390
391            unixfs_internal_iput(parent_ip);
392            unixfs_inodelayer_isucceeded(ip);
393            /* no put */
394
395        } /* for each component */
396
397    } /* for each block */
398
399    err = 0;
400
401    unixfs->s_statvfs.f_bsize = BCBLOCK;
402    unixfs->s_statvfs.f_frsize = BCBLOCK;
403    unixfs->s_statvfs.f_ffree = 0;
404    unixfs->s_statvfs.f_files = fs->s_files + fs->s_directories;
405    unixfs->s_statvfs.f_blocks = fs->s_fsize;
406    unixfs->s_statvfs.f_bfree = 0;
407    unixfs->s_statvfs.f_bavail = 0;
408    unixfs->s_dentsize = 1;
409    unixfs->s_statvfs.f_namemax = UNIXFS_MAXNAMLEN;
410
411    snprintf(unixfs->s_fsname, UNIXFS_MNAMELEN, "Old (binary) bcpio");
412
413    char* dmg_basename = basename((char*)dmg);
414    snprintf(unixfs->s_volname, UNIXFS_MAXNAMLEN, "%s (archive=%s)",
415             unixfs_fstype, (dmg_basename) ? dmg_basename : "bcpio Image");
416
417    *fsname = unixfs->s_fsname;
418    *volname = unixfs->s_volname;
419
420out:
421    if (err) {
422        if (fd >= 0)
423            close(fd);
424        if (fs)
425            free(fs);
426        if (sb)
427            free(sb);
428        return NULL;
429    }
430
431    return sb;
432}
433
434static void
435unixfs_internal_fini(void* filsys)
436{
437    struct super_block* sb = (struct super_block*)filsys;
438    struct filsys* fs = (struct filsys*)sb->s_fs_info;
439    ino_t i = fs->s_lastino;
440    for (; i >= ROOTINO; i--) {
441        struct inode* tmp = unixfs_internal_iget(i);
442        if (tmp) {
443            struct bcpio_node_info* ci = (struct bcpio_node_info*)tmp->I_private;
444            if (ci) {
445                free(ci->ci_name);
446                if (ci->ci_linktargetname)
447                    free(ci->ci_linktargetname);
448            }
449            unixfs_internal_iput(tmp);
450            unixfs_internal_iput(tmp);
451        }
452    }
453
454    unixfs_inodelayer_fini();
455
456    if (sb) {
457        if (sb->s_bdev >= 0)
458            close(sb->s_bdev);
459        sb->s_bdev = -1;
460        if (sb->s_fs_info)
461            free(sb->s_fs_info);
462    }
463}
464
465static off_t
466unixfs_internal_alloc(void)
467{
468    return (off_t)0;
469}
470
471static off_t
472unixfs_internal_bmap(struct inode* ip, off_t lblkno, int* error)
473{
474    return (off_t)0;
475}
476
477static int
478unixfs_internal_bread(off_t blkno, char* blkbuf)
479{
480    return EIO;
481}
482
483static struct inode*
484unixfs_internal_iget(ino_t ino)
485{
486    struct inode* ip = unixfs_inodelayer_iget(ino);
487    if (!ip) {
488        fprintf(stderr, "*** fatal error: no inode for %llu\n", (ino64_t)ino);
489        abort();
490    }
491
492    if (ip->I_initialized)
493        return ip;
494
495    unixfs_inodelayer_ifailed(ip);
496
497    return NULL; 
498}
499
500static void
501unixfs_internal_iput(struct inode* ip)
502{
503    unixfs_inodelayer_iput(ip);
504}
505
506static int
507unixfs_internal_igetattr(ino_t ino, struct stat* stbuf)
508{
509    struct inode* ip = unixfs_internal_iget(ino);
510    if (!ip)
511        return ENOENT;
512
513    unixfs_internal_istat(ip, stbuf);
514
515    unixfs_internal_iput(ip);
516
517    return 0;
518}
519
520static void
521unixfs_internal_istat(struct inode* ip, struct stat* stbuf)
522{
523    memcpy(stbuf, &ip->I_stat, sizeof(struct stat));
524}
525
526static int
527unixfs_internal_namei(ino_t parentino, const char* name, struct stat* stbuf)
528{
529    int ret = ENOENT;
530    stbuf->st_ino = 0;
531
532    size_t namelen = strlen(name);
533    if (namelen > UNIXFS_MAXNAMLEN)
534        return ENAMETOOLONG;
535
536    struct inode* dp = unixfs_internal_iget(parentino);
537    if (!dp)
538        return ENOENT;
539
540    if (!S_ISDIR(dp->I_mode)) {
541        ret = ENOTDIR;
542        goto out;
543    }
544
545    struct bcpio_node_info* child =
546        ((struct bcpio_node_info*)dp->I_private)->ci_children;;
547
548    if (!child) {
549        ret = ENOENT;
550        goto out;
551    }
552
553    int found = 0;
554
555    do {
556        size_t target_namelen = strlen((const char*)child->ci_name);
557        if ((namelen == target_namelen) &&
558            (memcmp(name, child->ci_name, target_namelen) == 0)) {
559            found = 1;
560            break;
561        }
562        child = child->ci_next_sibling;
563    } while (child);
564
565    if (found)
566        ret = unixfs_internal_igetattr((ino_t)child->ci_self->I_ino, stbuf);
567
568out:
569    unixfs_internal_iput(dp);
570
571    return ret;
572}
573
574static int
575unixfs_internal_nextdirentry(struct inode* dp, struct unixfs_dirbuf* dirbuf,
576                             off_t* offset, struct unixfs_direntry* dent)
577{
578    if (*offset >= dp->I_size)
579        return -1;
580
581    if (*offset < 2) {
582        int idx = 0;
583        dent->name[idx++] = '.';
584        dent->ino = ROOTINO;
585        if (*offset == 1) {
586            if (dp->I_ino != ROOTINO) {
587                struct inode* pdp = unixfs_internal_iget(dp->I_ino);
588                if (pdp) {
589                    dent->ino = pdp->I_ino;
590                    unixfs_internal_iput(pdp);
591                }
592            }
593            dent->name[idx++] = '.';
594        }
595        dent->name[idx++] = '\0';
596        goto out;
597    }
598
599    struct bcpio_node_info* child =
600        ((struct bcpio_node_info*)dp->I_private)->ci_children;;
601
602    off_t i;
603
604    for (i = 0; i < (*offset - 2); i++)
605        child = child->ci_next_sibling;
606
607    dent->ino = (ino_t)child->ci_self->I_ino;
608    size_t dirnamelen = strlen(child->ci_name);
609    dirnamelen = min(dirnamelen, UNIXFS_MAXNAMLEN);
610    memcpy(dent->name, child->ci_name, dirnamelen);
611    dent->name[dirnamelen] = '\0';
612
613out:
614    *offset += 1;
615
616    return 0;
617}
618
619static ssize_t
620unixfs_internal_pbread(struct inode* ip, char* buf, size_t nbyte, off_t offset,
621                       int* error)
622{
623    off_t start = (off_t)ip->I_daddr[0];
624
625    /* caller already checked for bounds */
626
627    return pread(unixfs->s_bdev, buf, nbyte, start + offset);
628}
629
630static int
631unixfs_internal_readlink(ino_t ino, char path[UNIXFS_MAXPATHLEN])
632{
633    struct inode* ip = unixfs_internal_iget(ino);
634    if (!ip)
635        return ENOENT;
636
637    int error;
638
639    struct bcpio_node_info* ci = (struct bcpio_node_info*)ip->I_private;
640    if (!ci->ci_linktargetname) {
641        error = ENOENT;
642        goto out;
643    } 
644
645    size_t linklen = min(ip->I_size, BCBLOCK);
646    memcpy(path, ci->ci_linktargetname, linklen);
647    path[linklen] = '\0';
648
649    error = 0;
650
651out:
652    unixfs_internal_iput(ip);
653
654    return error;
655}
656
657static int
658unixfs_internal_sanitycheck(void* filsys, off_t disksize)
659{
660    return 0;
661}
662
663static int
664unixfs_internal_statvfs(struct statvfs* svb)
665{
666    memcpy(svb, &unixfs->s_statvfs, sizeof(struct statvfs));
667    return 0;
668}