Contents of /trunk/mkinitrd-magellan/klibc/usr/utils/cpio.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 32979 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 32979 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | /* copyin.c - extract or list a cpio archive |
2 | Copyright (C) 1990,1991,1992,2001,2002,2003,2004 Free Software Foundation, Inc. |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; either version 2, or (at your option) |
7 | any later version. |
8 | |
9 | This program is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License along |
15 | with this program; if not, write to the Free Software Foundation, Inc., |
16 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
17 | |
18 | #include <errno.h> |
19 | #include <fcntl.h> |
20 | #include <malloc.h> |
21 | #include <stdbool.h> |
22 | #include <stdio.h> |
23 | #include <stdlib.h> |
24 | #include <string.h> |
25 | #include <sys/types.h> |
26 | #include <sys/stat.h> |
27 | #include <time.h> |
28 | #include <unistd.h> |
29 | #include <utime.h> |
30 | #ifndef FNM_PATHNAME |
31 | #include <fnmatch.h> |
32 | #endif |
33 | |
34 | #ifndef O_BINARY |
35 | # define O_BINARY 0 |
36 | #endif |
37 | |
38 | # ifndef DIRECTORY_SEPARATOR |
39 | # define DIRECTORY_SEPARATOR '/' |
40 | # endif |
41 | |
42 | # ifndef ISSLASH |
43 | # define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR) |
44 | # endif |
45 | |
46 | # ifndef FILE_SYSTEM_PREFIX_LEN |
47 | # define FILE_SYSTEM_PREFIX_LEN(Filename) 0 |
48 | # endif |
49 | |
50 | #ifndef SYMLINK_USES_UMASK |
51 | # define UMASKED_SYMLINK(name1,name2,mode) symlink(name1,name2) |
52 | #else |
53 | # define UMASKED_SYMLINK(name1,name2,mode) umasked_symlink(name1,name2,mode) |
54 | #endif /* SYMLINK_USES_UMASK */ |
55 | |
56 | /* Return 1 if an array of N objects, each of size S, cannot exist due |
57 | to size arithmetic overflow. S must be positive and N must be |
58 | nonnegative. This is a macro, not an inline function, so that it |
59 | works correctly even when SIZE_MAX < N. |
60 | |
61 | By gnulib convention, SIZE_MAX represents overflow in size |
62 | calculations, so the conservative dividend to use here is |
63 | SIZE_MAX - 1, since SIZE_MAX might represent an overflowed value. |
64 | However, malloc (SIZE_MAX) fails on all known hosts where |
65 | sizeof (ptrdiff_t) <= sizeof (size_t), so do not bother to test for |
66 | exactly-SIZE_MAX allocations on such hosts; this avoids a test and |
67 | branch when S is known to be 1. */ |
68 | # define xalloc_oversized(n, s) \ |
69 | ((size_t) (sizeof (ptrdiff_t) <= sizeof (size_t) ? -1 : -2) / (s) < (n)) |
70 | |
71 | #define DISK_IO_BLOCK_SIZE (512) |
72 | |
73 | char *progname = NULL; |
74 | |
75 | /* If true, print a . for each file processed. (-V) */ |
76 | char dot_flag = false; |
77 | |
78 | /* Input and output buffers. */ |
79 | char *input_buffer, *output_buffer; |
80 | |
81 | /* The size of the input buffer. */ |
82 | long input_buffer_size; |
83 | |
84 | /* Current locations in `input_buffer' and `output_buffer'. */ |
85 | char *in_buff, *out_buff; |
86 | |
87 | /* Current number of bytes stored at `input_buff' and `output_buff'. */ |
88 | long input_size, output_size; |
89 | |
90 | /* Block size value, initially 512. -B sets to 5120. */ |
91 | int io_block_size = 512; |
92 | |
93 | struct new_cpio_header { |
94 | unsigned short c_magic; |
95 | unsigned long c_ino; |
96 | unsigned long c_mode; |
97 | unsigned long c_uid; |
98 | unsigned long c_gid; |
99 | unsigned long c_nlink; |
100 | unsigned long c_mtime; |
101 | unsigned long c_filesize; |
102 | long c_dev_maj; |
103 | long c_dev_min; |
104 | long c_rdev_maj; |
105 | long c_rdev_min; |
106 | unsigned long c_namesize; |
107 | unsigned long c_chksum; |
108 | char *c_name; |
109 | char *c_tar_linkname; |
110 | }; |
111 | |
112 | /* Total number of bytes read and written for all files. |
113 | Now that many tape drives hold more than 4Gb we need more than 32 |
114 | bits to hold input_bytes and output_bytes. But it's not worth |
115 | the trouble of adding special multi-precision arithmetic if the |
116 | compiler doesn't support 64 bit ints since input_bytes and |
117 | output_bytes are only used to print the number of blocks copied. */ |
118 | #ifdef __GNUC__ |
119 | long long input_bytes, output_bytes; |
120 | #else |
121 | long input_bytes, output_bytes; |
122 | #endif |
123 | |
124 | /* Allocate N bytes of memory dynamically, with error checking. */ |
125 | |
126 | void *xmalloc(size_t n) |
127 | { |
128 | void *p; |
129 | if (xalloc_oversized(n, 1) || (!(p = malloc(n)) && n != 0)) { |
130 | fprintf(stderr, "%s: memory exhausted\n", progname); |
131 | exit(1); |
132 | } |
133 | return p; |
134 | /* return xnmalloc_inline (n, 1); */ |
135 | } |
136 | |
137 | /* Change the size of an allocated block of memory P to N bytes, |
138 | with error checking. */ |
139 | |
140 | void *xrealloc(void *p, size_t n) |
141 | { |
142 | if (xalloc_oversized(n, 1) || (!(p = realloc(p, n)) && n != 0)) { |
143 | fprintf(stderr, "%s: memory exhausted\n", progname); |
144 | exit(1); |
145 | } |
146 | return p; |
147 | /* return xnrealloc_inline (p, n, 1); */ |
148 | } |
149 | |
150 | /* Clone STRING. */ |
151 | |
152 | char *xstrdup(char const *string) |
153 | { |
154 | size_t s = strlen(string) + 1; |
155 | return memcpy(xmalloc(s), string, s); |
156 | /* return xmemdup_inline (string, strlen (string) + 1); */ |
157 | } |
158 | |
159 | /* Copy NUM_BYTES of buffer `in_buff' into IN_BUF. |
160 | `in_buff' may be partly full. |
161 | When `in_buff' is exhausted, refill it from file descriptor IN_DES. */ |
162 | |
163 | static void tape_fill_input_buffer(int in_des, int num_bytes) |
164 | { |
165 | in_buff = input_buffer; |
166 | num_bytes = (num_bytes < io_block_size) ? num_bytes : io_block_size; |
167 | input_size = read(in_des, input_buffer, num_bytes); |
168 | if (input_size < 0) { |
169 | fprintf(stderr, "%s: read error: %s\n", progname, |
170 | strerror(errno)); |
171 | exit(1); |
172 | } |
173 | if (input_size == 0) { |
174 | fprintf(stderr, "%s: premature end of file\n", progname); |
175 | exit(1); |
176 | } |
177 | input_bytes += input_size; |
178 | } |
179 | |
180 | /* Write `output_size' bytes of `output_buffer' to file |
181 | descriptor OUT_DES and reset `output_size' and `out_buff'. |
182 | If `swapping_halfwords' or `swapping_bytes' is set, |
183 | do the appropriate swapping first. Our callers have |
184 | to make sure to only set these flags if `output_size' |
185 | is appropriate (a multiple of 4 for `swapping_halfwords', |
186 | 2 for `swapping_bytes'). The fact that DISK_IO_BLOCK_SIZE |
187 | must always be a multiple of 4 helps us (and our callers) |
188 | insure this. */ |
189 | |
190 | void disk_empty_output_buffer(int out_des) |
191 | { |
192 | int bytes_written; |
193 | |
194 | bytes_written = write(out_des, output_buffer, output_size); |
195 | |
196 | if (bytes_written != output_size) { |
197 | fprintf(stderr, "%s: write error: %s\n", |
198 | progname, strerror(errno)); |
199 | exit(1); |
200 | } |
201 | output_bytes += output_size; |
202 | out_buff = output_buffer; |
203 | output_size = 0; |
204 | } |
205 | |
206 | /* Copy NUM_BYTES of buffer IN_BUF to `out_buff', which may be partly full. |
207 | When `out_buff' fills up, flush it to file descriptor OUT_DES. */ |
208 | |
209 | void disk_buffered_write(char *in_buf, int out_des, long num_bytes) |
210 | { |
211 | register long bytes_left = num_bytes; /* Bytes needing to be copied. */ |
212 | register long space_left; /* Room left in output buffer. */ |
213 | |
214 | while (bytes_left > 0) { |
215 | space_left = DISK_IO_BLOCK_SIZE - output_size; |
216 | if (space_left == 0) |
217 | disk_empty_output_buffer(out_des); |
218 | else { |
219 | if (bytes_left < space_left) |
220 | space_left = bytes_left; |
221 | memmove(out_buff, in_buf, (unsigned)space_left); |
222 | out_buff += space_left; |
223 | output_size += space_left; |
224 | in_buf += space_left; |
225 | bytes_left -= space_left; |
226 | } |
227 | } |
228 | } |
229 | |
230 | /* Copy a file using the input and output buffers, which may start out |
231 | partly full. After the copy, the files are not closed nor the last |
232 | block flushed to output, and the input buffer may still be partly |
233 | full. If `crc_i_flag' is set, add each byte to `crc'. |
234 | IN_DES is the file descriptor for input; |
235 | OUT_DES is the file descriptor for output; |
236 | NUM_BYTES is the number of bytes to copy. */ |
237 | |
238 | void copy_files_tape_to_disk(int in_des, int out_des, long num_bytes) |
239 | { |
240 | long size; |
241 | |
242 | while (num_bytes > 0) { |
243 | if (input_size == 0) |
244 | tape_fill_input_buffer(in_des, io_block_size); |
245 | size = (input_size < num_bytes) ? input_size : num_bytes; |
246 | disk_buffered_write(in_buff, out_des, size); |
247 | num_bytes -= size; |
248 | input_size -= size; |
249 | in_buff += size; |
250 | } |
251 | } |
252 | |
253 | /* if IN_BUF is NULL, Skip the next NUM_BYTES bytes of file descriptor IN_DES. */ |
254 | void tape_buffered_read(char *in_buf, int in_des, long num_bytes) |
255 | { |
256 | register long bytes_left = num_bytes; /* Bytes needing to be copied. */ |
257 | register long space_left; /* Bytes to copy from input buffer. */ |
258 | |
259 | while (bytes_left > 0) { |
260 | if (input_size == 0) |
261 | tape_fill_input_buffer(in_des, io_block_size); |
262 | if (bytes_left < input_size) |
263 | space_left = bytes_left; |
264 | else |
265 | space_left = input_size; |
266 | if (in_buf != NULL) { |
267 | memmove(in_buf, in_buff, (unsigned)space_left); |
268 | in_buf += space_left; |
269 | } |
270 | in_buff += space_left; |
271 | input_size -= space_left; |
272 | bytes_left -= space_left; |
273 | } |
274 | } |
275 | |
276 | /* Skip the next NUM_BYTES bytes of file descriptor IN_DES. */ |
277 | #define tape_toss_input(in_des,num_bytes) \ |
278 | (tape_buffered_read(NULL,(in_des),(num_bytes))) |
279 | |
280 | struct deferment { |
281 | struct deferment *next; |
282 | struct new_cpio_header header; |
283 | }; |
284 | |
285 | struct deferment *create_deferment(struct new_cpio_header *file_hdr) |
286 | { |
287 | struct deferment *d; |
288 | d = (struct deferment *)xmalloc(sizeof(struct deferment)); |
289 | d->header = *file_hdr; |
290 | d->header.c_name = (char *)xmalloc(strlen(file_hdr->c_name) + 1); |
291 | strcpy(d->header.c_name, file_hdr->c_name); |
292 | return d; |
293 | } |
294 | |
295 | void free_deferment(struct deferment *d) |
296 | { |
297 | free(d->header.c_name); |
298 | free(d); |
299 | } |
300 | |
301 | int link_to_name(char *link_name, char *link_target) |
302 | { |
303 | int res = link(link_target, link_name); |
304 | return res; |
305 | } |
306 | |
307 | struct inode_val { |
308 | unsigned long inode; |
309 | unsigned long major_num; |
310 | unsigned long minor_num; |
311 | char *file_name; |
312 | }; |
313 | |
314 | /* Inode hash table. Allocated by first call to add_inode. */ |
315 | static struct inode_val **hash_table = NULL; |
316 | |
317 | /* Size of current hash table. Initial size is 47. (47 = 2*22 + 3) */ |
318 | static int hash_size = 22; |
319 | |
320 | /* Number of elements in current hash table. */ |
321 | static int hash_num; |
322 | |
323 | /* Do the hash insert. Used in normal inserts and resizing the hash |
324 | table. It is guaranteed that there is room to insert the item. |
325 | NEW_VALUE is the pointer to the previously allocated inode, file |
326 | name association record. */ |
327 | |
328 | static void hash_insert(struct inode_val *new_value) |
329 | { |
330 | int start; /* Home position for the value. */ |
331 | int temp; /* Used for rehashing. */ |
332 | |
333 | /* Hash function is node number modulo the table size. */ |
334 | start = new_value->inode % hash_size; |
335 | |
336 | /* Do the initial look into the table. */ |
337 | if (hash_table[start] == NULL) { |
338 | hash_table[start] = new_value; |
339 | return; |
340 | } |
341 | |
342 | /* If we get to here, the home position is full with a different inode |
343 | record. Do a linear search for the first NULL pointer and insert |
344 | the new item there. */ |
345 | temp = (start + 1) % hash_size; |
346 | while (hash_table[temp] != NULL) |
347 | temp = (temp + 1) % hash_size; |
348 | |
349 | /* Insert at the NULL. */ |
350 | hash_table[temp] = new_value; |
351 | } |
352 | |
353 | /* Associate FILE_NAME with the inode NODE_NUM. (Insert into hash table.) */ |
354 | |
355 | void |
356 | add_inode(unsigned long node_num, char *file_name, unsigned long major_num, |
357 | unsigned long minor_num) |
358 | { |
359 | struct inode_val *temp; |
360 | |
361 | /* Create new inode record. */ |
362 | temp = (struct inode_val *)xmalloc(sizeof(struct inode_val)); |
363 | temp->inode = node_num; |
364 | temp->major_num = major_num; |
365 | temp->minor_num = minor_num; |
366 | temp->file_name = xstrdup(file_name); |
367 | |
368 | /* Do we have to increase the size of (or initially allocate) |
369 | the hash table? */ |
370 | if (hash_num == hash_size || hash_table == NULL) { |
371 | struct inode_val **old_table; /* Pointer to old table. */ |
372 | int i; /* Index for re-insert loop. */ |
373 | |
374 | /* Save old table. */ |
375 | old_table = hash_table; |
376 | if (old_table == NULL) |
377 | hash_num = 0; |
378 | |
379 | /* Calculate new size of table and allocate it. |
380 | Sequence of table sizes is 47, 97, 197, 397, 797, 1597, 3197, 6397 ... |
381 | where 3197 and most of the sizes after 6397 are not prime. The other |
382 | numbers listed are prime. */ |
383 | hash_size = 2 * hash_size + 3; |
384 | hash_table = (struct inode_val **) |
385 | xmalloc(hash_size * sizeof(struct inode_val *)); |
386 | memset(hash_table, 0, hash_size * sizeof(struct inode_val *)); |
387 | |
388 | /* Insert the values from the old table into the new table. */ |
389 | for (i = 0; i < hash_num; i++) |
390 | hash_insert(old_table[i]); |
391 | |
392 | if (old_table != NULL) |
393 | free(old_table); |
394 | } |
395 | |
396 | /* Insert the new record and increment the count of elements in the |
397 | hash table. */ |
398 | hash_insert(temp); |
399 | hash_num++; |
400 | } |
401 | |
402 | char *find_inode_file(unsigned long node_num, unsigned long major_num, |
403 | unsigned long minor_num) |
404 | { |
405 | int start; /* Initial hash location. */ |
406 | int temp; /* Rehash search variable. */ |
407 | |
408 | if (hash_table != NULL) { |
409 | /* Hash function is node number modulo the table size. */ |
410 | start = node_num % hash_size; |
411 | |
412 | /* Initial look into the table. */ |
413 | if (hash_table[start] == NULL) |
414 | return NULL; |
415 | if (hash_table[start]->inode == node_num |
416 | && hash_table[start]->major_num == major_num |
417 | && hash_table[start]->minor_num == minor_num) |
418 | return hash_table[start]->file_name; |
419 | |
420 | /* The home position is full with a different inode record. |
421 | Do a linear search terminated by a NULL pointer. */ |
422 | for (temp = (start + 1) % hash_size; |
423 | hash_table[temp] != NULL && temp != start; |
424 | temp = (temp + 1) % hash_size) { |
425 | if (hash_table[temp]->inode == node_num |
426 | && hash_table[start]->major_num == major_num |
427 | && hash_table[start]->minor_num == minor_num) |
428 | return hash_table[temp]->file_name; |
429 | } |
430 | } |
431 | return NULL; |
432 | } |
433 | |
434 | /* Try and create a hard link from FILE_NAME to another file |
435 | with the given major/minor device number and inode. If no other |
436 | file with the same major/minor/inode numbers is known, add this file |
437 | to the list of known files and associated major/minor/inode numbers |
438 | and return -1. If another file with the same major/minor/inode |
439 | numbers is found, try and create another link to it using |
440 | link_to_name, and return 0 for success and -1 for failure. */ |
441 | |
442 | int |
443 | link_to_maj_min_ino(char *file_name, int st_dev_maj, int st_dev_min, int st_ino) |
444 | { |
445 | int link_res; |
446 | char *link_name; |
447 | link_res = -1; |
448 | /* Is the file a link to a previously copied file? */ |
449 | link_name = find_inode_file(st_ino, st_dev_maj, st_dev_min); |
450 | if (link_name == NULL) |
451 | add_inode(st_ino, file_name, st_dev_maj, st_dev_min); |
452 | else |
453 | link_res = link_to_name(file_name, link_name); |
454 | return link_res; |
455 | } |
456 | |
457 | static void copyin_regular_file(struct new_cpio_header *file_hdr, |
458 | int in_file_des); |
459 | |
460 | void warn_junk_bytes(long bytes_skipped) |
461 | { |
462 | fprintf(stderr, "%s: warning: skipped %ld byte(s) of junk\n", |
463 | progname, bytes_skipped); |
464 | } |
465 | |
466 | /* Skip the padding on IN_FILE_DES after a header or file, |
467 | up to the next header. |
468 | The number of bytes skipped is based on OFFSET -- the current offset |
469 | from the last start of a header (or file) -- and the current |
470 | header type. */ |
471 | |
472 | static void tape_skip_padding(int in_file_des, int offset) |
473 | { |
474 | int pad; |
475 | pad = (4 - (offset % 4)) % 4; |
476 | |
477 | if (pad != 0) |
478 | tape_toss_input(in_file_des, pad); |
479 | } |
480 | |
481 | static int |
482 | try_existing_file(struct new_cpio_header *file_hdr, int in_file_des, |
483 | int *existing_dir) |
484 | { |
485 | struct stat file_stat; |
486 | |
487 | *existing_dir = false; |
488 | if (lstat(file_hdr->c_name, &file_stat) == 0) { |
489 | if (S_ISDIR(file_stat.st_mode) |
490 | && ((file_hdr->c_mode & S_IFMT) == S_IFDIR)) { |
491 | /* If there is already a directory there that |
492 | we are trying to create, don't complain about |
493 | it. */ |
494 | *existing_dir = true; |
495 | return 0; |
496 | } else if (S_ISDIR(file_stat.st_mode) |
497 | ? rmdir(file_hdr->c_name) |
498 | : unlink(file_hdr->c_name)) { |
499 | fprintf(stderr, "%s: cannot remove current %s: %s\n", |
500 | progname, file_hdr->c_name, strerror(errno)); |
501 | tape_toss_input(in_file_des, file_hdr->c_filesize); |
502 | tape_skip_padding(in_file_des, file_hdr->c_filesize); |
503 | return -1; /* Go to the next file. */ |
504 | } |
505 | } |
506 | return 0; |
507 | } |
508 | |
509 | /* The newc and crc formats store multiply linked copies of the same file |
510 | in the archive only once. The actual data is attached to the last link |
511 | in the archive, and the other links all have a filesize of 0. When a |
512 | file in the archive has multiple links and a filesize of 0, its data is |
513 | probably "attatched" to another file in the archive, so we can't create |
514 | it right away. We have to "defer" creating it until we have created |
515 | the file that has the data "attatched" to it. We keep a list of the |
516 | "defered" links on deferments. */ |
517 | |
518 | struct deferment *deferments = NULL; |
519 | |
520 | /* Add a file header to the deferments list. For now they all just |
521 | go on one list, although we could optimize this if necessary. */ |
522 | |
523 | static void defer_copyin(struct new_cpio_header *file_hdr) |
524 | { |
525 | struct deferment *d; |
526 | d = create_deferment(file_hdr); |
527 | d->next = deferments; |
528 | deferments = d; |
529 | return; |
530 | } |
531 | |
532 | /* We just created a file that (probably) has some other links to it |
533 | which have been defered. Go through all of the links on the deferments |
534 | list and create any which are links to this file. */ |
535 | |
536 | static void create_defered_links(struct new_cpio_header *file_hdr) |
537 | { |
538 | struct deferment *d; |
539 | struct deferment *d_prev; |
540 | int ino; |
541 | int maj; |
542 | int min; |
543 | int link_res; |
544 | ino = file_hdr->c_ino; |
545 | maj = file_hdr->c_dev_maj; |
546 | min = file_hdr->c_dev_min; |
547 | d = deferments; |
548 | d_prev = NULL; |
549 | while (d != NULL) { |
550 | if ((d->header.c_ino == ino) && (d->header.c_dev_maj == maj) |
551 | && (d->header.c_dev_min == min)) { |
552 | struct deferment *d_free; |
553 | link_res = |
554 | link_to_name(d->header.c_name, file_hdr->c_name); |
555 | if (link_res < 0) { |
556 | fprintf(stderr, |
557 | "%s: cannot link %s to %s: %s\n", |
558 | progname, d->header.c_name, |
559 | file_hdr->c_name, strerror(errno)); |
560 | } |
561 | if (d_prev != NULL) |
562 | d_prev->next = d->next; |
563 | else |
564 | deferments = d->next; |
565 | d_free = d; |
566 | d = d->next; |
567 | free_deferment(d_free); |
568 | } else { |
569 | d_prev = d; |
570 | d = d->next; |
571 | } |
572 | } |
573 | } |
574 | |
575 | /* If we had a multiply linked file that really was empty then we would |
576 | have defered all of its links, since we never found any with data |
577 | "attached", and they will still be on the deferment list even when |
578 | we are done reading the whole archive. Write out all of these |
579 | empty links that are still on the deferments list. */ |
580 | |
581 | static void create_final_defers() |
582 | { |
583 | struct deferment *d; |
584 | int link_res; |
585 | int out_file_des; |
586 | struct utimbuf times; /* For setting file times. */ |
587 | /* Initialize this in case it has members we don't know to set. */ |
588 | memset(×, 0, sizeof(struct utimbuf)); |
589 | |
590 | for (d = deferments; d != NULL; d = d->next) { |
591 | /* Debian hack: A line, which could cause an endless loop, was |
592 | removed (97/1/2). It was reported by Ronald F. Guilmette to |
593 | the upstream maintainers. -BEM */ |
594 | /* Debian hack: This was reported by Horst Knobloch. This bug has |
595 | been reported to "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM |
596 | */ |
597 | link_res = link_to_maj_min_ino(d->header.c_name, |
598 | d->header.c_dev_maj, |
599 | d->header.c_dev_min, |
600 | d->header.c_ino); |
601 | if (link_res == 0) { |
602 | continue; |
603 | } |
604 | out_file_des = open(d->header.c_name, |
605 | O_CREAT | O_WRONLY | O_BINARY, 0600); |
606 | if (out_file_des < 0) { |
607 | fprintf(stderr, "%s: open %s: %s\n", |
608 | progname, d->header.c_name, strerror(errno)); |
609 | continue; |
610 | } |
611 | |
612 | /* File is now copied; set attributes. */ |
613 | if ((fchown(out_file_des, d->header.c_uid, d->header.c_gid) < 0) |
614 | && errno != EPERM) |
615 | fprintf(stderr, "%s: fchown %s: %s\n", |
616 | progname, d->header.c_name, strerror(errno)); |
617 | /* chown may have turned off some permissions we wanted. */ |
618 | if (fchmod(out_file_des, (int)d->header.c_mode) < 0) |
619 | fprintf(stderr, "%s: fchmod %s: %s\n", |
620 | progname, d->header.c_name, strerror(errno)); |
621 | |
622 | if (close(out_file_des) < 0) |
623 | fprintf(stderr, "%s: close %s: %s\n", |
624 | progname, d->header.c_name, strerror(errno)); |
625 | |
626 | } |
627 | } |
628 | |
629 | static void |
630 | copyin_regular_file(struct new_cpio_header *file_hdr, int in_file_des) |
631 | { |
632 | int out_file_des; /* Output file descriptor. */ |
633 | |
634 | /* Can the current file be linked to a previously copied file? */ |
635 | if (file_hdr->c_nlink > 1) { |
636 | int link_res; |
637 | if (file_hdr->c_filesize == 0) { |
638 | /* The newc and crc formats store multiply linked copies |
639 | of the same file in the archive only once. The |
640 | actual data is attached to the last link in the |
641 | archive, and the other links all have a filesize |
642 | of 0. Since this file has multiple links and a |
643 | filesize of 0, its data is probably attatched to |
644 | another file in the archive. Save the link, and |
645 | process it later when we get the actual data. We |
646 | can't just create it with length 0 and add the |
647 | data later, in case the file is readonly. We still |
648 | lose if its parent directory is readonly (and we aren't |
649 | running as root), but there's nothing we can do about |
650 | that. */ |
651 | defer_copyin(file_hdr); |
652 | tape_toss_input(in_file_des, file_hdr->c_filesize); |
653 | tape_skip_padding(in_file_des, file_hdr->c_filesize); |
654 | return; |
655 | } |
656 | /* If the file has data (filesize != 0), then presumably |
657 | any other links have already been defer_copyin'ed(), |
658 | but GNU cpio version 2.0-2.2 didn't do that, so we |
659 | still have to check for links here (and also in case |
660 | the archive was created and later appeneded to). */ |
661 | /* Debian hack: (97/1/2) This was reported by Ronald |
662 | F. Guilmette to the upstream maintainers. -BEM */ |
663 | link_res = link_to_maj_min_ino(file_hdr->c_name, |
664 | file_hdr->c_dev_maj, |
665 | file_hdr->c_dev_min, |
666 | file_hdr->c_ino); |
667 | if (link_res == 0) { |
668 | tape_toss_input(in_file_des, file_hdr->c_filesize); |
669 | tape_skip_padding(in_file_des, file_hdr->c_filesize); |
670 | return; |
671 | } |
672 | } |
673 | |
674 | /* If not linked, copy the contents of the file. */ |
675 | out_file_des = open(file_hdr->c_name, |
676 | O_CREAT | O_WRONLY | O_BINARY, 0600); |
677 | |
678 | if (out_file_des < 0) { |
679 | fprintf(stderr, "%s: open %s: %s\n", |
680 | progname, file_hdr->c_name, strerror(errno)); |
681 | tape_toss_input(in_file_des, file_hdr->c_filesize); |
682 | tape_skip_padding(in_file_des, file_hdr->c_filesize); |
683 | return; |
684 | } |
685 | |
686 | copy_files_tape_to_disk(in_file_des, out_file_des, |
687 | file_hdr->c_filesize); |
688 | disk_empty_output_buffer(out_file_des); |
689 | |
690 | if (close(out_file_des) < 0) |
691 | fprintf(stderr, "%s: close %s: %s\n", |
692 | progname, file_hdr->c_name, strerror(errno)); |
693 | |
694 | /* File is now copied; set attributes. */ |
695 | if ((chown(file_hdr->c_name, file_hdr->c_uid, file_hdr->c_gid) < 0) |
696 | && errno != EPERM) |
697 | fprintf(stderr, "%s: chown %s: %s\n", |
698 | progname, file_hdr->c_name, strerror(errno)); |
699 | |
700 | /* chown may have turned off some permissions we wanted. */ |
701 | if (chmod(file_hdr->c_name, (int)file_hdr->c_mode) < 0) |
702 | fprintf(stderr, "%s: chmod %s: %s\n", |
703 | progname, file_hdr->c_name, strerror(errno)); |
704 | |
705 | tape_skip_padding(in_file_des, file_hdr->c_filesize); |
706 | if (file_hdr->c_nlink > 1) { |
707 | /* (see comment above for how the newc and crc formats |
708 | store multiple links). Now that we have the data |
709 | for this file, create any other links to it which |
710 | we defered. */ |
711 | create_defered_links(file_hdr); |
712 | } |
713 | } |
714 | |
715 | /* In general, we can't use the builtin `basename' function if available, |
716 | since it has different meanings in different environments. |
717 | In some environments the builtin `basename' modifies its argument. |
718 | |
719 | Return the address of the last file name component of NAME. If |
720 | NAME has no file name components because it is all slashes, return |
721 | NAME if it is empty, the address of its last slash otherwise. */ |
722 | |
723 | char *base_name(char const *name) |
724 | { |
725 | char const *base = name + FILE_SYSTEM_PREFIX_LEN(name); |
726 | char const *p; |
727 | |
728 | for (p = base; *p; p++) { |
729 | if (ISSLASH(*p)) { |
730 | /* Treat multiple adjacent slashes like a single slash. */ |
731 | do |
732 | p++; |
733 | while (ISSLASH(*p)); |
734 | |
735 | /* If the file name ends in slash, use the trailing slash as |
736 | the basename if no non-slashes have been found. */ |
737 | if (!*p) { |
738 | if (ISSLASH(*base)) |
739 | base = p - 1; |
740 | break; |
741 | } |
742 | |
743 | /* *P is a non-slash preceded by a slash. */ |
744 | base = p; |
745 | } |
746 | } |
747 | |
748 | return (char *)base; |
749 | } |
750 | |
751 | /* Return the length of of the basename NAME. Typically NAME is the |
752 | value returned by base_name. Act like strlen (NAME), except omit |
753 | redundant trailing slashes. */ |
754 | |
755 | size_t base_len(char const *name) |
756 | { |
757 | size_t len; |
758 | |
759 | for (len = strlen(name); 1 < len && ISSLASH(name[len - 1]); len--) |
760 | continue; |
761 | |
762 | return len; |
763 | } |
764 | |
765 | /* Remove trailing slashes from PATH. |
766 | Return true if a trailing slash was removed. |
767 | This is useful when using filename completion from a shell that |
768 | adds a "/" after directory names (such as tcsh and bash), because |
769 | the Unix rename and rmdir system calls return an "Invalid argument" error |
770 | when given a path that ends in "/" (except for the root directory). */ |
771 | |
772 | bool strip_trailing_slashes(char *path) |
773 | { |
774 | char *base = base_name(path); |
775 | char *base_lim = base + base_len(base); |
776 | bool had_slash = (*base_lim != '\0'); |
777 | *base_lim = '\0'; |
778 | return had_slash; |
779 | } |
780 | |
781 | static void copyin_directory(struct new_cpio_header *file_hdr, int existing_dir) |
782 | { |
783 | int res; /* Result of various function calls. */ |
784 | |
785 | /* Strip any trailing `/'s off the filename; tar puts |
786 | them on. We might as well do it here in case anybody |
787 | else does too, since they cause strange things to happen. */ |
788 | strip_trailing_slashes(file_hdr->c_name); |
789 | |
790 | /* Ignore the current directory. It must already exist, |
791 | and we don't want to change its permission, ownership |
792 | or time. */ |
793 | if (file_hdr->c_name[0] == '.' && file_hdr->c_name[1] == '\0') { |
794 | return; |
795 | } |
796 | |
797 | if (!existing_dir) |
798 | { |
799 | res = mkdir(file_hdr->c_name, file_hdr->c_mode); |
800 | } else |
801 | res = 0; |
802 | if (res < 0) { |
803 | /* In some odd cases where the file_hdr->c_name includes `.', |
804 | the directory may have actually been created by |
805 | create_all_directories(), so the mkdir will fail |
806 | because the directory exists. If that's the case, |
807 | don't complain about it. */ |
808 | struct stat file_stat; |
809 | if ((errno != EEXIST) || |
810 | (lstat(file_hdr->c_name, &file_stat) != 0) || |
811 | !(S_ISDIR(file_stat.st_mode))) { |
812 | fprintf(stderr, "%s: lstat %s: %s\n", |
813 | progname, file_hdr->c_name, strerror(errno)); |
814 | return; |
815 | } |
816 | } |
817 | if ((chown(file_hdr->c_name, file_hdr->c_uid, file_hdr->c_gid) < 0) |
818 | && errno != EPERM) |
819 | fprintf(stderr, "%s: chown %s: %s\n", |
820 | progname, file_hdr->c_name, strerror(errno)); |
821 | /* chown may have turned off some permissions we wanted. */ |
822 | if (chmod(file_hdr->c_name, (int)file_hdr->c_mode) < 0) |
823 | fprintf(stderr, "%s: chmod %s: %s\n", |
824 | progname, file_hdr->c_name, strerror(errno)); |
825 | } |
826 | |
827 | static void copyin_device(struct new_cpio_header *file_hdr) |
828 | { |
829 | int res; /* Result of various function calls. */ |
830 | |
831 | if (file_hdr->c_nlink > 1) { |
832 | int link_res; |
833 | /* Debian hack: This was reported by Horst |
834 | Knobloch. This bug has been reported to |
835 | "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM */ |
836 | link_res = link_to_maj_min_ino(file_hdr->c_name, |
837 | file_hdr->c_dev_maj, |
838 | file_hdr->c_dev_min, |
839 | file_hdr->c_ino); |
840 | if (link_res == 0) { |
841 | return; |
842 | } |
843 | } |
844 | |
845 | res = mknod(file_hdr->c_name, file_hdr->c_mode, |
846 | makedev(file_hdr->c_rdev_maj, file_hdr->c_rdev_min)); |
847 | if (res < 0) { |
848 | fprintf(stderr, "%s: mknod %s: %s\n", progname, |
849 | file_hdr->c_name, strerror(errno)); |
850 | return; |
851 | } |
852 | if ((chown(file_hdr->c_name, file_hdr->c_uid, file_hdr->c_gid) < 0) |
853 | && errno != EPERM) |
854 | fprintf(stderr, "%s: chown %s: %s\n", progname, |
855 | file_hdr->c_name, strerror(errno)); |
856 | /* chown may have turned off some permissions we wanted. */ |
857 | if (chmod(file_hdr->c_name, file_hdr->c_mode) < 0) |
858 | fprintf(stderr, "%s: chmod %s: %s\n", progname, |
859 | file_hdr->c_name, strerror(errno)); |
860 | } |
861 | |
862 | static void copyin_link(struct new_cpio_header *file_hdr, int in_file_des) |
863 | { |
864 | char *link_name = NULL; /* Name of hard and symbolic links. */ |
865 | int res; /* Result of various function calls. */ |
866 | |
867 | link_name = (char *)xmalloc((unsigned int)file_hdr->c_filesize + 1); |
868 | link_name[file_hdr->c_filesize] = '\0'; |
869 | tape_buffered_read(link_name, in_file_des, file_hdr->c_filesize); |
870 | tape_skip_padding(in_file_des, file_hdr->c_filesize); |
871 | |
872 | res = UMASKED_SYMLINK(link_name, file_hdr->c_name, file_hdr->c_mode); |
873 | if (res < 0) { |
874 | fprintf(stderr, "%s: UMASKED_SYMLINK %s: %s\n", |
875 | progname, file_hdr->c_name, strerror(errno)); |
876 | free(link_name); |
877 | return; |
878 | } |
879 | if ((lchown(file_hdr->c_name, file_hdr->c_uid, file_hdr->c_gid) < 0) |
880 | && errno != EPERM) { |
881 | fprintf(stderr, "%s: lchown %s: %s\n", |
882 | progname, file_hdr->c_name, strerror(errno)); |
883 | } |
884 | free(link_name); |
885 | } |
886 | |
887 | static void copyin_file(struct new_cpio_header *file_hdr, int in_file_des) |
888 | { |
889 | int existing_dir; |
890 | |
891 | if (try_existing_file(file_hdr, in_file_des, &existing_dir) < 0) |
892 | return; |
893 | |
894 | /* Do the real copy or link. */ |
895 | switch (file_hdr->c_mode & S_IFMT) { |
896 | case S_IFREG: |
897 | copyin_regular_file(file_hdr, in_file_des); |
898 | break; |
899 | |
900 | case S_IFDIR: |
901 | copyin_directory(file_hdr, existing_dir); |
902 | break; |
903 | |
904 | case S_IFCHR: |
905 | case S_IFBLK: |
906 | #ifdef S_IFSOCK |
907 | case S_IFSOCK: |
908 | #endif |
909 | #ifdef S_IFIFO |
910 | case S_IFIFO: |
911 | #endif |
912 | copyin_device(file_hdr); |
913 | break; |
914 | |
915 | #ifdef S_IFLNK |
916 | case S_IFLNK: |
917 | copyin_link(file_hdr, in_file_des); |
918 | break; |
919 | #endif |
920 | |
921 | default: |
922 | fprintf(stderr, "%s: %s: unknown file type\n", |
923 | progname, file_hdr->c_name); |
924 | tape_toss_input(in_file_des, file_hdr->c_filesize); |
925 | tape_skip_padding(in_file_des, file_hdr->c_filesize); |
926 | } |
927 | } |
928 | |
929 | /* Fill in FILE_HDR by reading a new-format ASCII format cpio header from |
930 | file descriptor IN_DES, except for the magic number, which is |
931 | already filled in. */ |
932 | |
933 | void read_in_new_ascii(struct new_cpio_header *file_hdr, int in_des) |
934 | { |
935 | char ascii_header[112]; |
936 | |
937 | tape_buffered_read(ascii_header, in_des, 104L); |
938 | ascii_header[104] = '\0'; |
939 | sscanf(ascii_header, |
940 | "%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx", |
941 | &file_hdr->c_ino, &file_hdr->c_mode, &file_hdr->c_uid, |
942 | &file_hdr->c_gid, &file_hdr->c_nlink, &file_hdr->c_mtime, |
943 | &file_hdr->c_filesize, &file_hdr->c_dev_maj, |
944 | &file_hdr->c_dev_min, &file_hdr->c_rdev_maj, |
945 | &file_hdr->c_rdev_min, &file_hdr->c_namesize, |
946 | &file_hdr->c_chksum); |
947 | /* Read file name from input. */ |
948 | if (file_hdr->c_name != NULL) |
949 | free(file_hdr->c_name); |
950 | file_hdr->c_name = (char *)xmalloc(file_hdr->c_namesize); |
951 | tape_buffered_read(file_hdr->c_name, in_des, |
952 | (long)file_hdr->c_namesize); |
953 | |
954 | /* In SVR4 ASCII format, the amount of space allocated for the header |
955 | is rounded up to the next long-word, so we might need to drop |
956 | 1-3 bytes. */ |
957 | tape_skip_padding(in_des, file_hdr->c_namesize + 110); |
958 | } |
959 | |
960 | /* Return 16-bit integer I with the bytes swapped. */ |
961 | #define swab_short(i) ((((i) << 8) & 0xff00) | (((i) >> 8) & 0x00ff)) |
962 | |
963 | /* Read the header, including the name of the file, from file |
964 | descriptor IN_DES into FILE_HDR. */ |
965 | |
966 | void read_in_header(struct new_cpio_header *file_hdr, int in_des) |
967 | { |
968 | long bytes_skipped = 0; /* Bytes of junk found before magic number. */ |
969 | |
970 | /* Search for a valid magic number. */ |
971 | |
972 | file_hdr->c_tar_linkname = NULL; |
973 | |
974 | tape_buffered_read((char *)file_hdr, in_des, 6L); |
975 | while (1) { |
976 | if (!strncmp((char *)file_hdr, "070702", 6) |
977 | || !strncmp((char *)file_hdr, "070701", 6)) |
978 | { |
979 | if (bytes_skipped > 0) |
980 | warn_junk_bytes(bytes_skipped); |
981 | |
982 | read_in_new_ascii(file_hdr, in_des); |
983 | break; |
984 | } |
985 | bytes_skipped++; |
986 | memmove((char *)file_hdr, (char *)file_hdr + 1, 5); |
987 | tape_buffered_read((char *)file_hdr + 5, in_des, 1L); |
988 | } |
989 | } |
990 | |
991 | /* Read the collection from standard input and create files |
992 | in the file system. */ |
993 | |
994 | void process_copy_in() |
995 | { |
996 | char done = false; /* True if trailer reached. */ |
997 | struct new_cpio_header file_hdr; /* Output header information. */ |
998 | int in_file_des; /* Input file descriptor. */ |
999 | |
1000 | /* Initialize the copy in. */ |
1001 | file_hdr.c_name = NULL; |
1002 | |
1003 | /* only from stdin */ |
1004 | in_file_des = 0; |
1005 | |
1006 | /* While there is more input in the collection, process the input. */ |
1007 | while (!done) { |
1008 | /* Start processing the next file by reading the header. */ |
1009 | read_in_header(&file_hdr, in_file_des); |
1010 | |
1011 | /* Is this the header for the TRAILER file? */ |
1012 | if (strcmp("TRAILER!!!", file_hdr.c_name) == 0) { |
1013 | done = true; |
1014 | break; |
1015 | } |
1016 | |
1017 | /* Copy the input file into the directory structure. */ |
1018 | |
1019 | copyin_file(&file_hdr, in_file_des); |
1020 | |
1021 | if (dot_flag) |
1022 | fputc('.', stderr); |
1023 | } |
1024 | |
1025 | if (dot_flag) |
1026 | fputc('\n', stderr); |
1027 | |
1028 | create_final_defers(); |
1029 | |
1030 | } |
1031 | |
1032 | /* Initialize the input and output buffers to their proper size and |
1033 | initialize all variables associated with the input and output |
1034 | buffers. */ |
1035 | |
1036 | void initialize_buffers() |
1037 | { |
1038 | int in_buf_size, out_buf_size; |
1039 | |
1040 | /* Make sure the input buffer can always hold 2 blocks and that it |
1041 | is big enough to hold 1 tar record (512 bytes) even if it |
1042 | is not aligned on a block boundary. The extra buffer space |
1043 | is needed by process_copyin and peek_in_buf to automatically |
1044 | figure out what kind of archive it is reading. */ |
1045 | if (io_block_size >= 512) |
1046 | in_buf_size = 2 * io_block_size; |
1047 | else |
1048 | in_buf_size = 1024; |
1049 | out_buf_size = DISK_IO_BLOCK_SIZE; |
1050 | |
1051 | input_buffer = (char *)xmalloc(in_buf_size); |
1052 | in_buff = input_buffer; |
1053 | input_buffer_size = in_buf_size; |
1054 | input_size = 0; |
1055 | input_bytes = 0; |
1056 | |
1057 | output_buffer = (char *)xmalloc(out_buf_size); |
1058 | out_buff = output_buffer; |
1059 | output_size = 0; |
1060 | output_bytes = 0; |
1061 | |
1062 | } |
1063 | |
1064 | int main(int argc, char *argv[]) |
1065 | { |
1066 | int c; |
1067 | int extract_flag = false; |
1068 | |
1069 | progname = argv[0]; |
1070 | |
1071 | do { |
1072 | c = getopt(argc, argv, "iV"); |
1073 | if (c == EOF) |
1074 | break; |
1075 | switch (c) { |
1076 | case 'V': |
1077 | dot_flag = true; |
1078 | break; |
1079 | |
1080 | case 'i': |
1081 | extract_flag = true; |
1082 | break; |
1083 | case '?': |
1084 | fprintf(stderr, |
1085 | "%s: not implemented or invalid option -%c\n", |
1086 | progname, optopt); |
1087 | exit(1); |
1088 | |
1089 | } |
1090 | } while (1); |
1091 | |
1092 | if (extract_flag) { |
1093 | initialize_buffers(); |
1094 | |
1095 | process_copy_in(); |
1096 | } else { |
1097 | fprintf(stderr, "Usage: %s [-V] -i [< archive]\n", progname); |
1098 | exit(1); |
1099 | } |
1100 | |
1101 | return 0; |
1102 | } |