1 /*
2 * tarsplit [-d] [-p pfx] [-s size] - split a tar archive into independent
3 * tar archives under some size
4 */
5
6 #include <u.h>
7 #include <libc.h>
8 #include <ctype.h>
9 #include "tar.h"
10
11 enum {
12 Stdin,
13 Endsize = 2 * Tblock, /* size of zero blocks at archive end */
14 };
15
16 /* private data */
17 static char *filenm;
18 static char *prefix = "ts.";
19 static vlong size = 512*1024*1024; /* fits on a CD with room to spare */
20
21 static int
opennext(int out,char * prefix)22 opennext(int out, char *prefix)
23 {
24 static int filenum = 0;
25
26 if (out >= 0) {
27 fprint(2, "%s: opennext called with file open\n", argv0);
28 exits("open botch");
29 }
30 free(filenm);
31 filenm = smprint("%s%.5d", prefix, filenum++);
32 fprint(2, "%s: %s ...", filenm, thisnm);
33 out = create(filenm, OWRITE, 0666);
34 if (out < 0)
35 sysfatal("%s: %r", filenm);
36 newarch();
37 return out;
38 }
39
40 static int
split(int in,int out,char *)41 split(int in, int out, char * /* inname */)
42 {
43 vlong len, membsz;
44 uvlong outoff = 0;
45 static Hblock hdr;
46 Hblock *hp = &hdr;
47
48 while (getdir(hp, in, &len)) {
49 membsz = Tblock + ROUNDUP((uvlong)len, Tblock);
50 if (outoff + membsz + Endsize > size) { /* won't fit? */
51 out = closeout(out, filenm, 1);
52 if (membsz + Endsize > size)
53 sysfatal("archive member %s (%,lld) + overhead "
54 "exceeds size limit %,lld", hp->name,
55 len, size);
56 }
57 if (out < 0)
58 out = opennext(out, prefix);
59 /* write directory block */
60 writetar(out, (char *)hp, Tblock);
61 outoff = passtar(hp, in, out, len);
62 }
63 return out;
64 }
65
66 void
usage(void)67 usage(void)
68 {
69 fprint(2, "usage: %s [-p pfx] [-s size] [file]...\n", argv0);
70 exits("usage");
71 }
72
73 void
main(int argc,char ** argv)74 main(int argc, char **argv)
75 {
76 int out = -1;
77
78 ARGBEGIN {
79 case 'p':
80 prefix = EARGF(usage());
81 break;
82 case 's':
83 size = atoll(EARGF(usage()));
84 if (size < Tblock + Endsize)
85 sysfatal("implausible max size of %lld bytes", size);
86 break;
87 default:
88 usage();
89 } ARGEND
90
91 if (argc <= 0)
92 out = split(Stdin, out, "/fd/0");
93 else
94 for (; argc-- > 0; argv++) {
95 int in = open(argv[0], OREAD);
96
97 if (in < 0)
98 sysfatal("%s: %r", argv[0]);
99 out = split(in, out, argv[0]);
100 close(in);
101 }
102 closeout(out, filenm, 1);
103 exits(0);
104 }
105