1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4
5 enum{
6 LEN = 8*1024,
7 NFLDS = 6, /* filename, modes, uid, gid, mtime, bytes */
8 };
9
10 int selected(char*, int, char*[]);
11 void mkdirs(char*, char*);
12 void mkdir(char*, ulong, ulong, char*, char*);
13 void extract(char*, ulong, ulong, char*, char*, uvlong);
14 void seekpast(uvlong);
15 void error(char*, ...);
16 void warn(char*, ...);
17 void usage(void);
18 #pragma varargck argpos warn 1
19 #pragma varargck argpos error 1
20
21 Biobufhdr bin;
22 uchar binbuf[2*LEN];
23 char linebuf[LEN];
24 int uflag;
25 int hflag;
26 int vflag;
27 int Tflag;
28
29 void
main(int argc,char ** argv)30 main(int argc, char **argv)
31 {
32 Biobuf bout;
33 char *fields[NFLDS], name[2*LEN], *p, *namep;
34 char *uid, *gid;
35 ulong mode, mtime;
36 uvlong bytes;
37
38 quotefmtinstall();
39 namep = name;
40 ARGBEGIN{
41 case 'd':
42 p = ARGF();
43 if(strlen(p) >= LEN)
44 error("destination fs name too long\n");
45 strcpy(name, p);
46 namep = name + strlen(name);
47 break;
48 case 'h':
49 hflag = 1;
50 Binit(&bout, 1, OWRITE);
51 break;
52 case 'u':
53 uflag = 1;
54 Tflag = 1;
55 break;
56 case 'T':
57 Tflag = 1;
58 break;
59 case 'v':
60 vflag = 1;
61 break;
62 default:
63 usage();
64 }ARGEND
65
66 Binits(&bin, 0, OREAD, binbuf, sizeof binbuf);
67 while(p = Brdline(&bin, '\n')){
68 p[Blinelen(&bin)-1] = '\0';
69 strcpy(linebuf, p);
70 p = linebuf;
71 if(strcmp(p, "end of archive") == 0){
72 Bterm(&bout);
73 fprint(2, "done\n");
74 exits(0);
75 }
76 if (gettokens(p, fields, NFLDS, " \t") != NFLDS){
77 warn("too few fields in file header");
78 continue;
79 }
80 p = unquotestrdup(fields[0]);
81 strcpy(namep, p);
82 free(p);
83 mode = strtoul(fields[1], 0, 8);
84 uid = fields[2];
85 gid = fields[3];
86 mtime = strtoul(fields[4], 0, 10);
87 bytes = strtoull(fields[5], 0, 10);
88 if(argc){
89 if(!selected(namep, argc, argv)){
90 if(bytes)
91 seekpast(bytes);
92 continue;
93 }
94 mkdirs(name, namep);
95 }
96 if(hflag){
97 Bprint(&bout, "%q %luo %q %q %lud %llud\n",
98 name, mode, uid, gid, mtime, bytes);
99 if(bytes)
100 seekpast(bytes);
101 continue;
102 }
103 if(mode & DMDIR)
104 mkdir(name, mode, mtime, uid, gid);
105 else
106 extract(name, mode, mtime, uid, gid, bytes);
107 }
108 fprint(2, "premature end of archive\n");
109 exits("premature end of archive");
110 }
111
112 int
fileprefix(char * prefix,char * s)113 fileprefix(char *prefix, char *s)
114 {
115 while(*prefix)
116 if(*prefix++ != *s++)
117 return 0;
118 if(*s && *s != '/')
119 return 0;
120 return 1;
121 }
122
123 int
selected(char * s,int argc,char * argv[])124 selected(char *s, int argc, char *argv[])
125 {
126 int i;
127
128 for(i=0; i<argc; i++)
129 if(fileprefix(argv[i], s))
130 return 1;
131 return 0;
132 }
133
134 void
mkdirs(char * name,char * namep)135 mkdirs(char *name, char *namep)
136 {
137 char buf[2*LEN], *p;
138 int fd;
139
140 strcpy(buf, name);
141 for(p = &buf[namep - name]; p = utfrune(p, '/'); p++){
142 if(p[1] == '\0')
143 return;
144 *p = 0;
145 fd = create(buf, OREAD, 0775|DMDIR);
146 close(fd);
147 *p = '/';
148 }
149 }
150
151 void
mkdir(char * name,ulong mode,ulong mtime,char * uid,char * gid)152 mkdir(char *name, ulong mode, ulong mtime, char *uid, char *gid)
153 {
154 Dir *d, xd;
155 int fd;
156 char *p;
157 char olderr[256];
158
159 fd = create(name, OREAD, mode);
160 if(fd < 0){
161 rerrstr(olderr, sizeof(olderr));
162 if((d = dirstat(name)) == nil || !(d->mode & DMDIR)){
163 free(d);
164 warn("can't make directory %q, mode %luo: %s", name, mode, olderr);
165 return;
166 }
167 free(d);
168 }
169 close(fd);
170
171 d = &xd;
172 nulldir(d);
173 p = utfrrune(name, L'/');
174 if(p)
175 p++;
176 else
177 p = name;
178 d->name = p;
179 if(uflag){
180 d->uid = uid;
181 d->gid = gid;
182 }
183 if(Tflag)
184 d->mtime = mtime;
185 d->mode = mode;
186 if(dirwstat(name, d) < 0)
187 warn("can't set modes for %q: %r", name);
188
189 if(uflag||Tflag){
190 if((d = dirstat(name)) == nil){
191 warn("can't reread modes for %q: %r", name);
192 return;
193 }
194 if(Tflag && d->mtime != mtime)
195 warn("%q: time mismatch %lud %lud\n", name, mtime, d->mtime);
196 if(uflag && strcmp(uid, d->uid))
197 warn("%q: uid mismatch %q %q", name, uid, d->uid);
198 if(uflag && strcmp(gid, d->gid))
199 warn("%q: gid mismatch %q %q", name, gid, d->gid);
200 }
201 }
202
203 void
extract(char * name,ulong mode,ulong mtime,char * uid,char * gid,uvlong bytes)204 extract(char *name, ulong mode, ulong mtime, char *uid, char *gid, uvlong bytes)
205 {
206 Dir d, *nd;
207 Biobuf *b;
208 char buf[LEN];
209 char *p;
210 ulong n;
211 uvlong tot;
212
213 if(vflag)
214 print("x %q %llud bytes\n", name, bytes);
215
216 b = Bopen(name, OWRITE);
217 if(!b){
218 warn("can't make file %q: %r", name);
219 seekpast(bytes);
220 return;
221 }
222 for(tot = 0; tot < bytes; tot += n){
223 n = sizeof buf;
224 if(tot + n > bytes)
225 n = bytes - tot;
226 n = Bread(&bin, buf, n);
227 if(n <= 0)
228 error("premature eof reading %q", name);
229 if(Bwrite(b, buf, n) != n)
230 warn("error writing %q: %r", name);
231 }
232
233 nulldir(&d);
234 p = utfrrune(name, '/');
235 if(p)
236 p++;
237 else
238 p = name;
239 d.name = p;
240 if(uflag){
241 d.uid = uid;
242 d.gid = gid;
243 }
244 if(Tflag)
245 d.mtime = mtime;
246 d.mode = mode;
247 Bflush(b);
248 if(dirfwstat(Bfildes(b), &d) < 0)
249 warn("can't set modes for %q: %r", name);
250 if(uflag||Tflag){
251 if((nd = dirfstat(Bfildes(b))) == nil)
252 warn("can't reread modes for %q: %r", name);
253 else{
254 if(Tflag && nd->mtime != mtime)
255 warn("%q: time mismatch %lud %lud\n",
256 name, mtime, nd->mtime);
257 if(uflag && strcmp(uid, nd->uid))
258 warn("%q: uid mismatch %q %q",
259 name, uid, nd->uid);
260 if(uflag && strcmp(gid, nd->gid))
261 warn("%q: gid mismatch %q %q",
262 name, gid, nd->gid);
263 free(nd);
264 }
265 }
266 Bterm(b);
267 }
268
269 void
seekpast(uvlong bytes)270 seekpast(uvlong bytes)
271 {
272 char buf[LEN];
273 long n;
274 uvlong tot;
275
276 for(tot = 0; tot < bytes; tot += n){
277 n = sizeof buf;
278 if(tot + n > bytes)
279 n = bytes - tot;
280 n = Bread(&bin, buf, n);
281 if(n < 0)
282 error("premature eof");
283 }
284 }
285
286 void
error(char * fmt,...)287 error(char *fmt, ...)
288 {
289 char buf[1024];
290 va_list arg;
291
292 sprint(buf, "%q: ", argv0);
293 va_start(arg, fmt);
294 vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
295 va_end(arg);
296 fprint(2, "%s\n", buf);
297 exits(0);
298 }
299
300 void
warn(char * fmt,...)301 warn(char *fmt, ...)
302 {
303 char buf[1024];
304 va_list arg;
305
306 sprint(buf, "%q: ", argv0);
307 va_start(arg, fmt);
308 vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg);
309 va_end(arg);
310 fprint(2, "%s\n", buf);
311 }
312
313 void
usage(void)314 usage(void)
315 {
316 fprint(2, "usage: mkext [-h] [-u] [-v] [-d dest-fs] [file ...]\n");
317 exits("usage");
318 }
319