diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-04-05 02:44:30 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-04-05 02:44:30 +0000 |
commit | 1af00eda92e9a036ff217b11ff45ebe559de66f9 (patch) | |
tree | 67472bb3e4eddce347cfe7fa753c24f07faa5c4a | |
parent | 1a95e3960ca7dde16bda26ec9dad986199bae3c3 (diff) | |
download | busybox-w32-1af00eda92e9a036ff217b11ff45ebe559de66f9.tar.gz busybox-w32-1af00eda92e9a036ff217b11ff45ebe559de66f9.tar.bz2 busybox-w32-1af00eda92e9a036ff217b11ff45ebe559de66f9.zip |
cpio: fix a bug where we do not extract zero-sized hardlinks
(spotted at http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=466771).
Add testsuite entry for that, and another one for another bug:
we do not list hardlinks in cpio -t (not fixed).
function old new delta
get_header_cpio 884 909 +25
static.saved_hardlinks_created - 4 +4
static.pending_hardlinks 4 - -4
static.inode 4 - -4
cpio_main 1122 1060 -62
-rw-r--r-- | TODO_config_nommu | 1 | ||||
-rw-r--r-- | archival/Config.in | 7 | ||||
-rw-r--r-- | archival/cpio.c | 4 | ||||
-rw-r--r-- | archival/libunarchive/data_extract_all.c | 2 | ||||
-rw-r--r-- | archival/libunarchive/get_header_cpio.c | 183 | ||||
-rw-r--r-- | archival/libunarchive/seek_by_jump.c | 6 | ||||
-rw-r--r-- | scripts/defconfig | 1 |
7 files changed, 99 insertions, 105 deletions
diff --git a/TODO_config_nommu b/TODO_config_nommu index 42d17314a..7fdec69bd 100644 --- a/TODO_config_nommu +++ b/TODO_config_nommu | |||
@@ -130,7 +130,6 @@ CONFIG_UNZIP=y | |||
130 | # | 130 | # |
131 | # Common options for cpio and tar | 131 | # Common options for cpio and tar |
132 | # | 132 | # |
133 | CONFIG_FEATURE_UNARCHIVE_TAPE=y | ||
134 | 133 | ||
135 | # | 134 | # |
136 | # Common options for dpkg and dpkg_deb | 135 | # Common options for dpkg and dpkg_deb |
diff --git a/archival/Config.in b/archival/Config.in index 28450612e..35ac40bb7 100644 --- a/archival/Config.in +++ b/archival/Config.in | |||
@@ -307,13 +307,6 @@ config UNZIP | |||
307 | comment "Common options for cpio and tar" | 307 | comment "Common options for cpio and tar" |
308 | depends on CPIO || TAR | 308 | depends on CPIO || TAR |
309 | 309 | ||
310 | config FEATURE_UNARCHIVE_TAPE | ||
311 | bool "Enable tape drive support" | ||
312 | default n | ||
313 | depends on CPIO || TAR | ||
314 | help | ||
315 | I don't think this is needed anymore. | ||
316 | |||
317 | comment "Common options for dpkg and dpkg_deb" | 310 | comment "Common options for dpkg and dpkg_deb" |
318 | depends on DPKG || DPKG_DEB | 311 | depends on DPKG || DPKG_DEB |
319 | 312 | ||
diff --git a/archival/cpio.c b/archival/cpio.c index dd9ed756c..2919ff8de 100644 --- a/archival/cpio.c +++ b/archival/cpio.c | |||
@@ -171,9 +171,7 @@ static int cpio_o(void) | |||
171 | int fd = xopen(name, O_RDONLY); | 171 | int fd = xopen(name, O_RDONLY); |
172 | fflush(stdout); | 172 | fflush(stdout); |
173 | /* We must abort if file got shorter too! */ | 173 | /* We must abort if file got shorter too! */ |
174 | if (bb_copyfd_size(fd, STDOUT_FILENO, st.st_size) != st.st_size) { | 174 | bb_copyfd_exact_size(fd, STDOUT_FILENO, st.st_size); |
175 | bb_error_msg_and_die("I/O error or file '%s' was truncated", name); | ||
176 | } | ||
177 | bytes += st.st_size; | 175 | bytes += st.st_size; |
178 | close(fd); | 176 | close(fd); |
179 | } | 177 | } |
diff --git a/archival/libunarchive/data_extract_all.c b/archival/libunarchive/data_extract_all.c index 4df9c09a7..29a224bbc 100644 --- a/archival/libunarchive/data_extract_all.c +++ b/archival/libunarchive/data_extract_all.c | |||
@@ -20,7 +20,7 @@ void data_extract_all(archive_handle_t *archive_handle) | |||
20 | 20 | ||
21 | /* Check if the file already exists */ | 21 | /* Check if the file already exists */ |
22 | if (archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) { | 22 | if (archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) { |
23 | /* Remove the existing entry if it exists */ | 23 | /* Remove the entry if it exists */ |
24 | if (((file_header->mode & S_IFMT) != S_IFDIR) | 24 | if (((file_header->mode & S_IFMT) != S_IFDIR) |
25 | && (unlink(file_header->name) == -1) | 25 | && (unlink(file_header->name) == -1) |
26 | && (errno != ENOENT) | 26 | && (errno != ENOENT) |
diff --git a/archival/libunarchive/get_header_cpio.c b/archival/libunarchive/get_header_cpio.c index 3f5135512..b97b53b20 100644 --- a/archival/libunarchive/get_header_cpio.c +++ b/archival/libunarchive/get_header_cpio.c | |||
@@ -8,70 +8,32 @@ | |||
8 | #include "unarchive.h" | 8 | #include "unarchive.h" |
9 | 9 | ||
10 | typedef struct hardlinks_s { | 10 | typedef struct hardlinks_s { |
11 | char *name; | ||
12 | int inode; | ||
13 | struct hardlinks_s *next; | 11 | struct hardlinks_s *next; |
12 | int inode; /* TODO: must match maj/min too! */ | ||
13 | int mode ; | ||
14 | int mtime; /* These three are useful only in corner case */ | ||
15 | int uid ; /* of hardlinks with zero size body */ | ||
16 | int gid ; | ||
17 | char name[1]; | ||
14 | } hardlinks_t; | 18 | } hardlinks_t; |
15 | 19 | ||
16 | char get_header_cpio(archive_handle_t *archive_handle) | 20 | char get_header_cpio(archive_handle_t *archive_handle) |
17 | { | 21 | { |
18 | static hardlinks_t *saved_hardlinks = NULL; | 22 | static hardlinks_t *saved_hardlinks = NULL; |
19 | static unsigned pending_hardlinks = 0; | 23 | static hardlinks_t *saved_hardlinks_created = NULL; |
20 | static int inode; | ||
21 | 24 | ||
22 | file_header_t *file_header = archive_handle->file_header; | 25 | file_header_t *file_header = archive_handle->file_header; |
23 | char cpio_header[110]; | 26 | char cpio_header[110]; |
24 | int namesize; | ||
25 | char dummy[16]; | 27 | char dummy[16]; |
26 | int major, minor, nlink; | 28 | int namesize; |
27 | 29 | int major, minor, nlink, mode, inode; | |
28 | if (pending_hardlinks) { /* Deal with any pending hardlinks */ | 30 | unsigned size, uid, gid, mtime; |
29 | hardlinks_t *tmp, *oldtmp; | ||
30 | |||
31 | tmp = saved_hardlinks; | ||
32 | oldtmp = NULL; | ||
33 | |||
34 | file_header->link_target = file_header->name; | ||
35 | file_header->size = 0; | ||
36 | |||
37 | while (tmp) { | ||
38 | if (tmp->inode != inode) { | ||
39 | tmp = tmp->next; | ||
40 | continue; | ||
41 | } | ||
42 | |||
43 | file_header->name = tmp->name; | ||
44 | |||
45 | if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { | ||
46 | archive_handle->action_data(archive_handle); | ||
47 | archive_handle->action_header(archive_handle->file_header); | ||
48 | } | ||
49 | |||
50 | pending_hardlinks--; | ||
51 | |||
52 | oldtmp = tmp; | ||
53 | tmp = tmp->next; | ||
54 | free(oldtmp->name); | ||
55 | free(oldtmp); | ||
56 | if (oldtmp == saved_hardlinks) | ||
57 | saved_hardlinks = tmp; | ||
58 | } | ||
59 | |||
60 | file_header->name = file_header->link_target; | ||
61 | |||
62 | if (pending_hardlinks > 1) { | ||
63 | bb_error_msg("error resolving hardlink: archive made by GNU cpio 2.0-2.2?"); | ||
64 | } | ||
65 | |||
66 | /* No more pending hardlinks, read next file entry */ | ||
67 | pending_hardlinks = 0; | ||
68 | } | ||
69 | 31 | ||
70 | /* There can be padding before archive header */ | 32 | /* There can be padding before archive header */ |
71 | data_align(archive_handle, 4); | 33 | data_align(archive_handle, 4); |
72 | 34 | ||
73 | if (archive_xread_all_eof(archive_handle, (unsigned char*)cpio_header, 110) == 0) { | 35 | if (archive_xread_all_eof(archive_handle, (unsigned char*)cpio_header, 110) == 0) { |
74 | return EXIT_FAILURE; | 36 | goto create_hardlinks; |
75 | } | 37 | } |
76 | archive_handle->offset += 110; | 38 | archive_handle->offset += 110; |
77 | 39 | ||
@@ -81,17 +43,19 @@ char get_header_cpio(archive_handle_t *archive_handle) | |||
81 | bb_error_msg_and_die("unsupported cpio format, use newc or crc"); | 43 | bb_error_msg_and_die("unsupported cpio format, use newc or crc"); |
82 | } | 44 | } |
83 | 45 | ||
84 | { | 46 | sscanf(cpio_header + 6, |
85 | unsigned long tmpsize; | 47 | "%8x" "%8x" "%8x" "%8x" |
86 | sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c", | 48 | "%8x" "%8x" "%8x" /*maj,min:*/ "%16c" |
87 | dummy, &inode, (unsigned int*)&file_header->mode, | 49 | /*rmaj,rmin:*/"%8x" "%8x" "%8x" /*chksum:*/ "%8c", |
88 | (unsigned int*)&file_header->uid, (unsigned int*)&file_header->gid, | 50 | &inode, &mode, &uid, &gid, |
89 | &nlink, &file_header->mtime, &tmpsize, | 51 | &nlink, &mtime, &size, dummy, |
90 | dummy, &major, &minor, &namesize, dummy); | 52 | &major, &minor, &namesize, dummy); |
91 | file_header->size = tmpsize; | 53 | file_header->mode = mode; |
92 | } | 54 | file_header->uid = uid; |
55 | file_header->gid = gid; | ||
56 | file_header->mtime = mtime; | ||
57 | file_header->size = size; | ||
93 | 58 | ||
94 | free(file_header->name); | ||
95 | file_header->name = xzalloc(namesize + 1); | 59 | file_header->name = xzalloc(namesize + 1); |
96 | /* Read in filename */ | 60 | /* Read in filename */ |
97 | xread(archive_handle->src_fd, file_header->name, namesize); | 61 | xread(archive_handle->src_fd, file_header->name, namesize); |
@@ -101,25 +65,9 @@ char get_header_cpio(archive_handle_t *archive_handle) | |||
101 | data_align(archive_handle, 4); | 65 | data_align(archive_handle, 4); |
102 | 66 | ||
103 | if (strcmp(file_header->name, "TRAILER!!!") == 0) { | 67 | if (strcmp(file_header->name, "TRAILER!!!") == 0) { |
104 | /* Always round up */ | 68 | /* Always round up. ">> 9" divides by 512 */ |
105 | printf("%d blocks\n", (int) (archive_handle->offset % 512 ? | 69 | printf("%"OFF_FMT"u blocks\n", (archive_handle->offset + 511) >> 9); |
106 | archive_handle->offset / 512 + 1 : | 70 | goto create_hardlinks; |
107 | archive_handle->offset / 512 | ||
108 | )); | ||
109 | if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */ | ||
110 | hardlinks_t *tmp = saved_hardlinks; | ||
111 | hardlinks_t *oldtmp = NULL; | ||
112 | while (tmp) { | ||
113 | bb_error_msg("%s not created: cannot resolve hardlink", tmp->name); | ||
114 | oldtmp = tmp; | ||
115 | tmp = tmp->next; | ||
116 | free(oldtmp->name); | ||
117 | free(oldtmp); | ||
118 | } | ||
119 | saved_hardlinks = NULL; | ||
120 | pending_hardlinks = 0; | ||
121 | } | ||
122 | return EXIT_FAILURE; | ||
123 | } | 71 | } |
124 | 72 | ||
125 | if (S_ISLNK(file_header->mode)) { | 73 | if (S_ISLNK(file_header->mode)) { |
@@ -130,25 +78,33 @@ char get_header_cpio(archive_handle_t *archive_handle) | |||
130 | } else { | 78 | } else { |
131 | file_header->link_target = NULL; | 79 | file_header->link_target = NULL; |
132 | } | 80 | } |
133 | if (nlink > 1 && !S_ISDIR(file_header->mode)) { | 81 | |
134 | if (file_header->size == 0) { /* Put file on a linked list for later */ | 82 | // TODO: data_extract_all can't deal with hardlinks to non-files... |
135 | hardlinks_t *new = xmalloc(sizeof(hardlinks_t)); | 83 | // (should be !S_ISDIR instead of S_ISREG here) |
84 | |||
85 | if (nlink > 1 && S_ISREG(file_header->mode)) { | ||
86 | hardlinks_t *new = xmalloc(sizeof(*new) + namesize); | ||
87 | new->inode = inode; | ||
88 | new->mode = mode ; | ||
89 | new->mtime = mtime; | ||
90 | new->uid = uid ; | ||
91 | new->gid = gid ; | ||
92 | strcpy(new->name, file_header->name); | ||
93 | /* Put file on a linked list for later */ | ||
94 | if (size == 0) { | ||
136 | new->next = saved_hardlinks; | 95 | new->next = saved_hardlinks; |
137 | new->inode = inode; | ||
138 | /* name current allocated, freed later */ | ||
139 | new->name = file_header->name; | ||
140 | file_header->name = NULL; | ||
141 | saved_hardlinks = new; | 96 | saved_hardlinks = new; |
142 | return EXIT_SUCCESS; /* Skip this one */ | 97 | return EXIT_SUCCESS; /* Skip this one */ |
98 | /* TODO: this breaks cpio -t (it does not show hardlinks) */ | ||
143 | } | 99 | } |
144 | /* Found the file with data in */ | 100 | new->next = saved_hardlinks_created; |
145 | pending_hardlinks = nlink; | 101 | saved_hardlinks_created = new; |
146 | } | 102 | } |
147 | file_header->device = makedev(major, minor); | 103 | file_header->device = makedev(major, minor); |
148 | 104 | ||
149 | if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { | 105 | if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { |
150 | archive_handle->action_data(archive_handle); | 106 | archive_handle->action_data(archive_handle); |
151 | archive_handle->action_header(archive_handle->file_header); | 107 | archive_handle->action_header(file_header); |
152 | } else { | 108 | } else { |
153 | data_skip(archive_handle); | 109 | data_skip(archive_handle); |
154 | } | 110 | } |
@@ -156,6 +112,57 @@ char get_header_cpio(archive_handle_t *archive_handle) | |||
156 | archive_handle->offset += file_header->size; | 112 | archive_handle->offset += file_header->size; |
157 | 113 | ||
158 | free(file_header->link_target); | 114 | free(file_header->link_target); |
115 | free(file_header->name); | ||
116 | file_header->link_target = NULL; | ||
117 | file_header->name = NULL; | ||
159 | 118 | ||
160 | return EXIT_SUCCESS; | 119 | return EXIT_SUCCESS; |
120 | |||
121 | create_hardlinks: | ||
122 | free(file_header->link_target); | ||
123 | free(file_header->name); | ||
124 | |||
125 | while (saved_hardlinks) { | ||
126 | hardlinks_t *cur; | ||
127 | hardlinks_t *make_me = saved_hardlinks; | ||
128 | saved_hardlinks = make_me->next; | ||
129 | |||
130 | memset(file_header, 0, sizeof(*file_header)); | ||
131 | file_header->name = make_me->name; | ||
132 | file_header->mode = make_me->mode; | ||
133 | /*file_header->size = 0;*/ | ||
134 | |||
135 | /* Try to find a file we are hardlinked to */ | ||
136 | cur = saved_hardlinks_created; | ||
137 | while (cur) { | ||
138 | /* TODO: must match maj/min too! */ | ||
139 | if (cur->inode == make_me->inode) { | ||
140 | file_header->link_target = cur->name; | ||
141 | /* link_target != NULL, size = 0: "I am a hardlink" */ | ||
142 | if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) | ||
143 | archive_handle->action_data(archive_handle); | ||
144 | free(make_me); | ||
145 | goto next_link; | ||
146 | } | ||
147 | } | ||
148 | /* Oops... no file with such inode was created... do it now | ||
149 | * (happens when hardlinked files are empty (zero length)) */ | ||
150 | file_header->mtime = make_me->mtime; | ||
151 | file_header->uid = make_me->uid ; | ||
152 | file_header->gid = make_me->gid ; | ||
153 | if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) | ||
154 | archive_handle->action_data(archive_handle); | ||
155 | /* Move to the list of created hardlinked files */ | ||
156 | make_me->next = saved_hardlinks_created; | ||
157 | saved_hardlinks_created = make_me; | ||
158 | next_link: ; | ||
159 | } | ||
160 | |||
161 | while (saved_hardlinks_created) { | ||
162 | hardlinks_t *p = saved_hardlinks_created; | ||
163 | saved_hardlinks_created = p->next; | ||
164 | free(p); | ||
165 | } | ||
166 | |||
167 | return EXIT_FAILURE; /* "No more files to process" */ | ||
161 | } | 168 | } |
diff --git a/archival/libunarchive/seek_by_jump.c b/archival/libunarchive/seek_by_jump.c index 8b5f3e887..5288c1d73 100644 --- a/archival/libunarchive/seek_by_jump.c +++ b/archival/libunarchive/seek_by_jump.c | |||
@@ -9,11 +9,9 @@ | |||
9 | void seek_by_jump(const archive_handle_t *archive_handle, unsigned amount) | 9 | void seek_by_jump(const archive_handle_t *archive_handle, unsigned amount) |
10 | { | 10 | { |
11 | if (lseek(archive_handle->src_fd, (off_t) amount, SEEK_CUR) == (off_t) -1) { | 11 | if (lseek(archive_handle->src_fd, (off_t) amount, SEEK_CUR) == (off_t) -1) { |
12 | #if ENABLE_FEATURE_UNARCHIVE_TAPE | 12 | if (errno == ESPIPE) |
13 | if (errno == ESPIPE) { | ||
14 | seek_by_read(archive_handle, amount); | 13 | seek_by_read(archive_handle, amount); |
15 | } else | 14 | else |
16 | #endif | ||
17 | bb_perror_msg_and_die("seek failure"); | 15 | bb_perror_msg_and_die("seek failure"); |
18 | } | 16 | } |
19 | } | 17 | } |
diff --git a/scripts/defconfig b/scripts/defconfig index c24d85242..26cc8d25d 100644 --- a/scripts/defconfig +++ b/scripts/defconfig | |||
@@ -130,7 +130,6 @@ CONFIG_UNZIP=y | |||
130 | # | 130 | # |
131 | # Common options for cpio and tar | 131 | # Common options for cpio and tar |
132 | # | 132 | # |
133 | CONFIG_FEATURE_UNARCHIVE_TAPE=y | ||
134 | # CONFIG_FEATURE_DEB_TAR_GZ is not set | 133 | # CONFIG_FEATURE_DEB_TAR_GZ is not set |
135 | # CONFIG_FEATURE_DEB_TAR_BZ2 is not set | 134 | # CONFIG_FEATURE_DEB_TAR_BZ2 is not set |
136 | # CONFIG_FEATURE_DEB_TAR_LZMA is not set | 135 | # CONFIG_FEATURE_DEB_TAR_LZMA is not set |