OSDN Git Service

"printf %x 64" wasn't working right on 32 bit because a variable type was wrong.
[android-x86/external-toybox.git] / toys / posix / cpio.c
1 /* cpio.c - a basic cpio
2  *
3  * Written 2013 AD by Isaac Dunham; this code is placed under the
4  * same license as toybox or as CC0, at your option.
5  *
6  * Portions Copyright 2015 by Frontier Silicon Ltd.
7  *
8  * http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/cpio.html
9  * and http://pubs.opengroup.org/onlinepubs/7908799/xcu/cpio.html
10  *
11  * Yes, that's SUSv2, the newer standards removed it around the time RPM
12  * and initramfs started heavily using this archive format.
13  *
14  * Modern cpio expanded header to 110 bytes (first field 6 bytes, rest are 8).
15  * In order: magic ino mode uid gid nlink mtime filesize devmajor devminor
16  * rdevmajor rdevminor namesize check
17  * This is the equiavlent of mode -H newc when using GNU CPIO.
18
19 USE_CPIO(NEWTOY(cpio, "(no-preserve-owner)mduH:p:|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN))
20
21 config CPIO
22   bool "cpio"
23   default y
24   help
25     usage: cpio -{o|t|i|p DEST} [-v] [--verbose] [-F FILE] [--no-preserve-owner]
26            [ignored: -mdu -H newc]
27
28     copy files into and out of a "newc" format cpio archive
29
30     -F FILE     use archive FILE instead of stdin/stdout
31     -p DEST     copy-pass mode, copy stdin file list to directory DEST
32     -i  extract from archive into file system (stdin=archive)
33     -o  create archive (stdin=list of files, stdout=archive)
34     -t  test files (list only, stdin=archive, stdout=list of files)
35     -v  verbose (list files during create/extract)
36     --no-preserve-owner (don't set ownership during extract)
37 */
38
39 #define FOR_cpio
40 #include "toys.h"
41
42 GLOBALS(
43   char *archive;
44   char *pass;
45   char *fmt;
46 )
47
48 // Read strings, tail padded to 4 byte alignment. Argument "align" is amount
49 // by which start of string isn't aligned (usually 0, but header is 110 bytes
50 // which is 2 bytes off because the first field wasn't expanded from 6 to 8).
51 static char *strpad(int fd, unsigned len, unsigned align)
52 {
53   char *str;
54
55   align = (align + len) & 3;
56   if (align) len += (4-align);
57   xreadall(fd, str = xmalloc(len+1), len);
58   str[len]=0; // redundant, in case archive is bad
59
60   return str;
61 }
62
63 //convert hex to uint; mostly to allow using bits of non-terminated strings
64 unsigned x8u(char *hex)
65 {
66   unsigned val, inpos = 8, outpos;
67   char pattern[6];
68
69   while (*hex == '0') {
70     hex++;
71     if (!--inpos) return 0;
72   }
73   // Because scanf gratuitously treats %*X differently than printf does.
74   sprintf(pattern, "%%%dX%%n", inpos);
75   sscanf(hex, pattern, &val, &outpos);
76   if (inpos != outpos) error_exit("bad header");
77
78   return val;
79 }
80
81 void cpio_main(void)
82 {
83   // Subtle bit: FLAG_o is 1 so we can just use it to select stdin/stdout.
84   int pipe, afd = toys.optflags & FLAG_o;
85   pid_t pid = 0;
86
87   // In passthrough mode, parent stays in original dir and generates archive
88   // to pipe, child does chdir to new dir and reads archive from stdin (pipe).
89   if (TT.pass) {
90     if (toys.stacktop) {
91       // xpopen() doesn't return from child due to vfork(), instead restarts
92       // with !toys.stacktop
93       pid = xpopen(0, &pipe, 0);
94       afd = pipe;
95     } else {
96       // child
97       toys.optflags |= FLAG_i;
98       xchdir(TT.pass);
99     }
100   }
101
102   if (TT.archive) {
103     int perm = (toys.optflags & FLAG_o) ? O_CREAT|O_WRONLY|O_TRUNC : O_RDONLY;
104
105     afd = xcreate(TT.archive, perm, 0644);
106   }
107
108   // read cpio archive
109
110   if (toys.optflags & (FLAG_i|FLAG_t)) for (;;) {
111     char *name, *tofree, *data;
112     unsigned size, mode, uid, gid, timestamp;
113     int test = toys.optflags & FLAG_t, err = 0;
114
115     // Read header and name.
116     xreadall(afd, toybuf, 110);
117     if (memcmp(toybuf, "070701", 6)) error_exit("bad cpio magic");
118     tofree = name = strpad(afd, x8u(toybuf+94), 110);
119     if (!strcmp("TRAILER!!!", name)) {
120       if (CFG_TOYBOX_FREE) free(tofree);
121       break;
122     }
123
124     // If you want to extract absolute paths, "cd /" and run cpio.
125     while (*name == '/') name++;
126     // TODO: remove .. entries
127
128     size = x8u(toybuf+54);
129     mode = x8u(toybuf+14);
130     uid = x8u(toybuf+22);
131     gid = x8u(toybuf+30);
132     timestamp = x8u(toybuf+46); // unsigned 32 bit, so year 2100 problem
133
134     if (toys.optflags & (FLAG_t|FLAG_v)) puts(name);
135
136     if (!test && strrchr(name, '/') && mkpathat(AT_FDCWD, name, 0, 2)) {
137       perror_msg("mkpath '%s'", name);
138       test++;
139     }
140
141     // Consume entire record even if it couldn't create file, so we're
142     // properly aligned with next file.
143
144     if (S_ISDIR(mode)) {
145       if (!test) err = mkdir(name, mode);
146     } else if (S_ISLNK(mode)) {
147       data = strpad(afd, size, 0);
148       if (!test) err = symlink(data, name);
149       free(data);
150       // Can't get a filehandle to a symlink, so do special chown
151       if (!err && !geteuid() && !(toys.optflags & FLAG_no_preserve_owner))
152         err = lchown(name, uid, gid);
153     } else if (S_ISREG(mode)) {
154       int fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, mode);
155
156       // If write fails, we still need to read/discard data to continue with
157       // archive. Since doing so overwrites errno, report error now
158       if (fd < 0) {
159         perror_msg("create %s", name);
160         test++;
161       }
162
163       data = toybuf;
164       while (size) {
165         if (size < sizeof(toybuf)) data = strpad(afd, size, 0);
166         else xreadall(afd, toybuf, sizeof(toybuf));
167         if (!test) xwrite(fd, data, data == toybuf ? sizeof(toybuf) : size);
168         if (data != toybuf) {
169           free(data);
170           break;
171         }
172         size -= sizeof(toybuf);
173       }
174
175       if (!test) {
176         // set owner, restore dropped suid bit
177         if (!geteuid() && !(toys.optflags & FLAG_no_preserve_owner)) {
178           err = fchown(fd, uid, gid);
179           if (!err) err = fchmod(fd, mode);
180         }
181         close(fd);
182       }
183     } else if (!test)
184       err = mknod(name, mode, dev_makedev(x8u(toybuf+78), x8u(toybuf+86)));
185
186     // Set ownership and timestamp.
187     if (!test && !err) {
188       // Creading dir/dev doesn't give us a filehandle, we have to refer to it
189       // by name to chown/utime, but how do we know it's the same item?
190       // Check that we at least have the right type of entity open, and do
191       // NOT restore dropped suid bit in this case.
192       if (!S_ISREG(mode) && !S_ISLNK(mode) && !geteuid()
193           && !(toys.optflags & FLAG_no_preserve_owner))
194       {
195         int fd = open(name, O_RDONLY|O_NOFOLLOW);
196         struct stat st;
197
198         if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT))
199           err = fchown(fd, uid, gid);
200         else err = 1;
201
202         close(fd);
203       }
204
205       // set timestamp
206       if (!err) {
207         struct timespec times[2];
208
209         memset(times, 0, sizeof(struct timespec)*2);
210         times[0].tv_sec = times[1].tv_sec = timestamp;
211         err = utimensat(AT_FDCWD, name, times, AT_SYMLINK_NOFOLLOW);
212       }
213     }
214
215     if (err) perror_msg_raw(name);
216     free(tofree);
217
218   // Output cpio archive
219
220   } else {
221     char *name = 0;
222     size_t size = 0;
223
224     for (;;) {
225       struct stat st;
226       unsigned nlen, error = 0, zero = 0;
227       int len, fd = -1;
228       ssize_t llen;
229
230       len = getline(&name, &size, stdin);
231       if (len<1) break;
232       if (name[len-1] == '\n') name[--len] = 0;
233       nlen = len+1;
234       if (lstat(name, &st) || (S_ISREG(st.st_mode)
235           && st.st_size && (fd = open(name, O_RDONLY))<0))
236       {
237         perror_msg_raw(name);
238         continue;
239       }
240
241       if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) st.st_size = 0;
242       if (st.st_size >> 32) perror_msg("skipping >2G file '%s'", name);
243       else {
244         llen = sprintf(toybuf,
245           "070701%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X",
246           (int)st.st_ino, st.st_mode, st.st_uid, st.st_gid, (int)st.st_nlink,
247           (int)st.st_mtime, (int)st.st_size, dev_major(st.st_dev),
248           dev_minor(st.st_dev), dev_major(st.st_rdev), dev_minor(st.st_rdev),
249           nlen, 0);
250         xwrite(afd, toybuf, llen);
251         xwrite(afd, name, nlen);
252
253         // NUL Pad header up to 4 multiple bytes.
254         llen = (llen + nlen) & 3;
255         if (llen) xwrite(afd, &zero, 4-llen);
256
257         // Write out body for symlink or regular file
258         llen = st.st_size;
259         if (S_ISLNK(st.st_mode)) {
260           if (readlink(name, toybuf, sizeof(toybuf)-1) == llen)
261             xwrite(afd, toybuf, llen);
262           else perror_msg("readlink '%s'", name);
263         } else while (llen) {
264           nlen = llen > sizeof(toybuf) ? sizeof(toybuf) : llen;
265           llen -= nlen;
266           // If read fails, write anyway (already wrote size in header)
267           if (nlen != readall(fd, toybuf, nlen))
268             if (!error++) perror_msg("bad read from file '%s'", name);
269           xwrite(afd, toybuf, nlen);
270         }
271         llen = st.st_size & 3;
272         if (llen) xwrite(afd, &zero, 4-llen);
273       }
274       close(fd);
275     }
276     free(name);
277
278     memset(toybuf, 0, sizeof(toybuf));
279     xwrite(afd, toybuf,
280       sprintf(toybuf, "070701%040X%056X%08XTRAILER!!!", 1, 0x0b, 0)+4);
281   }
282   if (TT.archive) xclose(afd);
283
284   if (TT.pass) toys.exitval |= xpclose(pid, pipe);
285 }