Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * File system for tar archives (read-only)
 */

#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include "tapefs.h"

/* fundamental constants */
enum {
        Tblock = 512,
        Namsiz = 100,
        Maxpfx = 155,           /* from POSIX */
        Maxname = Namsiz + 1 + Maxpfx,
        Binsize = 0x80,         /* flag in size[0], from gnu: positive binary size */
        Binnegsz = 0xff,        /* flag in size[0]: negative binary size */
};

/* POSIX link flags */
enum {
        LF_PLAIN1 =     '\0',
        LF_PLAIN2 =     '0',
        LF_LINK =       '1',
        LF_SYMLINK1 =   '2',
        LF_SYMLINK2 =   's',            /* 4BSD used this */
        LF_CHR =        '3',
        LF_BLK =        '4',
        LF_DIR =        '5',
        LF_FIFO =       '6',
        LF_CONTIG =     '7',
        /* 'A' - 'Z' are reserved for custom implementations */
};

typedef union {
        char    dummy[Tblock];
        char    tbuf[Maxbuf];
        struct Header {
                char    name[Namsiz];
                char    mode[8];
                char    uid[8];
                char    gid[8];
                char    size[12];
                char    mtime[12];
                char    chksum[8];
                char    linkflag;
                char    linkname[Namsiz];

                /* rest are defined by POSIX's ustar format; see p1003.2b */
                char    magic[6];       /* "ustar" */
                char    version[2];
                char    uname[32];
                char    gname[32];
                char    devmajor[8];
                char    devminor[8];
                char    prefix[Maxpfx]; /* if non-null, path= prefix "/" name */
        };
} Hdr;

Hdr dblock;
int tapefile;

int     checksum(void);

static int
isustar(Hdr *hp)
{
        return strcmp(hp->magic, "ustar") == 0;
}

/*
 * s is at most n bytes long, but need not be NUL-terminated.
 * if shorter than n bytes, all bytes after the first NUL must also
 * be NUL.
 */
static int
strnlen(char *s, int n)
{
        return s[n - 1] != '\0'? n: strlen(s);
}

/* set fullname from header */
static char *
tarname(Hdr *hp)
{
        int pfxlen, namlen;
        static char fullname[Maxname+1];

        namlen = strnlen(hp->name, sizeof hp->name);
        if (hp->prefix[0] == '\0' || !isustar(hp)) {    /* old-style name? */
                memmove(fullname, hp->name, namlen);
                fullname[namlen] = '\0';
                return fullname;
        }

        /* posix name: name is in two pieces */
        pfxlen = strnlen(hp->prefix, sizeof hp->prefix);
        memmove(fullname, hp->prefix, pfxlen);
        fullname[pfxlen] = '/';
        memmove(fullname + pfxlen + 1, hp->name, namlen);
        fullname[pfxlen + 1 + namlen] = '\0';
        return fullname;
}

void
populate(char *name)
{
        long chksum, linkflg;
        vlong blkno;
        char *fname;
        Fileinf f;
        Hdr *hp;

        tapefile = open(name, OREAD);
        if (tapefile < 0)
                error("Can't open argument file");
        replete = 1;
        hp = &dblock;
        for (blkno = 0; ; blkno++) {
                seek(tapefile, Tblock*blkno, 0);
                if (readn(tapefile, hp->dummy, sizeof hp->dummy) < sizeof hp->dummy)
                        break;
                fname = tarname(hp);
                if (fname[0] == '\0')
                        break;

                /* crack header */
                f.addr = blkno + 1;
                f.mode = strtoul(hp->mode, 0, 8);
                f.uid  = strtoul(hp->uid, 0, 8);
                f.gid  = strtoul(hp->gid, 0, 8);
                if((uchar)hp->size[0] == 0x80)
                        f.size = b8byte(hp->size+3);
                else
                        f.size = strtoull(hp->size, 0, 8);
                f.mdate = strtoul(hp->mtime, 0, 8);
                chksum  = strtoul(hp->chksum, 0, 8);
                /* the mode test is ugly but sometimes necessary */
                if (hp->linkflag == LF_DIR || (f.mode&0170000) == 040000 ||
                    strrchr(fname, '\0')[-1] == '/'){
                        f.mode |= DMDIR;
                        f.size = 0;
                }
                f.mode &= DMDIR | 0777;

                /* make file name safe, canonical and free of . and .. */
                while (fname[0] == '/')         /* don't allow absolute paths */
                        ++fname;
                cleanname(fname);
                while (strncmp(fname, "../", 3) == 0)
                        fname += 3;

                /* reject links */
                linkflg = hp->linkflag == LF_SYMLINK1 ||
                        hp->linkflag == LF_SYMLINK2 || hp->linkflag == LF_LINK;
                if (chksum != checksum()){
                        fprint(2, "%s: bad checksum on %.28s at offset %lld\n",
                                argv0, fname, Tblock*blkno);
                        exits("checksum");
                }
                if (linkflg) {
                        /*fprint(2, "link %s->%s skipped\n", fname, hp->linkname);*/
                        f.size = 0;
                } else {
                        /* accept this file */
                        f.name = fname;
                        if (f.name[0] == '\0')
                                fprint(2, "%s: null name skipped\n", argv0);
                        else
                                poppath(f, 1);
                        blkno += (f.size + Tblock - 1)/Tblock;
                }
        }
}

void
dotrunc(Ram *r)
{
        USED(r);
}

void
docreate(Ram *r)
{
        USED(r);
}

char *
doread(Ram *r, vlong off, long cnt)
{
        int n;

        seek(tapefile, Tblock*r->addr + off, 0);
        if (cnt > sizeof dblock.tbuf)
                error("read too big");
        n = readn(tapefile, dblock.tbuf, cnt);
        if (n != cnt)
                memset(dblock.tbuf + n, 0, cnt - n);
        return dblock.tbuf;
}

void
popdir(Ram *r)
{
        USED(r);
}

void
dowrite(Ram *r, char *buf, long off, long cnt)
{
        USED(r); USED(buf); USED(off); USED(cnt);
}

int
dopermw(Ram *r)
{
        USED(r);
        return 0;
}

int
checksum(void)
{
        int i, n;
        uchar *cp;

        memset(dblock.chksum, ' ', sizeof dblock.chksum);
        cp = (uchar *)dblock.dummy;
        i = 0;
        for (n = Tblock; n-- > 0; )
                i += *cp++;
        return i;
}