Mercurial > hg > toybox
comparison toys/pending/tar.c @ 1379:b02c05e64f20 draft
TAR - this supports archive creation and extraction based on the USTAR format (described in PAX Spec). For (de)compression '-z' gzip is supported.
author | Ashwini Sharma <ak.ashwini1981@gmail.com> |
---|---|
date | Fri, 04 Jul 2014 21:46:00 -0500 |
parents | |
children | 00c20f410c46 |
comparison
equal
deleted
inserted
replaced
1378:6c54196fce62 | 1379:b02c05e64f20 |
---|---|
1 /* tar.c - create/extract archives | |
2 * | |
3 * Copyright 2014 Ashwini Kumar <ak.ashwini81@gmail.com> | |
4 * | |
5 * USTAR interchange format is of interest in | |
6 * See http://http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html | |
7 * For writing to external program | |
8 * http://www.gnu.org/software/tar/manual/html_node/Writing-to-an-External-Program.html | |
9 | |
10 USE_TAR(NEWTOY(tar, "&(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc]", TOYFLAG_USR|TOYFLAG_BIN)) | |
11 | |
12 config TAR | |
13 bool "tar" | |
14 default n | |
15 help | |
16 usage: tar -[cxtzhmvO] [-X FILE] [-T FILE] [-f TARFILE] [-C DIR] | |
17 | |
18 Create, extract, or list files from a tar file | |
19 | |
20 Operation: | |
21 c Create | |
22 f Name of TARFILE ('-' for stdin/out) | |
23 h Follow symlinks | |
24 m Don't restore mtime | |
25 t List | |
26 v Verbose | |
27 x Extract | |
28 z (De)compress using gzip | |
29 C Change to DIR before operation | |
30 O Extract to stdout | |
31 exclude=FILE File to exclude | |
32 X File with names to exclude | |
33 T File with names to include | |
34 */ | |
35 #define FOR_tar | |
36 #include "toys.h" | |
37 | |
38 GLOBALS( | |
39 char *fname; | |
40 char *dir; | |
41 struct arg_list *inc_file; | |
42 struct arg_list *exc_file; | |
43 char *tocmd; | |
44 struct arg_list *exc; | |
45 | |
46 struct arg_list *inc, *pass; | |
47 void *inodes, *handle; | |
48 ) | |
49 | |
50 struct tar_hdr { | |
51 char name[100], mode[8], uid[8], gid[8],size[12], mtime[12], chksum[8], | |
52 type, link[100], magic[8], uname[32], gname[32], major[8], minor[8], | |
53 prefix[155], padd[12]; | |
54 }; | |
55 | |
56 struct file_header { | |
57 char *name, *link_target, *uname, *gname; | |
58 off_t size; | |
59 uid_t uid; | |
60 gid_t gid; | |
61 mode_t mode; | |
62 time_t mtime; | |
63 dev_t device; | |
64 }; | |
65 | |
66 struct archive_handler { | |
67 int src_fd; | |
68 struct file_header file_hdr; | |
69 off_t offset; | |
70 void (*extract_handler)(struct archive_handler*); | |
71 }; | |
72 | |
73 struct inode_list { | |
74 struct inode_list *next; | |
75 char *arg; | |
76 ino_t ino; | |
77 dev_t dev; | |
78 }; | |
79 | |
80 static void copy_in_out(int src, int dst, off_t size) | |
81 { | |
82 int i, rd, rem = size%512, cnt; | |
83 | |
84 cnt = size/512 + (rem?1:0); | |
85 | |
86 for (i = 0; i < cnt; i++) { | |
87 rd = (((i == cnt-1) && rem)? rem:512); | |
88 if (readall(src, toybuf, rd) != rd) error_exit("short read"); | |
89 writeall(dst, toybuf, rd); | |
90 } | |
91 } | |
92 | |
93 //convert to octal | |
94 static void itoo(char *str, int len, off_t val) | |
95 { | |
96 char *t, tmp[sizeof(off_t)*3+1]; //1 for NUL termination | |
97 int cnt = sprintf(tmp, "%0*llo", len, val); | |
98 | |
99 t = tmp; | |
100 t += (cnt - len); | |
101 if (*t == '0') t++; | |
102 memcpy(str, t, len); | |
103 } | |
104 | |
105 static struct inode_list *seen_inode(void **list, struct stat *st, char *name) | |
106 { | |
107 if (!st) llist_traverse(*list, llist_free_arg); | |
108 else if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) { | |
109 struct inode_list *new; | |
110 | |
111 for (new = *list; new; new = new->next) | |
112 if(new->ino == st->st_ino && new->dev == st->st_dev) | |
113 return new; | |
114 | |
115 new = xzalloc(sizeof(*new)); | |
116 new->ino = st->st_ino; | |
117 new->dev = st->st_dev; | |
118 new->arg = xstrdup(name); | |
119 new->next = *list; | |
120 *list = new; | |
121 } | |
122 return 0; | |
123 } | |
124 | |
125 static void write_longname(struct archive_handler *tar, char *name, char type) | |
126 { | |
127 struct tar_hdr tmp; | |
128 unsigned int sum = 0; | |
129 int i, sz = strlen(name) +1; | |
130 char buf[512] = {0,}; | |
131 | |
132 memset(&tmp, 0, sizeof(tmp)); | |
133 strcpy(tmp.name, "././@LongLink"); | |
134 sprintf(tmp.mode, "%0*d", sizeof(tmp.mode)-1, 0); | |
135 sprintf(tmp.uid, "%0*d", sizeof(tmp.uid)-1, 0); | |
136 sprintf(tmp.gid, "%0*d", sizeof(tmp.gid)-1, 0); | |
137 sprintf(tmp.size, "%0*d", sizeof(tmp.size)-1, 0); | |
138 sprintf(tmp.mtime, "%0*d", sizeof(tmp.mtime)-1, 0); | |
139 itoo(tmp.size, sizeof(tmp.size), sz); | |
140 tmp.type = type; | |
141 memset(tmp.chksum, ' ', 8); | |
142 strcpy(tmp.magic, "ustar "); | |
143 for (i= 0; i < 512; i++) sum += (unsigned int)((char*)&tmp)[i]; | |
144 itoo(tmp.chksum, sizeof(tmp.chksum)-1, sum); | |
145 | |
146 writeall(tar->src_fd, (void*) &tmp, sizeof(tmp)); | |
147 //write name to archive | |
148 writeall(tar->src_fd, name, sz); | |
149 if (sz%512) writeall(tar->src_fd, buf, (512-(sz%512))); | |
150 } | |
151 | |
152 static int filter(struct arg_list *lst, char *name) | |
153 { | |
154 struct arg_list *cur; | |
155 | |
156 for (cur = lst; cur; cur = cur->next) | |
157 if (!fnmatch(cur->arg, name, 1<<3)) return 1; | |
158 return 0; | |
159 } | |
160 | |
161 static void add_file(struct archive_handler *tar, char **nam, struct stat *st) | |
162 { | |
163 struct tar_hdr hdr; | |
164 struct passwd *pw; | |
165 struct group *gr; | |
166 struct inode_list *node; | |
167 int i, fd =-1; | |
168 char *c, *p, *name = *nam, *lnk, *hname, buf[512] = {0,}; | |
169 unsigned int sum = 0; | |
170 static int warn = 1; | |
171 | |
172 for (p = name; *p; p++) | |
173 if ((p == name || p[-1] == '/') && *p != '/' | |
174 && filter(TT.exc, p)) return; | |
175 | |
176 if (S_ISDIR(st->st_mode) && name[strlen(name)-1] != '/') { | |
177 lnk = xmprintf("%s/",name); | |
178 free(name); | |
179 *nam = name = lnk; | |
180 } | |
181 hname = name; | |
182 //remove leading '/' or relative path '../' component | |
183 if (*hname == '/') hname++; | |
184 if (!*hname) return; | |
185 while ((c = strstr(hname, "../"))) hname = c + 3; | |
186 if (warn && hname != name) { | |
187 printf("removing leading '%.*s' " | |
188 "from member names\n",hname-name, name); | |
189 warn = 0; | |
190 } | |
191 | |
192 memset(&hdr, 0, sizeof(hdr)); | |
193 strncpy(hdr.name, hname, sizeof(hdr.name)); | |
194 itoo(hdr.mode, sizeof(hdr.mode), st->st_mode &07777); | |
195 itoo(hdr.uid, sizeof(hdr.uid), st->st_uid); | |
196 itoo(hdr.gid, sizeof(hdr.gid), st->st_gid); | |
197 itoo(hdr.size, sizeof(hdr.size), 0); //set size later | |
198 itoo(hdr.mtime, sizeof(hdr.mtime), st->st_mtime); | |
199 for (i=0; i<sizeof(hdr.chksum); i++) hdr.chksum[i] = ' '; | |
200 | |
201 if ((node = seen_inode(&TT.inodes, st, hname))) { | |
202 //this is a hard link | |
203 hdr.type = '1'; | |
204 if (strlen(node->arg) > sizeof(hdr.link)) | |
205 write_longname(tar, hname, 'K'); //write longname LINK | |
206 strncpy(hdr.link, node->arg, sizeof(hdr.link)); | |
207 } else if (S_ISREG(st->st_mode)) { | |
208 hdr.type = '0'; | |
209 if (st->st_size <= (off_t)0777777777777LL) | |
210 itoo(hdr.size, sizeof(hdr.size), st->st_size); | |
211 else { | |
212 error_msg("can't store file '%s' of size '%d'\n", hname, st->st_size); | |
213 return; | |
214 } | |
215 } else if (S_ISLNK(st->st_mode)) { | |
216 hdr.type = '2'; //'K' long link | |
217 if (!(lnk = xreadlink(name))) { | |
218 perror_msg("readlink"); | |
219 return; | |
220 } | |
221 if (strlen(lnk) > sizeof(hdr.link)) | |
222 write_longname(tar, hname, 'K'); //write longname LINK | |
223 strncpy(hdr.link, lnk, sizeof(hdr.link)); | |
224 free(lnk); | |
225 } | |
226 else if (S_ISDIR(st->st_mode)) hdr.type = '5'; | |
227 else if (S_ISFIFO(st->st_mode)) hdr.type = '6'; | |
228 else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) { | |
229 hdr.type = (S_ISCHR(st->st_mode))?'3':'4'; | |
230 itoo(hdr.major, sizeof(hdr.major), major(st->st_rdev)); | |
231 itoo(hdr.minor, sizeof(hdr.minor), minor(st->st_rdev)); | |
232 } else { | |
233 error_msg("unknown file type '%s'"); | |
234 return; | |
235 } | |
236 if (strlen(hname) > sizeof(hdr.name)) | |
237 write_longname(tar, hname, 'L'); //write longname NAME | |
238 strcpy(hdr.magic, "ustar "); | |
239 if ((pw = getpwuid(st->st_uid))) | |
240 snprintf(hdr.uname, sizeof(hdr.uname), "%s", pw->pw_name); | |
241 else snprintf(hdr.uname, sizeof(hdr.uname), "%d", st->st_uid); | |
242 | |
243 if ((gr = getgrgid(st->st_gid))) | |
244 snprintf(hdr.gname, sizeof(hdr.gname), "%s", gr->gr_name); | |
245 else snprintf(hdr.gname, sizeof(hdr.gname), "%d", st->st_gid); | |
246 | |
247 //calculate chksum. | |
248 for (i= 0; i < 512; i++) sum += (unsigned int)((char*)&hdr)[i]; | |
249 itoo(hdr.chksum, sizeof(hdr.chksum)-1, sum); | |
250 if (toys.optflags & FLAG_v) printf("%s\n",hname); | |
251 writeall(tar->src_fd, (void*)&hdr, 512); | |
252 | |
253 //write actual data to archive | |
254 if (hdr.type != '0') return; //nothing to write | |
255 if ((fd = open(name, O_RDONLY)) < 0) { | |
256 perror_msg("can't open '%s'", name); | |
257 return; | |
258 } | |
259 copy_in_out(fd, tar->src_fd, st->st_size); | |
260 if (st->st_size%512) writeall(tar->src_fd, buf, (512-(st->st_size%512))); | |
261 close(fd); | |
262 } | |
263 | |
264 static int add_to_tar(struct dirtree *node) | |
265 { | |
266 struct stat st; | |
267 char *path; | |
268 struct archive_handler *hdl = (struct archive_handler*)TT.handle; | |
269 | |
270 if (!fstat(hdl->src_fd, &st) && st.st_dev == node->st.st_dev | |
271 && st.st_ino == node->st.st_ino) { | |
272 error_msg("'%s' file is the archive; not dumped", TT.fname); | |
273 return ((DIRTREE_RECURSE | ((toys.optflags & FLAG_h)?DIRTREE_SYMFOLLOW:0))); | |
274 } | |
275 | |
276 if (node->parent && !dirtree_notdotdot(node)) return 0; | |
277 path = dirtree_path(node, 0); | |
278 add_file(hdl, &path, &(node->st)); //path may be modified | |
279 free(path); | |
280 if (toys.optflags & FLAG_no_recursion) return 0; | |
281 return ((DIRTREE_RECURSE | ((toys.optflags & FLAG_h)?DIRTREE_SYMFOLLOW:0))); | |
282 } | |
283 | |
284 static void compress_stream(struct archive_handler *tar_hdl) | |
285 { | |
286 int pipefd[2]; | |
287 pid_t cpid; | |
288 | |
289 if (pipe(pipefd) == -1) error_exit("pipe"); | |
290 | |
291 signal(SIGPIPE, SIG_IGN); | |
292 cpid = fork(); | |
293 if (cpid == -1) perror_exit("fork"); | |
294 | |
295 if (!cpid) { /* Child reads from pipe */ | |
296 char *argv[] = {"gzip", "-f", NULL}; | |
297 xclose(pipefd[1]); /* Close unused write*/ | |
298 dup2(pipefd[0], 0); | |
299 dup2(tar_hdl->src_fd, 1); //write to tar fd | |
300 xexec(argv); | |
301 } else { | |
302 xclose(pipefd[0]); /* Close unused read end */ | |
303 dup2(pipefd[1], tar_hdl->src_fd); //write to pipe | |
304 } | |
305 } | |
306 | |
307 static void extract_to_stdout(struct archive_handler *tar) | |
308 { | |
309 struct file_header *file_hdr = &tar->file_hdr; | |
310 | |
311 copy_in_out(tar->src_fd, 0, file_hdr->size); | |
312 tar->offset += file_hdr->size; | |
313 } | |
314 | |
315 static void extract_to_command(struct archive_handler *tar) | |
316 { | |
317 int pipefd[2], status = 0; | |
318 pid_t cpid; | |
319 struct file_header *file_hdr = &tar->file_hdr; | |
320 | |
321 if (pipe(pipefd) == -1) error_exit("pipe"); | |
322 if (!S_ISREG(file_hdr->mode)) return; //only regular files are supported. | |
323 | |
324 cpid = fork(); | |
325 if (cpid == -1) perror_exit("fork"); | |
326 | |
327 if (!cpid) { // Child reads from pipe | |
328 char buf[64], *argv[4] = {"sh", "-c", TT.tocmd, NULL}; | |
329 | |
330 setenv("TAR_FILETYPE", "f", 1); | |
331 sprintf(buf, "%0o", file_hdr->mode); | |
332 setenv("TAR_MODE", buf, 1); | |
333 sprintf(buf, "%ld", (long)file_hdr->size); | |
334 setenv("TAR_SIZE", buf, 1); | |
335 setenv("TAR_FILENAME", file_hdr->name, 1); | |
336 setenv("TAR_UNAME", file_hdr->uname, 1); | |
337 setenv("TAR_GNAME", file_hdr->gname, 1); | |
338 sprintf(buf, "%0o", (int)file_hdr->mtime); | |
339 setenv("TAR_MTIME", buf, 1); | |
340 sprintf(buf, "%0o", file_hdr->uid); | |
341 setenv("TAR_UID", buf, 1); | |
342 sprintf(buf, "%0o", file_hdr->gid); | |
343 setenv("TAR_GID", buf, 1); | |
344 | |
345 xclose(pipefd[1]); // Close unused write | |
346 dup2(pipefd[0], 0); | |
347 signal(SIGPIPE, SIG_DFL); | |
348 xexec(argv); | |
349 } else { | |
350 xclose(pipefd[0]); // Close unused read end | |
351 copy_in_out(tar->src_fd, pipefd[1], file_hdr->size); | |
352 tar->offset += file_hdr->size; | |
353 xclose(pipefd[1]); | |
354 waitpid(cpid, &status, 0); | |
355 if (WIFSIGNALED(status)) | |
356 xprintf("tar : %d: child returned %d\n", cpid, WTERMSIG(status)); | |
357 } | |
358 } | |
359 | |
360 static void extract_to_disk(struct archive_handler *tar) | |
361 { | |
362 int flags, dst_fd = -1; | |
363 char *s; | |
364 struct stat ex; | |
365 struct file_header *file_hdr = &tar->file_hdr; | |
366 | |
367 if (file_hdr->name[strlen(file_hdr->name)-1] == '/') | |
368 file_hdr->name[strlen(file_hdr->name)-1] = 0; | |
369 //Regular file with preceding path | |
370 if ((s = strrchr(file_hdr->name, '/'))) { | |
371 if (mkpathat(AT_FDCWD, file_hdr->name, 00, 2) && errno !=EEXIST) { | |
372 error_msg(":%s: not created", file_hdr->name); | |
373 return; | |
374 } | |
375 } | |
376 | |
377 //remove old file, if exists | |
378 if (!(toys.optflags & FLAG_k) && !S_ISDIR(file_hdr->mode) | |
379 && !lstat( file_hdr->name, &ex)) { | |
380 if (unlink(file_hdr->name)) { | |
381 perror_msg("can't remove: %s",file_hdr->name); | |
382 } | |
383 } | |
384 | |
385 //hard link | |
386 if (S_ISREG(file_hdr->mode) && file_hdr->link_target) { | |
387 if (link(file_hdr->link_target, file_hdr->name)) | |
388 perror_msg("can't link '%s' -> '%s'",file_hdr->name, file_hdr->link_target); | |
389 goto COPY; | |
390 } | |
391 | |
392 switch (file_hdr->mode & S_IFMT) { | |
393 case S_IFREG: | |
394 flags = O_WRONLY|O_CREAT|O_EXCL; | |
395 if (toys.optflags & FLAG_overwrite) flags = O_WRONLY|O_CREAT|O_TRUNC; | |
396 dst_fd = open(file_hdr->name, flags, file_hdr->mode & 07777); | |
397 if (dst_fd == -1) perror_msg("%s: can't open", file_hdr->name); | |
398 break; | |
399 case S_IFDIR: | |
400 if ((mkdir(file_hdr->name, file_hdr->mode) == -1) && errno != EEXIST) | |
401 perror_msg("%s: can't create", file_hdr->name); | |
402 break; | |
403 case S_IFLNK: | |
404 if (symlink(file_hdr->link_target, file_hdr->name)) | |
405 perror_msg("can't link '%s' -> '%s'",file_hdr->name, file_hdr->link_target); | |
406 break; | |
407 case S_IFBLK: | |
408 case S_IFCHR: | |
409 case S_IFIFO: | |
410 if (mknod(file_hdr->name, file_hdr->mode, file_hdr->device)) | |
411 perror_msg("can't create '%s'", file_hdr->name); | |
412 break; | |
413 default: | |
414 printf("type not yet supported\n"); | |
415 break; | |
416 } | |
417 | |
418 //copy file.... | |
419 COPY: | |
420 copy_in_out(tar->src_fd, dst_fd, file_hdr->size); | |
421 tar->offset += file_hdr->size; | |
422 close(dst_fd); | |
423 | |
424 if (S_ISLNK(file_hdr->mode)) return; | |
425 if (!(toys.optflags & FLAG_o)) { | |
426 //set ownership..., --no-same-owner, --numeric-owner | |
427 uid_t u = file_hdr->uid; | |
428 gid_t g = file_hdr->gid; | |
429 | |
430 if (!(toys.optflags & FLAG_numeric_owner)) { | |
431 struct group *gr = getgrnam(file_hdr->gname); | |
432 struct passwd *pw = getpwnam(file_hdr->uname); | |
433 if (pw) u = pw->pw_uid; | |
434 if (gr) g = gr->gr_gid; | |
435 } | |
436 chown(file_hdr->name, u, g); | |
437 } | |
438 | |
439 if (toys.optflags & FLAG_p) // || !(toys.optflags & FLAG_no_same_permissions)) | |
440 chmod(file_hdr->name, file_hdr->mode); | |
441 | |
442 //apply mtime | |
443 if (!(toys.optflags & FLAG_m)) { | |
444 struct timeval times[2] = {{file_hdr->mtime, 0},{file_hdr->mtime, 0}}; | |
445 utimes(file_hdr->name, times); | |
446 } | |
447 } | |
448 | |
449 static void add_to_list(struct arg_list **llist, char *name) | |
450 { | |
451 struct arg_list **list = llist; | |
452 | |
453 while (*list) list=&((*list)->next); | |
454 *list = xzalloc(sizeof(struct arg_list)); | |
455 (*list)->arg = name; | |
456 if ((name[strlen(name)-1] == '/') && strlen(name) != 1) | |
457 name[strlen(name)-1] = '\0'; | |
458 } | |
459 | |
460 static void add_from_file(struct arg_list **llist, struct arg_list *flist) | |
461 { | |
462 char *line = NULL; | |
463 | |
464 while (flist) { | |
465 int fd = 0; | |
466 | |
467 if (strcmp((char *)flist->arg, "-")) | |
468 fd = xopen((char *)flist->arg, O_RDONLY); | |
469 | |
470 while ((line = get_line(fd))) { | |
471 add_to_list(llist, line); | |
472 } | |
473 if (fd) close(fd); | |
474 flist = flist->next; | |
475 } | |
476 } | |
477 | |
478 static struct archive_handler *init_handler() | |
479 { | |
480 struct archive_handler *tar_hdl = xzalloc(sizeof(struct archive_handler)); | |
481 tar_hdl->extract_handler = extract_to_disk; | |
482 return tar_hdl; | |
483 } | |
484 | |
485 //convert octal to int | |
486 static int otoi(char *str, int len) | |
487 { | |
488 long val; | |
489 char *endp, inp[len+1]; //1 for NUL termination | |
490 | |
491 memcpy(inp, str, len); | |
492 inp[len] = '\0'; //nul-termination made sure | |
493 val = strtol(inp, &endp, 8); | |
494 if (*endp && *endp != ' ') error_exit("invalid param"); | |
495 return (int)val; | |
496 } | |
497 | |
498 static void extract_stream(struct archive_handler *tar_hdl) | |
499 { | |
500 int pipefd[2]; | |
501 pid_t cpid; | |
502 | |
503 if (pipe(pipefd) == -1) error_exit("pipe"); | |
504 | |
505 cpid = fork(); | |
506 if (cpid == -1) perror_exit("fork"); | |
507 | |
508 if (!cpid) { /* Child reads from pipe */ | |
509 char *argv[] = {"gunzip", "-cf", "-", NULL}; | |
510 xclose(pipefd[0]); /* Close unused read*/ | |
511 dup2(tar_hdl->src_fd, 0); | |
512 dup2(pipefd[1], 1); //write to pipe | |
513 xexec(argv); | |
514 } else { | |
515 xclose(pipefd[1]); /* Close unused read end */ | |
516 dup2(pipefd[0], tar_hdl->src_fd); //read from pipe | |
517 } | |
518 } | |
519 | |
520 static char *process_extended_hdr(struct archive_handler *tar, int size) | |
521 { | |
522 char *value = NULL, *p, *buf = xzalloc(size+1); | |
523 | |
524 if (readall(tar->src_fd, buf, size) != size) error_exit("short read"); | |
525 buf[size] = 0; | |
526 tar->offset += size; | |
527 p = buf; | |
528 | |
529 while (size) { | |
530 char *key; | |
531 int len, n; | |
532 | |
533 // extended records are of the format: "LEN NAME=VALUE\n" | |
534 sscanf(p, "%d %n", &len, &n); | |
535 key = p + n; | |
536 p += len; | |
537 size -= len; | |
538 p[-1] = 0; | |
539 if (size < 0) { | |
540 error_msg("corrupted extended header"); | |
541 break; | |
542 } | |
543 | |
544 len = strlen("path="); | |
545 if (!strncmp(key, "path=", len)) { | |
546 value = key + strlen("path="); | |
547 break; | |
548 } | |
549 } | |
550 return ((value)?xstrdup(value) : NULL); | |
551 } | |
552 | |
553 static void tar_skip(struct archive_handler *tar, int sz) | |
554 { | |
555 int x; | |
556 | |
557 while ((x = lskip(tar->src_fd, sz))) { | |
558 tar->offset += sz - x; | |
559 sz = x; | |
560 } | |
561 tar->offset += sz; | |
562 } | |
563 | |
564 static void unpack_tar(struct archive_handler *tar_hdl) | |
565 { | |
566 struct tar_hdr tar; | |
567 struct file_header *file_hdr; | |
568 int i, j, maj, min, sz, e = 0; | |
569 unsigned int cksum; | |
570 unsigned char *gzMagic; | |
571 char *longname = NULL, *longlink = NULL; | |
572 | |
573 while (1) { | |
574 cksum = 0; | |
575 if (tar_hdl->offset % 512) { | |
576 sz = 512 - tar_hdl->offset % 512; | |
577 tar_skip(tar_hdl, sz); | |
578 } | |
579 i = readall(tar_hdl->src_fd, &tar, 512); | |
580 tar_hdl->offset += i; | |
581 if (i != 512) { | |
582 if (i >= 2) goto CHECK_MAGIC; //may be a small (<512 byte)zipped file | |
583 error_exit("read error"); | |
584 } | |
585 | |
586 if (!tar.name[0]) { | |
587 if (e) return; //end of tar 2 empty blocks | |
588 e = 1;//empty jump to next block | |
589 continue; | |
590 } | |
591 if (strncmp(tar.magic, "ustar", 5)) { | |
592 //try detecting by reading magic | |
593 CHECK_MAGIC: | |
594 gzMagic = (unsigned char*)&tar; | |
595 if ((gzMagic[0] == 0x1f) && (gzMagic[1] == 0x8b) | |
596 && !lseek(tar_hdl->src_fd, -i, SEEK_CUR)) { | |
597 tar_hdl->offset -= i; | |
598 extract_stream(tar_hdl); | |
599 continue; | |
600 } | |
601 error_exit("invalid tar format"); | |
602 } | |
603 | |
604 for (j = 0; j<148; j++) cksum += (unsigned int)((char*)&tar)[j]; | |
605 for (j = 156; j<500; j++) cksum += (unsigned int)((char*)&tar)[j]; | |
606 //cksum field itself treated as ' ' | |
607 for ( j= 0; j<8; j++) cksum += (unsigned int)' '; | |
608 | |
609 if (cksum != otoi(tar.chksum, sizeof(tar.chksum))) error_exit("wrong cksum"); | |
610 | |
611 file_hdr = &tar_hdl->file_hdr; | |
612 memset(file_hdr, 0, sizeof(struct file_header)); | |
613 file_hdr->mode = otoi(tar.mode, sizeof(tar.mode)); | |
614 file_hdr->uid = otoi(tar.uid, sizeof(tar.uid)); | |
615 file_hdr->gid = otoi(tar.gid, sizeof(tar.gid)); | |
616 file_hdr->size = otoi(tar.size, sizeof(tar.size)); | |
617 file_hdr->mtime = otoi(tar.mtime, sizeof(tar.mtime)); | |
618 file_hdr->uname = xstrdup(tar.uname); | |
619 file_hdr->gname = xstrdup(tar.gname); | |
620 maj = otoi(tar.major, sizeof(tar.major)); | |
621 min = otoi(tar.minor, sizeof(tar.minor)); | |
622 file_hdr->device = makedev(maj, min); | |
623 | |
624 if (tar.type <= '7') { | |
625 if (tar.link[0]) { | |
626 sz = sizeof(tar.link); | |
627 file_hdr->link_target = xmalloc(sz + 1); | |
628 memcpy(file_hdr->link_target, tar.link, sz); | |
629 file_hdr->link_target[sz] = '\0'; | |
630 } | |
631 | |
632 file_hdr->name = xzalloc(256);// pathname supported size | |
633 if (tar.prefix[0]) { | |
634 memcpy(file_hdr->name, tar.prefix, sizeof(tar.prefix)); | |
635 sz = strlen(file_hdr->name); | |
636 if (file_hdr->name[sz-1] != '/') file_hdr->name[sz] = '/'; | |
637 } | |
638 sz = strlen(file_hdr->name); | |
639 memcpy(file_hdr->name + sz, tar.name, sizeof(tar.name)); | |
640 if (file_hdr->name[255]) error_exit("filename too long"); | |
641 } | |
642 | |
643 switch (tar.type) { | |
644 // case '\0': | |
645 case '0': | |
646 case '7': | |
647 case '1': //Hard Link | |
648 file_hdr->mode |= S_IFREG; | |
649 break; | |
650 case '2': | |
651 file_hdr->mode |= S_IFLNK; | |
652 break; | |
653 case '3': | |
654 file_hdr->mode |= S_IFCHR; | |
655 break; | |
656 case '4': | |
657 file_hdr->mode |= S_IFBLK; | |
658 break; | |
659 case '5': | |
660 file_hdr->mode |= S_IFDIR; | |
661 break; | |
662 case '6': | |
663 file_hdr->mode |= S_IFIFO; | |
664 break; | |
665 case 'K': | |
666 longlink = xzalloc(file_hdr->size +1); | |
667 xread(tar_hdl->src_fd, longlink, file_hdr->size); | |
668 tar_hdl->offset += file_hdr->size; | |
669 continue; | |
670 case 'L': | |
671 free(longname); | |
672 longname = xzalloc(file_hdr->size +1); | |
673 xread(tar_hdl->src_fd, longname, file_hdr->size); | |
674 tar_hdl->offset += file_hdr->size; | |
675 continue; | |
676 case 'D': | |
677 case 'M': | |
678 case 'N': | |
679 case 'S': | |
680 case 'V': | |
681 case 'g': // pax global header | |
682 tar_skip(tar_hdl, file_hdr->size); | |
683 continue; | |
684 case 'x': // pax extended header | |
685 free(longname); | |
686 longname = process_extended_hdr(tar_hdl, file_hdr->size); | |
687 continue; | |
688 default: break; | |
689 } | |
690 | |
691 if (longname) { | |
692 free(file_hdr->name); | |
693 file_hdr->name = longname; | |
694 longname = NULL; | |
695 } | |
696 if (longlink) { | |
697 free(file_hdr->link_target); | |
698 file_hdr->link_target = longlink; | |
699 longlink = NULL; | |
700 } | |
701 | |
702 if ((file_hdr->mode & S_IFREG) && | |
703 file_hdr->name[strlen(file_hdr->name)-1] == '/') { | |
704 file_hdr->name[strlen(file_hdr->name)-1] = '\0'; | |
705 file_hdr->mode &= ~S_IFREG; | |
706 file_hdr->mode |= S_IFDIR; | |
707 } | |
708 | |
709 if ((file_hdr->link_target && *(file_hdr->link_target)) | |
710 || S_ISLNK(file_hdr->mode) || S_ISDIR(file_hdr->mode)) | |
711 file_hdr->size = 0; | |
712 | |
713 if (filter(TT.exc, file_hdr->name) || | |
714 (TT.inc && !filter(TT.inc, file_hdr->name))) goto SKIP; | |
715 add_to_list(&TT.pass, xstrdup(file_hdr->name)); | |
716 | |
717 if (toys.optflags & FLAG_t) { | |
718 if (toys.optflags & FLAG_v) { | |
719 char perm[11]; | |
720 struct tm *lc = localtime((const time_t*)&(file_hdr->mtime)); | |
721 | |
722 mode_to_string(file_hdr->mode, perm); | |
723 printf("%s %s/%s %9ld %d-%02d-%02d %02d:%02d:%02d ",perm,file_hdr->uname, | |
724 file_hdr->gname, (long)file_hdr->size, 1900+lc->tm_year, | |
725 1+lc->tm_mon, lc->tm_mday, lc->tm_hour, lc->tm_min, lc->tm_sec); | |
726 } | |
727 printf("%s",file_hdr->name); | |
728 if (file_hdr->link_target) printf(" -> %s",file_hdr->link_target); | |
729 xputc('\n'); | |
730 SKIP: | |
731 tar_skip(tar_hdl, file_hdr->size); | |
732 } else { | |
733 if (toys.optflags & FLAG_v) printf("%s\n",file_hdr->name); | |
734 tar_hdl->extract_handler(tar_hdl); | |
735 } | |
736 free(file_hdr->name); | |
737 free(file_hdr->link_target); | |
738 free(file_hdr->uname); | |
739 free(file_hdr->gname); | |
740 } | |
741 } | |
742 | |
743 void tar_main(void) | |
744 { | |
745 struct archive_handler *tar_hdl; | |
746 int fd = 0, flags = O_RDONLY; | |
747 struct arg_list *tmp; | |
748 char **args = toys.optargs; | |
749 | |
750 if (!toys.argv[1]) { | |
751 toys.exithelp++; | |
752 error_exit(NULL); | |
753 } | |
754 | |
755 if (!geteuid()) toys.optflags |= FLAG_p; | |
756 | |
757 for (tmp = TT.exc; tmp; tmp = tmp->next) | |
758 tmp->arg = xstrdup(tmp->arg); //freeing at the end fails otherwise | |
759 | |
760 while(*args) add_to_list(&TT.inc, xstrdup(*args++)); | |
761 if (toys.optflags & FLAG_X) add_from_file(&TT.exc, TT.exc_file); | |
762 if (toys.optflags & FLAG_T) add_from_file(&TT.inc, TT.inc_file); | |
763 | |
764 if (toys.optflags & FLAG_c) { | |
765 if (!TT.inc) error_exit("empty archive"); | |
766 fd = 1, flags = O_WRONLY|O_CREAT|O_TRUNC; | |
767 } | |
768 if ((toys.optflags & FLAG_f) && strcmp(TT.fname, "-")) | |
769 fd = xcreate(TT.fname, flags, 0666); | |
770 if (toys.optflags & FLAG_C) xchdir(TT.dir); | |
771 | |
772 tar_hdl = init_handler(); | |
773 tar_hdl->src_fd = fd; | |
774 | |
775 if (toys.optflags & FLAG_x || toys.optflags & FLAG_t) { | |
776 if (toys.optflags & FLAG_O) tar_hdl->extract_handler = extract_to_stdout; | |
777 if (toys.optflags & FLAG_to_command) { | |
778 signal(SIGPIPE, SIG_IGN); //will be using pipe between child & parent | |
779 tar_hdl->extract_handler = extract_to_command; | |
780 } | |
781 if (toys.optflags & FLAG_z) extract_stream(tar_hdl); | |
782 unpack_tar(tar_hdl); | |
783 for (tmp = TT.inc; tmp; tmp = tmp->next) | |
784 if (!filter(TT.exc, tmp->arg) && !filter(TT.pass, tmp->arg)) | |
785 error_msg("'%s' not in archive", tmp->arg); | |
786 } else if (toys.optflags & FLAG_c) { | |
787 //create the tar here. | |
788 if (toys.optflags & FLAG_z) compress_stream(tar_hdl); | |
789 for (tmp = TT.inc; tmp; tmp = tmp->next) { | |
790 TT.handle = tar_hdl; | |
791 //recurse thru dir and add files to archive | |
792 struct dirtree *root = dirtree_add_node(0,tmp->arg,toys.optflags & FLAG_h); | |
793 | |
794 if (root) dirtree_handle_callback(root, add_to_tar); | |
795 } | |
796 memset(toybuf, 0, 1024); | |
797 writeall(tar_hdl->src_fd, toybuf, 1024); | |
798 seen_inode(&TT.inodes, 0, 0); | |
799 } | |
800 | |
801 if (CFG_TOYBOX_FREE) { | |
802 close(tar_hdl->src_fd); | |
803 free(tar_hdl); | |
804 llist_traverse(TT.exc, llist_free_arg); | |
805 llist_traverse(TT.inc, llist_free_arg); | |
806 llist_traverse(TT.pass, llist_free_arg); | |
807 } | |
808 } |