blob: b447b27e40d0adfc756d0bbf62b5820d710aa60c [file] [log] [blame]
Glenn L McGrath8f5b63e2001-06-22 09:22:06 +00001/* vi: set sw=4 ts=4: */
2/*
3 * Mini cpio implementation for busybox
4 *
Eric Andersenc7bda1c2004-03-15 08:29:22 +00005 * Copyright (C) 2001 by Glenn McGrath
Glenn L McGrath8f5b63e2001-06-22 09:22:06 +00006 *
"Robert P. J. Day"801ab142006-07-12 07:56:04 +00007 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Glenn L McGrath8f5b63e2001-06-22 09:22:06 +00008 *
9 * Limitations:
Denis Vlasenko261f2372008-04-05 00:07:46 +000010 * Doesn't check CRC's
11 * Only supports new ASCII and CRC formats
Glenn L McGrath8f5b63e2001-06-22 09:22:06 +000012 *
13 */
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000014#include "libbb.h"
Denis Vlasenko9a7d38f2007-05-31 22:42:12 +000015#include "unarchive.h"
Glenn L McGrath8f5b63e2001-06-22 09:22:06 +000016
Denis Vlasenko261f2372008-04-05 00:07:46 +000017#if ENABLE_FEATURE_CPIO_O
18static off_t cpio_pad4(off_t size)
19{
20 int i;
21
22 i = (- size) & 3;
23 size += i;
24 while (--i >= 0)
25 bb_putchar('\0');
26 return size;
27}
28
29/* Return value will become exit code.
30 * It's ok to exit instead of return. */
31static int cpio_o(void)
32{
33 struct name_s {
34 struct name_s *next;
35 char name[1];
36 };
37 struct inodes_s {
38 struct inodes_s *next;
39 struct name_s *names;
40 struct stat st;
41 };
42
43 struct inodes_s *links = NULL;
44 off_t bytes = 0; /* output bytes count */
45
46 while (1) {
47 const char *name;
48 char *line;
49 struct stat st;
50
51 line = xmalloc_fgetline(stdin);
52
53 if (line) {
54 /* Strip leading "./[./]..." from the filename */
55 name = line;
56 while (name[0] == '.' && name[1] == '/') {
57 while (*++name == '/')
58 continue;
59 }
60 if (!*name) { /* line is empty */
61 free(line);
62 continue;
63 }
64 if (lstat(name, &st)) {
65 abort_cpio_o:
66 bb_simple_perror_msg_and_die(name);
67 }
68
69 if (!(S_ISLNK(st.st_mode) || S_ISREG(st.st_mode)))
70 st.st_size = 0; /* paranoia */
71
72 /* Store hardlinks for later processing, dont output them */
73 if (!S_ISDIR(st.st_mode) && st.st_nlink > 1) {
74 struct name_s *n;
75 struct inodes_s *l;
76
77 /* Do we have this hardlink remembered? */
78 l = links;
79 while (1) {
80 if (l == NULL) {
81 /* Not found: add new item to "links" list */
82 l = xzalloc(sizeof(*l));
83 l->st = st;
84 l->next = links;
85 links = l;
86 break;
87 }
88 if (l->st.st_ino == st.st_ino) {
89 /* found */
90 break;
91 }
92 l = l->next;
93 }
94 /* Add new name to "l->names" list */
95 n = xmalloc(sizeof(*n) + strlen(name));
96 strcpy(n->name, name);
97 n->next = l->names;
98 l->names = n;
99
100 free(line);
101 continue;
102 }
103
104 } else { /* line == NULL: EOF */
105 next_link:
106 if (links) {
107 /* Output hardlink's data */
108 st = links->st;
109 name = links->names->name;
110 links->names = links->names->next;
111 /* GNU cpio is reported to emit file data
112 * only for the last instance. Mimic that. */
113 if (links->names == NULL)
114 links = links->next;
115 else
116 st.st_size = 0;
117 /* NB: we leak links->names and/or links,
118 * this is intended (we exit soon anyway) */
119 } else {
120 /* If no (more) hardlinks to output,
121 * output "trailer" entry */
122 name = "TRAILER!!!";
123 /* st.st_size == 0 is a must, but for uniformity
124 * in the output, we zero out everything */
125 memset(&st, 0, sizeof(st));
126 /* st.st_nlink = 1; - GNU cpio does this */
127 }
128 }
129
130 bytes += printf("070701"
131 "%08X%08X%08X%08X%08X%08X%08X"
132 "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */
133 /* strlen+1: */ "%08X"
134 /* chksum: */ "00000000" /* (only for "070702" files) */
135 /* name,NUL: */ "%s%c",
136 (unsigned)(uint32_t) st.st_ino,
137 (unsigned)(uint32_t) st.st_mode,
138 (unsigned)(uint32_t) st.st_uid,
139 (unsigned)(uint32_t) st.st_gid,
140 (unsigned)(uint32_t) st.st_nlink,
141 (unsigned)(uint32_t) st.st_mtime,
142 (unsigned)(uint32_t) st.st_size,
143 (unsigned)(uint32_t) major(st.st_dev),
144 (unsigned)(uint32_t) minor(st.st_dev),
145 (unsigned)(uint32_t) major(st.st_rdev),
146 (unsigned)(uint32_t) minor(st.st_rdev),
147 (unsigned)(strlen(name) + 1),
148 name, '\0');
149 bytes = cpio_pad4(bytes);
150
151 if (st.st_size) {
152 if (S_ISLNK(st.st_mode)) {
153 char *lpath = xmalloc_readlink_or_warn(name);
154 if (!lpath)
155 goto abort_cpio_o;
156 bytes += printf("%s", lpath);
157 free(lpath);
158 } else { /* S_ISREG */
159 int fd = xopen(name, O_RDONLY);
160 fflush(stdout);
161 /* We must abort if file got shorter too! */
Denis Vlasenko1af00ed2008-04-05 02:44:30 +0000162 bb_copyfd_exact_size(fd, STDOUT_FILENO, st.st_size);
Denis Vlasenko261f2372008-04-05 00:07:46 +0000163 bytes += st.st_size;
164 close(fd);
165 }
166 bytes = cpio_pad4(bytes);
167 }
168
169 if (!line) {
170 if (links)
171 goto next_link;
172 /* TODO: GNU cpio pads trailer to 512 bytes, do we want that? */
173 return EXIT_SUCCESS;
174 }
175
176 free(line);
177 } /* end of "while (1)" */
178}
179#endif
Glenn L McGrath10b78132004-02-25 09:30:06 +0000180
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000181/* GNU cpio 2.9 --help (abridged):
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000182
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000183 Modes:
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000184 -i, --extract Extract files from an archive
185 -o, --create Create the archive
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000186 -p, --pass-through Copy-pass mode [was ist das?!]
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000187 -t, --list List the archive
188
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000189 Options valid in any mode:
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000190 --block-size=SIZE I/O block size = SIZE * 512 bytes
191 -B I/O block size = 5120 bytes
192 -c Use the old portable (ASCII) archive format
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000193 -C, --io-size=NUMBER I/O block size in bytes
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000194 -f, --nonmatching Only copy files that do not match given pattern
195 -F, --file=FILE Use FILE instead of standard input or output
196 -H, --format=FORMAT Use given archive FORMAT
197 -M, --message=STRING Print STRING when the end of a volume of the
198 backup media is reached
199 -n, --numeric-uid-gid If -v, show numeric UID and GID
200 --quiet Do not print the number of blocks copied
201 --rsh-command=COMMAND Use remote COMMAND instead of rsh
202 -v, --verbose Verbosely list the files processed
203 -V, --dot Print a "." for each file processed
204 -W, --warning=FLAG Control warning display: 'none','truncate','all';
205 multiple options accumulate
206
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000207 Options valid only in --extract mode:
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000208 -b, --swap Swap both halfwords of words and bytes of
209 halfwords in the data (equivalent to -sS)
210 -r, --rename Interactively rename files
211 -s, --swap-bytes Swap the bytes of each halfword in the files
212 -S, --swap-halfwords Swap the halfwords of each word (4 bytes)
213 --to-stdout Extract files to standard output
214 -E, --pattern-file=FILE Read additional patterns specifying filenames to
215 extract or list from FILE
216 --only-verify-crc Verify CRC's, don't actually extract the files
217
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000218 Options valid only in --create mode:
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000219 -A, --append Append to an existing archive
220 -O FILE File to use instead of standard output
221
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000222 Options valid only in --pass-through mode:
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000223 -l, --link Link files instead of copying them, when possible
224
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000225 Options valid in --extract and --create modes:
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000226 --absolute-filenames Do not strip file system prefix components from
227 the file names
228 --no-absolute-filenames Create all files relative to the current dir
229
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000230 Options valid in --create and --pass-through modes:
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000231 -0, --null A list of filenames is terminated by a NUL
232 -a, --reset-access-time Reset the access times of files after reading them
233 -I FILE File to use instead of standard input
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000234 -L, --dereference Dereference symbolic links (copy the files
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000235 that they point to instead of copying the links)
236 -R, --owner=[USER][:.][GROUP] Set owner of created files
237
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000238 Options valid in --extract and --pass-through modes:
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000239 -d, --make-directories Create leading directories where needed
Denis Vlasenkoee1b3b12008-07-11 22:20:59 +0000240 -m, --preserve-modification-time Retain mtime when creating files
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000241 --no-preserve-owner Do not change the ownership of the files
242 --sparse Write files with blocks of zeros as sparse files
243 -u, --unconditional Replace all files unconditionally
244 */
245
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000246int cpio_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000247int cpio_main(int argc UNUSED_PARAM, char **argv)
Glenn L McGrath8f5b63e2001-06-22 09:22:06 +0000248{
Glenn L McGrath7ca04f32002-09-25 02:47:48 +0000249 archive_handle_t *archive_handle;
Denis Vlasenko261f2372008-04-05 00:07:46 +0000250 char *cpio_filename;
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000251 USE_FEATURE_CPIO_O(const char *cpio_fmt = "";)
Denis Vlasenko67b23e62006-10-03 21:00:06 +0000252 unsigned opt;
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000253 enum {
254 CPIO_OPT_EXTRACT = (1 << 0),
255 CPIO_OPT_TEST = (1 << 1),
256 CPIO_OPT_UNCONDITIONAL = (1 << 2),
257 CPIO_OPT_VERBOSE = (1 << 3),
258 CPIO_OPT_FILE = (1 << 4),
259 CPIO_OPT_CREATE_LEADING_DIR = (1 << 5),
260 CPIO_OPT_PRESERVE_MTIME = (1 << 6),
261 CPIO_OPT_CREATE = (1 << 7),
262 CPIO_OPT_FORMAT = (1 << 8),
263 };
264
Denis Vlasenko4d0cd282008-07-16 21:51:43 +0000265#if ENABLE_GETOPT_LONG
Denis Vlasenko2b407b12008-07-11 21:42:12 +0000266 applet_long_options =
267 "extract\0" No_argument "i"
268 "list\0" No_argument "t"
269#if ENABLE_FEATURE_CPIO_O
270 "create\0" No_argument "o"
271 "format\0" Required_argument "H"
272#endif
273 ;
274#endif
Glenn L McGrath7ca04f32002-09-25 02:47:48 +0000275
Denis Vlasenko261f2372008-04-05 00:07:46 +0000276 /* Initialize */
Glenn L McGrath7ca04f32002-09-25 02:47:48 +0000277 archive_handle = init_handle();
Eric Andersen70060d22004-03-27 10:02:48 +0000278 archive_handle->src_fd = STDIN_FILENO;
Denis Vlasenko13858992006-10-08 12:49:22 +0000279 archive_handle->seek = seek_by_read;
Denis Vlasenkoa60936d2008-06-28 05:04:09 +0000280 archive_handle->ah_flags = ARCHIVE_EXTRACT_NEWER;
Glenn L McGrath10b78132004-02-25 09:30:06 +0000281
Denis Vlasenko261f2372008-04-05 00:07:46 +0000282#if ENABLE_FEATURE_CPIO_O
283 opt = getopt32(argv, "ituvF:dmoH:", &cpio_filename, &cpio_fmt);
284
285 if (opt & CPIO_OPT_CREATE) {
286 if (*cpio_fmt != 'n')
287 bb_show_usage();
288 if (opt & CPIO_OPT_FILE) {
289 fclose(stdout);
Denis Vlasenko5415c852008-07-21 23:05:26 +0000290 stdout = fopen_for_write(cpio_filename);
Denis Vlasenko261f2372008-04-05 00:07:46 +0000291 /* Paranoia: I don't trust libc that much */
292 xdup2(fileno(stdout), STDOUT_FILENO);
293 }
294 return cpio_o();
295 }
296#else
Denis Vlasenkofe7cd642007-08-18 15:32:12 +0000297 opt = getopt32(argv, "ituvF:dm", &cpio_filename);
Denis Vlasenko261f2372008-04-05 00:07:46 +0000298#endif
299 argv += optind;
Glenn L McGrath10b78132004-02-25 09:30:06 +0000300
301 /* One of either extract or test options must be given */
302 if ((opt & (CPIO_OPT_TEST | CPIO_OPT_EXTRACT)) == 0) {
303 bb_show_usage();
304 }
305
306 if (opt & CPIO_OPT_TEST) {
Rob Landleyb7128c62005-09-11 01:05:30 +0000307 /* if both extract and test options are given, ignore extract option */
Glenn L McGrath10b78132004-02-25 09:30:06 +0000308 if (opt & CPIO_OPT_EXTRACT) {
309 opt &= ~CPIO_OPT_EXTRACT;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000310 }
Glenn L McGrath10b78132004-02-25 09:30:06 +0000311 archive_handle->action_header = header_list;
312 }
313 if (opt & CPIO_OPT_EXTRACT) {
314 archive_handle->action_data = data_extract_all;
315 }
316 if (opt & CPIO_OPT_UNCONDITIONAL) {
Denis Vlasenkoa60936d2008-06-28 05:04:09 +0000317 archive_handle->ah_flags |= ARCHIVE_EXTRACT_UNCONDITIONAL;
318 archive_handle->ah_flags &= ~ARCHIVE_EXTRACT_NEWER;
Glenn L McGrath10b78132004-02-25 09:30:06 +0000319 }
320 if (opt & CPIO_OPT_VERBOSE) {
321 if (archive_handle->action_header == header_list) {
322 archive_handle->action_header = header_verbose_list;
323 } else {
324 archive_handle->action_header = header_list;
Glenn L McGrath8f5b63e2001-06-22 09:22:06 +0000325 }
326 }
Denis Vlasenko261f2372008-04-05 00:07:46 +0000327 if (opt & CPIO_OPT_FILE) { /* -F */
Rob Landley86b4d642006-08-03 17:58:17 +0000328 archive_handle->src_fd = xopen(cpio_filename, O_RDONLY);
Glenn L McGrath10b78132004-02-25 09:30:06 +0000329 archive_handle->seek = seek_by_jump;
330 }
331 if (opt & CPIO_OPT_CREATE_LEADING_DIR) {
Denis Vlasenkoa60936d2008-06-28 05:04:09 +0000332 archive_handle->ah_flags |= ARCHIVE_CREATE_LEADING_DIRS;
Glenn L McGrath10b78132004-02-25 09:30:06 +0000333 }
Denis Vlasenkobbd55c92008-06-27 15:52:07 +0000334 if (opt & CPIO_OPT_PRESERVE_MTIME) {
Denis Vlasenkoa60936d2008-06-28 05:04:09 +0000335 archive_handle->ah_flags |= ARCHIVE_PRESERVE_DATE;
Denis Vlasenkobbd55c92008-06-27 15:52:07 +0000336 }
Glenn L McGrath8f5b63e2001-06-22 09:22:06 +0000337
Denis Vlasenko261f2372008-04-05 00:07:46 +0000338 while (*argv) {
Glenn L McGrath7ca04f32002-09-25 02:47:48 +0000339 archive_handle->filter = filter_accept_list;
Denis Vlasenko261f2372008-04-05 00:07:46 +0000340 llist_add_to(&(archive_handle->accept), *argv);
341 argv++;
Glenn L McGrath8f5b63e2001-06-22 09:22:06 +0000342 }
343
Denis Vlasenkocf4dd072008-10-17 14:11:04 +0000344 /* see get_header_cpio */
345 archive_handle->ah_priv[2] = (void*) ~(ptrdiff_t)0;
Denis Vlasenko261f2372008-04-05 00:07:46 +0000346 while (get_header_cpio(archive_handle) == EXIT_SUCCESS)
347 continue;
Glenn L McGrath7ca04f32002-09-25 02:47:48 +0000348
Denis Vlasenkocf4dd072008-10-17 14:11:04 +0000349 if (archive_handle->ah_priv[2] != (void*) ~(ptrdiff_t)0)
350 printf("%lu blocks\n", (unsigned long)(ptrdiff_t)(archive_handle->ah_priv[2]));
Denis Vlasenkod83676e2008-10-17 14:03:56 +0000351
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000352 return EXIT_SUCCESS;
Glenn L McGrath8f5b63e2001-06-22 09:22:06 +0000353}