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