xref: /plan9/sys/src/cmd/tapefs/tarfs.c (revision 728c92e567f21a49f3c62100b4a1865b6c377f7d)
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