1 /*
2 * File system for tar archives (read-only)
3 */
4
5 #include <u.h>
6 #include <libc.h>
7 #include <auth.h>
8 #include <fcall.h>
9 #include "tapefs.h"
10
11 /* fundamental constants */
12 enum {
13 Tblock = 512,
14 Namsiz = 100,
15 Maxpfx = 155, /* from POSIX */
16 Maxname = Namsiz + 1 + Maxpfx,
17 Binsize = 0x80, /* flag in size[0], from gnu: positive binary size */
18 Binnegsz = 0xff, /* flag in size[0]: negative binary size */
19 };
20
21 /* POSIX link flags */
22 enum {
23 LF_PLAIN1 = '\0',
24 LF_PLAIN2 = '0',
25 LF_LINK = '1',
26 LF_SYMLINK1 = '2',
27 LF_SYMLINK2 = 's', /* 4BSD used this */
28 LF_CHR = '3',
29 LF_BLK = '4',
30 LF_DIR = '5',
31 LF_FIFO = '6',
32 LF_CONTIG = '7',
33 /* 'A' - 'Z' are reserved for custom implementations */
34 };
35
36 typedef union {
37 char dummy[Tblock];
38 char tbuf[Maxbuf];
39 struct Header {
40 char name[Namsiz];
41 char mode[8];
42 char uid[8];
43 char gid[8];
44 char size[12];
45 char mtime[12];
46 char chksum[8];
47 char linkflag;
48 char linkname[Namsiz];
49
50 /* rest are defined by POSIX's ustar format; see p1003.2b */
51 char magic[6]; /* "ustar" */
52 char version[2];
53 char uname[32];
54 char gname[32];
55 char devmajor[8];
56 char devminor[8];
57 char prefix[Maxpfx]; /* if non-null, path= prefix "/" name */
58 };
59 } Hdr;
60
61 Hdr dblock;
62 int tapefile;
63
64 int checksum(void);
65
66 static int
isustar(Hdr * hp)67 isustar(Hdr *hp)
68 {
69 return strcmp(hp->magic, "ustar") == 0;
70 }
71
72 /*
73 * s is at most n bytes long, but need not be NUL-terminated.
74 * if shorter than n bytes, all bytes after the first NUL must also
75 * be NUL.
76 */
77 static int
strnlen(char * s,int n)78 strnlen(char *s, int n)
79 {
80 return s[n - 1] != '\0'? n: strlen(s);
81 }
82
83 /* set fullname from header */
84 static char *
tarname(Hdr * hp)85 tarname(Hdr *hp)
86 {
87 int pfxlen, namlen;
88 static char fullname[Maxname+1];
89
90 namlen = strnlen(hp->name, sizeof hp->name);
91 if (hp->prefix[0] == '\0' || !isustar(hp)) { /* old-style name? */
92 memmove(fullname, hp->name, namlen);
93 fullname[namlen] = '\0';
94 return fullname;
95 }
96
97 /* posix name: name is in two pieces */
98 pfxlen = strnlen(hp->prefix, sizeof hp->prefix);
99 memmove(fullname, hp->prefix, pfxlen);
100 fullname[pfxlen] = '/';
101 memmove(fullname + pfxlen + 1, hp->name, namlen);
102 fullname[pfxlen + 1 + namlen] = '\0';
103 return fullname;
104 }
105
106 void
populate(char * name)107 populate(char *name)
108 {
109 long chksum, linkflg;
110 vlong blkno;
111 char *fname;
112 Fileinf f;
113 Hdr *hp;
114
115 tapefile = open(name, OREAD);
116 if (tapefile < 0)
117 error("Can't open argument file");
118 replete = 1;
119 hp = &dblock;
120 for (blkno = 0; ; blkno++) {
121 seek(tapefile, Tblock*blkno, 0);
122 if (readn(tapefile, hp->dummy, sizeof hp->dummy) < sizeof hp->dummy)
123 break;
124 fname = tarname(hp);
125 if (fname[0] == '\0')
126 break;
127
128 /* crack header */
129 f.addr = blkno + 1;
130 f.mode = strtoul(hp->mode, 0, 8);
131 f.uid = strtoul(hp->uid, 0, 8);
132 f.gid = strtoul(hp->gid, 0, 8);
133 if((uchar)hp->size[0] == 0x80)
134 f.size = b8byte(hp->size+3);
135 else
136 f.size = strtoull(hp->size, 0, 8);
137 f.mdate = strtoul(hp->mtime, 0, 8);
138 chksum = strtoul(hp->chksum, 0, 8);
139 /* the mode test is ugly but sometimes necessary */
140 if (hp->linkflag == LF_DIR || (f.mode&0170000) == 040000 ||
141 strrchr(fname, '\0')[-1] == '/'){
142 f.mode |= DMDIR;
143 f.size = 0;
144 }
145 f.mode &= DMDIR | 0777;
146
147 /* make file name safe, canonical and free of . and .. */
148 while (fname[0] == '/') /* don't allow absolute paths */
149 ++fname;
150 cleanname(fname);
151 while (strncmp(fname, "../", 3) == 0)
152 fname += 3;
153
154 /* reject links */
155 linkflg = hp->linkflag == LF_SYMLINK1 ||
156 hp->linkflag == LF_SYMLINK2 || hp->linkflag == LF_LINK;
157 if (chksum != checksum()){
158 fprint(2, "%s: bad checksum on %.28s at offset %lld\n",
159 argv0, fname, Tblock*blkno);
160 exits("checksum");
161 }
162 if (linkflg) {
163 /*fprint(2, "link %s->%s skipped\n", fname, hp->linkname);*/
164 f.size = 0;
165 } else {
166 /* accept this file */
167 f.name = fname;
168 if (f.name[0] == '\0')
169 fprint(2, "%s: null name skipped\n", argv0);
170 else
171 poppath(f, 1);
172 blkno += (f.size + Tblock - 1)/Tblock;
173 }
174 }
175 }
176
177 void
dotrunc(Ram * r)178 dotrunc(Ram *r)
179 {
180 USED(r);
181 }
182
183 void
docreate(Ram * r)184 docreate(Ram *r)
185 {
186 USED(r);
187 }
188
189 char *
doread(Ram * r,vlong off,long cnt)190 doread(Ram *r, vlong off, long cnt)
191 {
192 int n;
193
194 seek(tapefile, Tblock*r->addr + off, 0);
195 if (cnt > sizeof dblock.tbuf)
196 error("read too big");
197 n = readn(tapefile, dblock.tbuf, cnt);
198 if (n != cnt)
199 memset(dblock.tbuf + n, 0, cnt - n);
200 return dblock.tbuf;
201 }
202
203 void
popdir(Ram * r)204 popdir(Ram *r)
205 {
206 USED(r);
207 }
208
209 void
dowrite(Ram * r,char * buf,long off,long cnt)210 dowrite(Ram *r, char *buf, long off, long cnt)
211 {
212 USED(r); USED(buf); USED(off); USED(cnt);
213 }
214
215 int
dopermw(Ram * r)216 dopermw(Ram *r)
217 {
218 USED(r);
219 return 0;
220 }
221
222 int
checksum(void)223 checksum(void)
224 {
225 int i, n;
226 uchar *cp;
227
228 memset(dblock.chksum, ' ', sizeof dblock.chksum);
229 cp = (uchar *)dblock.dummy;
230 i = 0;
231 for (n = Tblock; n-- > 0; )
232 i += *cp++;
233 return i;
234 }
235