PageRenderTime 53ms CodeModel.GetById 14ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 1ms

/filesystems/unixfs/ancientfs/ancientfs_cpio_odc.c

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