PageRenderTime 59ms CodeModel.GetById 9ms app.highlight 43ms RepoModel.GetById 2ms app.codeStats 0ms

/filesystems/unixfs/ancientfs/ancientfs_tar.c

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