xref: /plan9/sys/src/cmd/tar.c (revision 6a9fc400c33447ef5e1cda7185cb4de2c8e8010e)
1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <bio.h>
6 
7 #define TBLOCK	512
8 #define NBLOCK	40	/* maximum blocksize */
9 #define DBLOCK	20	/* default blocksize */
10 #define NAMSIZ	100
11 union	hblock
12 {
13 	char	dummy[TBLOCK];
14 	struct	header
15 	{
16 		char	name[NAMSIZ];
17 		char	mode[8];
18 		char	uid[8];
19 		char	gid[8];
20 		char	size[12];
21 		char	mtime[12];
22 		char	chksum[8];
23 		char	linkflag;
24 		char	linkname[NAMSIZ];
25 	} dbuf;
26 } dblock, tbuf[NBLOCK];
27 
28 Dir *stbuf;
29 Biobuf bout;
30 
31 int	rflag, xflag, vflag, tflag, mt, cflag, fflag, Tflag, Rflag;
32 int	uflag, gflag;
33 int	chksum, recno, first;
34 int	nblock = DBLOCK;
35 
36 void	usage(void);
37 void	dorep(char **);
38 int	endtar(void);
39 void	getdir(void);
40 void	passtar(void);
41 void	putfile(char*, char *, char *);
42 void	doxtract(char **);
43 void	dotable(void);
44 void	putempty(void);
45 void	longt(Dir *);
46 int	checkdir(char *, int, Qid*);
47 void	tomodes(Dir *);
48 int	checksum(void);
49 int	checkupdate(char *);
50 int	prefix(char *, char *);
51 int	readtar(char *);
52 int	writetar(char *);
53 void	backtar(void);
54 void	flushtar(void);
55 void	affix(int, char *);
56 int	volprompt(void);
57 void
58 main(int argc, char **argv)
59 {
60 	char *usefile;
61 	char *cp, *ap;
62 
63 	if (argc < 2)
64 		usage();
65 
66 	Binit(&bout, 1, OWRITE);
67 	usefile =  0;
68 	argv[argc] = 0;
69 	argv++;
70 	for (cp = *argv++; *cp; cp++)
71 		switch(*cp) {
72 		case 'f':
73 			usefile = *argv++;
74 			if(!usefile)
75 				usage();
76 			fflag++;
77 			break;
78 		case 'u':
79 			ap = *argv++;
80 			if(!ap)
81 				usage();
82 			uflag = strtoul(ap, 0, 0);
83 			break;
84 		case 'g':
85 			ap = *argv++;
86 			if(!ap)
87 				usage();
88 			gflag = strtoul(ap, 0, 0);
89 			break;
90 		case 'c':
91 			cflag++;
92 			rflag++;
93 			break;
94 		case 'r':
95 			rflag++;
96 			break;
97 		case 'v':
98 			vflag++;
99 			break;
100 		case 'x':
101 			xflag++;
102 			break;
103 		case 'T':
104 			Tflag++;
105 			break;
106 		case 't':
107 			tflag++;
108 			break;
109 		case 'R':
110 			Rflag++;
111 			break;
112 		case '-':
113 			break;
114 		default:
115 			fprint(2, "tar: %c: unknown option\n", *cp);
116 			usage();
117 		}
118 
119 	fmtinstall('M', dirmodefmt);
120 
121 	if (rflag) {
122 		if (!usefile) {
123 			if (cflag == 0) {
124 				fprint(2, "tar: can only create standard output archives\n");
125 				exits("arg error");
126 			}
127 			mt = dup(1, -1);
128 			nblock = 1;
129 		}
130 		else if ((mt = open(usefile, ORDWR)) < 0) {
131 			if (cflag == 0 || (mt = create(usefile, OWRITE, 0666)) < 0) {
132 				fprint(2, "tar: cannot open %s: %r\n", usefile);
133 				exits("open");
134 			}
135 		}
136 		dorep(argv);
137 	}
138 	else if (xflag)  {
139 		if (!usefile) {
140 			mt = dup(0, -1);
141 			nblock = 1;
142 		}
143 		else if ((mt = open(usefile, OREAD)) < 0) {
144 			fprint(2, "tar: cannot open %s: %r\n", usefile);
145 			exits("open");
146 		}
147 		doxtract(argv);
148 	}
149 	else if (tflag) {
150 		if (!usefile) {
151 			mt = dup(0, -1);
152 			nblock = 1;
153 		}
154 		else if ((mt = open(usefile, OREAD)) < 0) {
155 			fprint(2, "tar: cannot open %s: %r\n", usefile);
156 			exits("open");
157 		}
158 		dotable();
159 	}
160 	else
161 		usage();
162 	exits(0);
163 }
164 
165 void
166 usage(void)
167 {
168 	fprint(2, "tar: usage  tar {txrc}[Rvf] [tarfile] file1 file2...\n");
169 	exits("usage");
170 }
171 
172 void
173 dorep(char **argv)
174 {
175 	char cwdbuf[2048], *cwd, thisdir[2048];
176 	char *cp, *cp2;
177 	int cd;
178 
179 	if (getwd(cwdbuf, sizeof(cwdbuf)) == 0) {
180 		fprint(2, "tar: can't find current directory: %r\n");
181 		exits("cwd");
182 	}
183 	cwd = cwdbuf;
184 
185 	if (!cflag) {
186 		getdir();
187 		do {
188 			passtar();
189 			getdir();
190 		} while (!endtar());
191 	}
192 
193 	while (*argv) {
194 		cp2 = *argv;
195 		if (!strcmp(cp2, "-C") && argv[1]) {
196 			argv++;
197 			if (chdir(*argv) < 0)
198 				perror(*argv);
199 			cwd = *argv;
200 			argv++;
201 			continue;
202 		}
203 		cd = 0;
204 		for (cp = *argv; *cp; cp++)
205 			if (*cp == '/')
206 				cp2 = cp;
207 		if (cp2 != *argv) {
208 			*cp2 = '\0';
209 			chdir(*argv);
210 			if(**argv == '/')
211 				strncpy(thisdir, *argv, sizeof(thisdir));
212 			else
213 				snprint(thisdir, sizeof(thisdir), "%s/%s", cwd, *argv);
214 			*cp2 = '/';
215 			cp2++;
216 			cd = 1;
217 		} else
218 			strncpy(thisdir, cwd, sizeof(thisdir));
219 		putfile(thisdir, *argv++, cp2);
220 		if(cd && chdir(cwd) < 0) {
221 			fprint(2, "tar: can't cd back to %s: %r\n", cwd);
222 			exits("cwd");
223 		}
224 	}
225 	putempty();
226 	putempty();
227 	flushtar();
228 }
229 
230 int
231 endtar(void)
232 {
233 	if (dblock.dbuf.name[0] == '\0') {
234 		backtar();
235 		return(1);
236 	}
237 	else
238 		return(0);
239 }
240 
241 void
242 getdir(void)
243 {
244 	Dir *sp;
245 
246 	readtar((char*)&dblock);
247 	if (dblock.dbuf.name[0] == '\0')
248 		return;
249 	if(stbuf == nil){
250 		stbuf = malloc(sizeof(Dir));
251 		if(stbuf == nil) {
252 			fprint(2, "tar: can't malloc: %r\n");
253 			exits("malloc");
254 		}
255 	}
256 	sp = stbuf;
257 	sp->mode = strtol(dblock.dbuf.mode, 0, 8);
258 	sp->uid = "adm";
259 	sp->gid = "adm";
260 	sp->length = strtol(dblock.dbuf.size, 0, 8);
261 	sp->mtime = strtol(dblock.dbuf.mtime, 0, 8);
262 	chksum = strtol(dblock.dbuf.chksum, 0, 8);
263 	if (chksum != checksum()) {
264 		fprint(2, "directory checksum error\n");
265 		exits("checksum error");
266 	}
267 	sp->qid.type = 0;
268 	/* the mode test is ugly but sometimes necessary */
269 	if (dblock.dbuf.linkflag == '5' || (sp->mode&0170000) == 040000) {
270 		sp->qid.type |= QTDIR;
271 		sp->mode |= DMDIR;
272 	}
273 }
274 
275 void
276 passtar(void)
277 {
278 	long blocks;
279 	char buf[TBLOCK];
280 
281 	if (dblock.dbuf.linkflag == '1' || dblock.dbuf.linkflag == 's')
282 		return;
283 	blocks = stbuf->length;
284 	blocks += TBLOCK-1;
285 	blocks /= TBLOCK;
286 
287 	while (blocks-- > 0)
288 		readtar(buf);
289 }
290 
291 void
292 putfile(char *dir, char *longname, char *sname)
293 {
294 	int infile;
295 	long blocks;
296 	char buf[TBLOCK];
297 	char curdir[4096];
298 	char shortname[4096];
299 	char *cp, *cp2;
300 	Dir *db;
301 	int i, n;
302 
303 	if(strlen(sname) > sizeof shortname - 3){
304 		fprint(2, "tar: %s: name too long (max %d)\n", sname, sizeof shortname - 3);
305 		return;
306 	}
307 
308 	snprint(shortname, sizeof shortname, "./%s", sname);
309 	infile = open(shortname, OREAD);
310 	if (infile < 0) {
311 		fprint(2, "tar: %s: cannot open file - %r\n", longname);
312 		return;
313 	}
314 
315 	if(stbuf != nil)
316 		free(stbuf);
317 	stbuf = dirfstat(infile);
318 
319 	if (stbuf->qid.type & QTDIR) {
320 		/* Directory */
321 		for (i = 0, cp = buf; *cp++ = longname[i++];);
322 		*--cp = '/';
323 		*++cp = 0;
324 		if( (cp - buf) >= NAMSIZ) {
325 			fprint(2, "tar: %s: file name too long\n", longname);
326 			close(infile);
327 			return;
328 		}
329 		stbuf->length = 0;
330 		tomodes(stbuf);
331 		strcpy(dblock.dbuf.name,buf);
332 		dblock.dbuf.linkflag = '5';		/* Directory */
333 		sprint(dblock.dbuf.chksum, "%6o", checksum());
334 		writetar( (char *) &dblock);
335 		if (chdir(shortname) < 0) {
336 			fprint(2, "tar: can't cd to %s: %r\n", shortname);
337 			snprint(curdir, sizeof(curdir), "cd %s", shortname);
338 			exits(curdir);
339 		}
340 		sprint(curdir, "%s/%s", dir, sname);
341 		while ((n = dirread(infile, &db)) > 0) {
342 			for(i = 0; i < n; i++){
343 				strncpy(cp, db[i].name, sizeof buf - (cp-buf));
344 				putfile(curdir, buf, db[i].name);
345 			}free(db);
346 		}
347 		close(infile);
348 		if (chdir(dir) < 0 && chdir("..") < 0) {
349 			fprint(2, "tar: can't cd to ..(%s): %r\n", dir);
350 			snprint(curdir, sizeof(curdir), "cd ..(%s)", dir);
351 			exits(curdir);
352 		}
353 		return;
354 	}
355 
356 
357 	tomodes(stbuf);
358 
359 	cp2 = longname;
360 	for (cp = dblock.dbuf.name, i=0; (*cp++ = *cp2++) && i < NAMSIZ; i++);
361 	if (i >= NAMSIZ) {
362 		fprint(2, "%s: file name too long\n", longname);
363 		close(infile);
364 		return;
365 	}
366 
367 	blocks = (stbuf->length + (TBLOCK-1)) / TBLOCK;
368 	if (vflag) {
369 		fprint(2, "a %s ", longname);
370 		fprint(2, "%ld blocks\n", blocks);
371 	}
372 	dblock.dbuf.linkflag = 0;			/* Regular file */
373 	sprint(dblock.dbuf.chksum, "%6o", checksum());
374 	writetar( (char *) &dblock);
375 
376 	while ((i = readn(infile, buf, TBLOCK)) > 0 && blocks > 0) {
377 		writetar(buf);
378 		blocks--;
379 	}
380 	close(infile);
381 	if (blocks != 0 || i != 0)
382 		fprint(2, "%s: file changed size\n", longname);
383 	while (blocks-- >  0)
384 		putempty();
385 }
386 
387 
388 void
389 doxtract(char **argv)
390 {
391 	Dir null;
392 	long blocks, bytes;
393 	char buf[TBLOCK], outname[NAMSIZ+4];
394 	char **cp;
395 	int ofile;
396 
397 	for (;;) {
398 		getdir();
399 		if (endtar())
400 			break;
401 
402 		if (*argv == 0)
403 			goto gotit;
404 
405 		for (cp = argv; *cp; cp++)
406 			if (prefix(*cp, dblock.dbuf.name))
407 				goto gotit;
408 		passtar();
409 		continue;
410 
411 gotit:
412 		if(checkdir(dblock.dbuf.name, stbuf->mode, &(stbuf->qid)))
413 			continue;
414 
415 		if (dblock.dbuf.linkflag == '1') {
416 			fprint(2, "tar: can't link %s %s\n",
417 				dblock.dbuf.linkname, dblock.dbuf.name);
418 			remove(dblock.dbuf.name);
419 			continue;
420 		}
421 		if (dblock.dbuf.linkflag == 's') {
422 			fprint(2, "tar: %s: cannot symlink\n", dblock.dbuf.name);
423 			continue;
424 		}
425 		if(dblock.dbuf.name[0] != '/' || Rflag)
426 			sprint(outname, "./%s", dblock.dbuf.name);
427 		else
428 			strcpy(outname, dblock.dbuf.name);
429 		if ((ofile = create(outname, OWRITE, stbuf->mode & 0777)) < 0) {
430 			fprint(2, "tar: %s - cannot create: %r\n", outname);
431 			passtar();
432 			continue;
433 		}
434 
435 		blocks = ((bytes = stbuf->length) + TBLOCK-1)/TBLOCK;
436 		if (vflag)
437 			fprint(2, "x %s, %ld bytes\n",
438 				dblock.dbuf.name, bytes);
439 		while (blocks-- > 0) {
440 			readtar(buf);
441 			if (bytes > TBLOCK) {
442 				if (write(ofile, buf, TBLOCK) < 0) {
443 					fprint(2, "tar: %s: HELP - extract write error: %r\n", dblock.dbuf.name);
444 					exits("extract write");
445 				}
446 			} else
447 				if (write(ofile, buf, bytes) < 0) {
448 					fprint(2, "tar: %s: HELP - extract write error: %r\n", dblock.dbuf.name);
449 					exits("extract write");
450 				}
451 			bytes -= TBLOCK;
452 		}
453 		if(Tflag){
454 			nulldir(&null);
455 			null.mtime = stbuf->mtime;
456 			dirfwstat(ofile, &null);
457 		}
458 		close(ofile);
459 	}
460 }
461 
462 void
463 dotable(void)
464 {
465 	for (;;) {
466 		getdir();
467 		if (endtar())
468 			break;
469 		if (vflag)
470 			longt(stbuf);
471 		Bprint(&bout, "%s", dblock.dbuf.name);
472 		if (dblock.dbuf.linkflag == '1')
473 			Bprint(&bout, " linked to %s", dblock.dbuf.linkname);
474 		if (dblock.dbuf.linkflag == 's')
475 			Bprint(&bout, " -> %s", dblock.dbuf.linkname);
476 		Bprint(&bout, "\n");
477 		passtar();
478 	}
479 }
480 
481 void
482 putempty(void)
483 {
484 	char buf[TBLOCK];
485 
486 	memset(buf, 0, TBLOCK);
487 	writetar(buf);
488 }
489 
490 void
491 longt(Dir *st)
492 {
493 	char *cp;
494 
495 	Bprint(&bout, "%M %4d/%1d ", st->mode, 0, 0);	/* 0/0 uid/gid */
496 	Bprint(&bout, "%8lld", st->length);
497 	cp = ctime(st->mtime);
498 	Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24);
499 }
500 
501 int
502 checkdir(char *name, int mode, Qid *qid)
503 {
504 	char *cp;
505 	int f;
506 	Dir *d, null;
507 
508 	if(Rflag && *name == '/')
509 		name++;
510 	cp = name;
511 	if(*cp == '/')
512 		cp++;
513 	for (; *cp; cp++) {
514 		if (*cp == '/') {
515 			*cp = '\0';
516 			if (access(name, 0) < 0) {
517 				f = create(name, OREAD, DMDIR + 0775L);
518 				if(f < 0)
519 					fprint(2, "tar: mkdir %s failed: %r\n", name);
520 				close(f);
521 			}
522 			*cp = '/';
523 		}
524 	}
525 
526 	/* if this is a directory, chmod it to the mode in the tar plus 700 */
527 	if(cp[-1] == '/' || (qid->type&QTDIR)){
528 		if((d=dirstat(name)) != 0){
529 			nulldir(&null);
530 			null.mode = DMDIR | (mode & 0777) | 0700;
531 			dirwstat(name, &null);
532 			free(d);
533 		}
534 		return 1;
535 	} else
536 		return 0;
537 }
538 
539 void
540 tomodes(Dir *sp)
541 {
542 	char *cp;
543 
544 	for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
545 		*cp = '\0';
546 	sprint(dblock.dbuf.mode, "%6lo ", sp->mode & 0777);
547 	sprint(dblock.dbuf.uid, "%6o ", uflag);
548 	sprint(dblock.dbuf.gid, "%6o ", gflag);
549 	sprint(dblock.dbuf.size, "%11llo ", sp->length);
550 	sprint(dblock.dbuf.mtime, "%11lo ", sp->mtime);
551 }
552 
553 int
554 checksum(void)
555 {
556 	int i;
557 	char *cp;
558 
559 	for (cp = dblock.dbuf.chksum; cp < &dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)]; cp++)
560 		*cp = ' ';
561 	i = 0;
562 	for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
563 		i += *cp & 0xff;
564 	return(i);
565 }
566 
567 int
568 prefix(char *s1, char *s2)
569 {
570 	while (*s1)
571 		if (*s1++ != *s2++)
572 			return(0);
573 	if (*s2)
574 		return(*s2 == '/');
575 	return(1);
576 }
577 
578 int
579 readtar(char *buffer)
580 {
581 	int i;
582 
583 	if (recno >= nblock || first == 0) {
584 		if ((i = readn(mt, tbuf, TBLOCK*nblock)) <= 0) {
585 			fprint(2, "tar: archive read error: %r\n");
586 			exits("archive read");
587 		}
588 		if (first == 0) {
589 			if ((i % TBLOCK) != 0) {
590 				fprint(2, "tar: archive blocksize error: %r\n");
591 				exits("blocksize");
592 			}
593 			i /= TBLOCK;
594 			if (i != nblock) {
595 				fprint(2, "tar: blocksize = %d\n", i);
596 				nblock = i;
597 			}
598 		}
599 		recno = 0;
600 	}
601 	first = 1;
602 	memmove(buffer, &tbuf[recno++], TBLOCK);
603 	return(TBLOCK);
604 }
605 
606 int
607 writetar(char *buffer)
608 {
609 	first = 1;
610 	if (recno >= nblock) {
611 		if (write(mt, tbuf, TBLOCK*nblock) != TBLOCK*nblock) {
612 			fprint(2, "tar: archive write error: %r\n");
613 			exits("write");
614 		}
615 		recno = 0;
616 	}
617 	memmove(&tbuf[recno++], buffer, TBLOCK);
618 	if (recno >= nblock) {
619 		if (write(mt, tbuf, TBLOCK*nblock) != TBLOCK*nblock) {
620 			fprint(2, "tar: archive write error: %r\n");
621 			exits("write");
622 		}
623 		recno = 0;
624 	}
625 	return(TBLOCK);
626 }
627 
628 /*
629  * backup over last tar block
630  */
631 void
632 backtar(void)
633 {
634 	seek(mt, -TBLOCK*nblock, 1);
635 	recno--;
636 }
637 
638 void
639 flushtar(void)
640 {
641 	write(mt, tbuf, TBLOCK*nblock);
642 }
643