1018fc28bSDavid du Colombier /*
2018fc28bSDavid du Colombier * File system for tar archives (read-only)
3018fc28bSDavid du Colombier */
4018fc28bSDavid du Colombier
5bd389b36SDavid du Colombier #include <u.h>
6bd389b36SDavid du Colombier #include <libc.h>
7219b2ee8SDavid du Colombier #include <auth.h>
8bd389b36SDavid du Colombier #include <fcall.h>
9bd389b36SDavid du Colombier #include "tapefs.h"
10bd389b36SDavid du Colombier
11018fc28bSDavid du Colombier /* fundamental constants */
12018fc28bSDavid du Colombier enum {
13018fc28bSDavid du Colombier Tblock = 512,
14018fc28bSDavid du Colombier Namsiz = 100,
15018fc28bSDavid du Colombier Maxpfx = 155, /* from POSIX */
16018fc28bSDavid du Colombier Maxname = Namsiz + 1 + Maxpfx,
17018fc28bSDavid du Colombier Binsize = 0x80, /* flag in size[0], from gnu: positive binary size */
18018fc28bSDavid du Colombier Binnegsz = 0xff, /* flag in size[0]: negative binary size */
19018fc28bSDavid du Colombier };
20bd389b36SDavid du Colombier
21018fc28bSDavid du Colombier /* POSIX link flags */
22018fc28bSDavid du Colombier enum {
23018fc28bSDavid du Colombier LF_PLAIN1 = '\0',
24018fc28bSDavid du Colombier LF_PLAIN2 = '0',
25018fc28bSDavid du Colombier LF_LINK = '1',
26018fc28bSDavid du Colombier LF_SYMLINK1 = '2',
27018fc28bSDavid du Colombier LF_SYMLINK2 = 's', /* 4BSD used this */
28018fc28bSDavid du Colombier LF_CHR = '3',
29018fc28bSDavid du Colombier LF_BLK = '4',
30018fc28bSDavid du Colombier LF_DIR = '5',
31018fc28bSDavid du Colombier LF_FIFO = '6',
32018fc28bSDavid du Colombier LF_CONTIG = '7',
33018fc28bSDavid du Colombier /* 'A' - 'Z' are reserved for custom implementations */
34018fc28bSDavid du Colombier };
35bd389b36SDavid du Colombier
36018fc28bSDavid du Colombier typedef union {
37018fc28bSDavid du Colombier char dummy[Tblock];
389a747e4fSDavid du Colombier char tbuf[Maxbuf];
39018fc28bSDavid du Colombier struct Header {
40018fc28bSDavid du Colombier char name[Namsiz];
41bd389b36SDavid du Colombier char mode[8];
42bd389b36SDavid du Colombier char uid[8];
43bd389b36SDavid du Colombier char gid[8];
44bd389b36SDavid du Colombier char size[12];
45bd389b36SDavid du Colombier char mtime[12];
46bd389b36SDavid du Colombier char chksum[8];
47bd389b36SDavid du Colombier char linkflag;
48018fc28bSDavid du Colombier char linkname[Namsiz];
49bd389b36SDavid du Colombier
50018fc28bSDavid du Colombier /* rest are defined by POSIX's ustar format; see p1003.2b */
51018fc28bSDavid du Colombier char magic[6]; /* "ustar" */
52018fc28bSDavid du Colombier char version[2];
53018fc28bSDavid du Colombier char uname[32];
54018fc28bSDavid du Colombier char gname[32];
55018fc28bSDavid du Colombier char devmajor[8];
56018fc28bSDavid du Colombier char devminor[8];
57018fc28bSDavid du Colombier char prefix[Maxpfx]; /* if non-null, path= prefix "/" name */
58018fc28bSDavid du Colombier };
59018fc28bSDavid du Colombier } Hdr;
60018fc28bSDavid du Colombier
61018fc28bSDavid du Colombier Hdr dblock;
62bd389b36SDavid du Colombier int tapefile;
63018fc28bSDavid du Colombier
64bd389b36SDavid du Colombier int checksum(void);
65bd389b36SDavid du Colombier
66018fc28bSDavid du Colombier static int
isustar(Hdr * hp)67018fc28bSDavid du Colombier isustar(Hdr *hp)
68018fc28bSDavid du Colombier {
69018fc28bSDavid du Colombier return strcmp(hp->magic, "ustar") == 0;
70018fc28bSDavid du Colombier }
71018fc28bSDavid du Colombier
72018fc28bSDavid du Colombier /*
73018fc28bSDavid du Colombier * s is at most n bytes long, but need not be NUL-terminated.
74018fc28bSDavid du Colombier * if shorter than n bytes, all bytes after the first NUL must also
75018fc28bSDavid du Colombier * be NUL.
76018fc28bSDavid du Colombier */
77018fc28bSDavid du Colombier static int
strnlen(char * s,int n)78018fc28bSDavid du Colombier strnlen(char *s, int n)
79018fc28bSDavid du Colombier {
80018fc28bSDavid du Colombier return s[n - 1] != '\0'? n: strlen(s);
81018fc28bSDavid du Colombier }
82018fc28bSDavid du Colombier
83018fc28bSDavid du Colombier /* set fullname from header */
84018fc28bSDavid du Colombier static char *
tarname(Hdr * hp)85018fc28bSDavid du Colombier tarname(Hdr *hp)
86018fc28bSDavid du Colombier {
87018fc28bSDavid du Colombier int pfxlen, namlen;
88018fc28bSDavid du Colombier static char fullname[Maxname+1];
89018fc28bSDavid du Colombier
90018fc28bSDavid du Colombier namlen = strnlen(hp->name, sizeof hp->name);
91018fc28bSDavid du Colombier if (hp->prefix[0] == '\0' || !isustar(hp)) { /* old-style name? */
92018fc28bSDavid du Colombier memmove(fullname, hp->name, namlen);
93018fc28bSDavid du Colombier fullname[namlen] = '\0';
94018fc28bSDavid du Colombier return fullname;
95018fc28bSDavid du Colombier }
96018fc28bSDavid du Colombier
97018fc28bSDavid du Colombier /* posix name: name is in two pieces */
98018fc28bSDavid du Colombier pfxlen = strnlen(hp->prefix, sizeof hp->prefix);
99018fc28bSDavid du Colombier memmove(fullname, hp->prefix, pfxlen);
100018fc28bSDavid du Colombier fullname[pfxlen] = '/';
101018fc28bSDavid du Colombier memmove(fullname + pfxlen + 1, hp->name, namlen);
102018fc28bSDavid du Colombier fullname[pfxlen + 1 + namlen] = '\0';
103018fc28bSDavid du Colombier return fullname;
104018fc28bSDavid du Colombier }
105018fc28bSDavid du Colombier
106bd389b36SDavid du Colombier void
populate(char * name)107bd389b36SDavid du Colombier populate(char *name)
108bd389b36SDavid du Colombier {
109018fc28bSDavid du Colombier long chksum, linkflg;
110018fc28bSDavid du Colombier vlong blkno;
111018fc28bSDavid du Colombier char *fname;
112bd389b36SDavid du Colombier Fileinf f;
113018fc28bSDavid du Colombier Hdr *hp;
114bd389b36SDavid du Colombier
115bd389b36SDavid du Colombier tapefile = open(name, OREAD);
116bd389b36SDavid du Colombier if (tapefile < 0)
117bd389b36SDavid du Colombier error("Can't open argument file");
118bd389b36SDavid du Colombier replete = 1;
119018fc28bSDavid du Colombier hp = &dblock;
120018fc28bSDavid du Colombier for (blkno = 0; ; blkno++) {
121018fc28bSDavid du Colombier seek(tapefile, Tblock*blkno, 0);
122018fc28bSDavid du Colombier if (readn(tapefile, hp->dummy, sizeof hp->dummy) < sizeof hp->dummy)
123bd389b36SDavid du Colombier break;
124018fc28bSDavid du Colombier fname = tarname(hp);
125018fc28bSDavid du Colombier if (fname[0] == '\0')
126bd389b36SDavid du Colombier break;
127018fc28bSDavid du Colombier
128018fc28bSDavid du Colombier /* crack header */
12941fe996aSDavid du Colombier f.addr = blkno + 1;
130018fc28bSDavid du Colombier f.mode = strtoul(hp->mode, 0, 8);
131018fc28bSDavid du Colombier f.uid = strtoul(hp->uid, 0, 8);
132018fc28bSDavid du Colombier f.gid = strtoul(hp->gid, 0, 8);
133018fc28bSDavid du Colombier if((uchar)hp->size[0] == 0x80)
134018fc28bSDavid du Colombier f.size = b8byte(hp->size+3);
13541fe996aSDavid du Colombier else
136018fc28bSDavid du Colombier f.size = strtoull(hp->size, 0, 8);
137018fc28bSDavid du Colombier f.mdate = strtoul(hp->mtime, 0, 8);
138018fc28bSDavid du Colombier chksum = strtoul(hp->chksum, 0, 8);
1393ff48bf5SDavid du Colombier /* the mode test is ugly but sometimes necessary */
140018fc28bSDavid du Colombier if (hp->linkflag == LF_DIR || (f.mode&0170000) == 040000 ||
141018fc28bSDavid du Colombier strrchr(fname, '\0')[-1] == '/'){
1423ff48bf5SDavid du Colombier f.mode |= DMDIR;
143f3c44067SDavid du Colombier f.size = 0;
144f3c44067SDavid du Colombier }
145ed250ae1SDavid du Colombier f.mode &= DMDIR | 0777;
146018fc28bSDavid du Colombier
147*728c92e5SDavid du Colombier /* make file name safe, canonical and free of . and .. */
148018fc28bSDavid du Colombier while (fname[0] == '/') /* don't allow absolute paths */
149018fc28bSDavid du Colombier ++fname;
150018fc28bSDavid du Colombier cleanname(fname);
151*728c92e5SDavid du Colombier while (strncmp(fname, "../", 3) == 0)
152*728c92e5SDavid du Colombier fname += 3;
153018fc28bSDavid du Colombier
154018fc28bSDavid du Colombier /* reject links */
155018fc28bSDavid du Colombier linkflg = hp->linkflag == LF_SYMLINK1 ||
156018fc28bSDavid du Colombier hp->linkflag == LF_SYMLINK2 || hp->linkflag == LF_LINK;
157bd389b36SDavid du Colombier if (chksum != checksum()){
158018fc28bSDavid du Colombier fprint(2, "%s: bad checksum on %.28s at offset %lld\n",
159018fc28bSDavid du Colombier argv0, fname, Tblock*blkno);
160304cb09aSDavid du Colombier exits("checksum");
161bd389b36SDavid du Colombier }
162bd389b36SDavid du Colombier if (linkflg) {
163018fc28bSDavid du Colombier /*fprint(2, "link %s->%s skipped\n", fname, hp->linkname);*/
164bd389b36SDavid du Colombier f.size = 0;
165018fc28bSDavid du Colombier } else {
166018fc28bSDavid du Colombier /* accept this file */
167018fc28bSDavid du Colombier f.name = fname;
168219b2ee8SDavid du Colombier if (f.name[0] == '\0')
169018fc28bSDavid du Colombier fprint(2, "%s: null name skipped\n", argv0);
170219b2ee8SDavid du Colombier else
171bd389b36SDavid du Colombier poppath(f, 1);
172018fc28bSDavid du Colombier blkno += (f.size + Tblock - 1)/Tblock;
173018fc28bSDavid du Colombier }
174bd389b36SDavid du Colombier }
175bd389b36SDavid du Colombier }
176bd389b36SDavid du Colombier
177bd389b36SDavid du Colombier void
dotrunc(Ram * r)178bd389b36SDavid du Colombier dotrunc(Ram *r)
179bd389b36SDavid du Colombier {
180bd389b36SDavid du Colombier USED(r);
181bd389b36SDavid du Colombier }
182bd389b36SDavid du Colombier
183bd389b36SDavid du Colombier void
docreate(Ram * r)184bd389b36SDavid du Colombier docreate(Ram *r)
185bd389b36SDavid du Colombier {
186bd389b36SDavid du Colombier USED(r);
187bd389b36SDavid du Colombier }
188bd389b36SDavid du Colombier
189bd389b36SDavid du Colombier char *
doread(Ram * r,vlong off,long cnt)19041fe996aSDavid du Colombier doread(Ram *r, vlong off, long cnt)
191bd389b36SDavid du Colombier {
192018fc28bSDavid du Colombier int n;
193018fc28bSDavid du Colombier
194018fc28bSDavid du Colombier seek(tapefile, Tblock*r->addr + off, 0);
195018fc28bSDavid du Colombier if (cnt > sizeof dblock.tbuf)
196bd389b36SDavid du Colombier error("read too big");
197018fc28bSDavid du Colombier n = readn(tapefile, dblock.tbuf, cnt);
198018fc28bSDavid du Colombier if (n != cnt)
199018fc28bSDavid du Colombier memset(dblock.tbuf + n, 0, cnt - n);
200bd389b36SDavid du Colombier return dblock.tbuf;
201bd389b36SDavid du Colombier }
202bd389b36SDavid du Colombier
203bd389b36SDavid du Colombier void
popdir(Ram * r)204bd389b36SDavid du Colombier popdir(Ram *r)
205bd389b36SDavid du Colombier {
206bd389b36SDavid du Colombier USED(r);
207bd389b36SDavid du Colombier }
208bd389b36SDavid du Colombier
209bd389b36SDavid du Colombier void
dowrite(Ram * r,char * buf,long off,long cnt)210bd389b36SDavid du Colombier dowrite(Ram *r, char *buf, long off, long cnt)
211bd389b36SDavid du Colombier {
212bd389b36SDavid du Colombier USED(r); USED(buf); USED(off); USED(cnt);
213bd389b36SDavid du Colombier }
214bd389b36SDavid du Colombier
215bd389b36SDavid du Colombier int
dopermw(Ram * r)216bd389b36SDavid du Colombier dopermw(Ram *r)
217bd389b36SDavid du Colombier {
218bd389b36SDavid du Colombier USED(r);
219bd389b36SDavid du Colombier return 0;
220bd389b36SDavid du Colombier }
221bd389b36SDavid du Colombier
222bd389b36SDavid du Colombier int
checksum(void)223018fc28bSDavid du Colombier checksum(void)
224bd389b36SDavid du Colombier {
225018fc28bSDavid du Colombier int i, n;
226018fc28bSDavid du Colombier uchar *cp;
227bd389b36SDavid du Colombier
228018fc28bSDavid du Colombier memset(dblock.chksum, ' ', sizeof dblock.chksum);
229018fc28bSDavid du Colombier cp = (uchar *)dblock.dummy;
230bd389b36SDavid du Colombier i = 0;
231018fc28bSDavid du Colombier for (n = Tblock; n-- > 0; )
232018fc28bSDavid du Colombier i += *cp++;
233018fc28bSDavid du Colombier return i;
234bd389b36SDavid du Colombier }
235