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