aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277>2006-11-25 23:56:50 +0000
committervda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277>2006-11-25 23:56:50 +0000
commitf709ebefbdf62ba30226a538b19aa4811a8c54b8 (patch)
tree7b6bbba9c73e1e8a2c10d9cafde28ce49125f914
parenta0f78c266b49e283f862a642ede1cc1ed439162a (diff)
downloadbusybox-w32-f709ebefbdf62ba30226a538b19aa4811a8c54b8.tar.gz
busybox-w32-f709ebefbdf62ba30226a538b19aa4811a8c54b8.tar.bz2
busybox-w32-f709ebefbdf62ba30226a538b19aa4811a8c54b8.zip
tar: add support for FEATURE_TAR_GNU_EXTENSIONS so than we can save
long names now. We were able to read such tars, but not create. +275 bytes. Without FEATURE_TAR_GNU_EXTENSIONS: -25 bytes. We still cannot unpack Linux kernels, but not for long ;) git-svn-id: svn://busybox.net/trunk/busybox@16670 69ca8d6d-28ef-0310-b511-8ec308f3f277
-rw-r--r--archival/tar.c123
1 files changed, 98 insertions, 25 deletions
diff --git a/archival/tar.c b/archival/tar.c
index c870d53c2..bf6786f0a 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -169,6 +169,72 @@ static void putOctal(char *cp, int len, off_t value)
169 /* Copy the string to the field */ 169 /* Copy the string to the field */
170 memcpy(cp, tempString, len); 170 memcpy(cp, tempString, len);
171} 171}
172#define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b))
173
174static void chksum_and_xwrite(int fd, struct TarHeader* hp)
175{
176 const unsigned char *cp;
177 int chksum, size;
178
179 strcpy(hp->magic, "ustar ");
180
181 /* Calculate and store the checksum (i.e., the sum of all of the bytes of
182 * the header). The checksum field must be filled with blanks for the
183 * calculation. The checksum field is formatted differently from the
184 * other fields: it has 6 digits, a null, then a space -- rather than
185 * digits, followed by a null like the other fields... */
186 memset(hp->chksum, ' ', sizeof(hp->chksum));
187 cp = (const unsigned char *) hp;
188 chksum = 0;
189 size = sizeof(*hp);
190 do { chksum += *cp++; } while (--size);
191 putOctal(hp->chksum, sizeof(hp->chksum)-1, chksum);
192
193 /* Now write the header out to disk */
194 xwrite(fd, hp, sizeof(*hp));
195}
196
197#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
198static void writeLongname(int fd, int type, const char *name, int dir)
199{
200 static const struct {
201 char mode[8]; /* 100-107 */
202 char uid[8]; /* 108-115 */
203 char gid[8]; /* 116-123 */
204 char size[12]; /* 124-135 */
205 char mtime[12]; /* 136-147 */
206 } prefilled = {
207 "0000000",
208 "0000000",
209 "0000000",
210 "00000000000",
211 "00000000000",
212 };
213 struct TarHeader header;
214 int size;
215
216 dir = !!dir; /* normalize: 0/1 */
217 size = strlen(name) + 1 + dir; /* GNU tar uses strlen+1 */
218 /* + dir: account for possible '/' */
219
220 bzero(&header, sizeof(header));
221 strcpy(header.name, "././@LongLink");
222 memcpy(header.mode, prefilled.mode, sizeof(prefilled));
223 PUT_OCTAL(header.size, size);
224 header.typeflag = type;
225 chksum_and_xwrite(fd, &header);
226
227 /* Write filename[/] and pad the block. */
228 /* dir=0: writes 'name<NUL>', pads */
229 /* dir=1: writes 'name', writes '/<NUL>', pads */
230 dir *= 2;
231 xwrite(fd, name, size - dir);
232 xwrite(fd, "/", dir);
233 size = (-size) & (TAR_BLOCK_SIZE-1);
234 bzero(&header, size);
235 xwrite(fd, &header, size);
236}
237#endif
172 238
173/* Write out a tar header for the specified file/directory/whatever */ 239/* Write out a tar header for the specified file/directory/whatever */
174void BUG_tar_header_size(void); 240void BUG_tar_header_size(void);
@@ -176,25 +242,20 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
176 const char *header_name, const char *fileName, struct stat *statbuf) 242 const char *header_name, const char *fileName, struct stat *statbuf)
177{ 243{
178 struct TarHeader header; 244 struct TarHeader header;
179 const unsigned char *cp;
180 int chksum;
181 int size;
182 245
183 if (sizeof(header) != 512) 246 if (sizeof(header) != 512)
184 BUG_tar_header_size(); 247 BUG_tar_header_size();
185 248
186 bzero(&header, sizeof(struct TarHeader)); 249 bzero(&header, sizeof(struct TarHeader));
187 250
188 safe_strncpy(header.name, header_name, sizeof(header.name)); 251 strncpy(header.name, header_name, sizeof(header.name));
189 252
190#define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b))
191 /* POSIX says to mask mode with 07777. */ 253 /* POSIX says to mask mode with 07777. */
192 PUT_OCTAL(header.mode, statbuf->st_mode & 07777); 254 PUT_OCTAL(header.mode, statbuf->st_mode & 07777);
193 PUT_OCTAL(header.uid, statbuf->st_uid); 255 PUT_OCTAL(header.uid, statbuf->st_uid);
194 PUT_OCTAL(header.gid, statbuf->st_gid); 256 PUT_OCTAL(header.gid, statbuf->st_gid);
195 memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */ 257 memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */
196 PUT_OCTAL(header.mtime, statbuf->st_mtime); 258 PUT_OCTAL(header.mtime, statbuf->st_mtime);
197 strcpy(header.magic, "ustar ");
198 259
199 /* Enter the user and group names */ 260 /* Enter the user and group names */
200 safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname)); 261 safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname));
@@ -205,24 +266,36 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
205 header.typeflag = LNKTYPE; 266 header.typeflag = LNKTYPE;
206 strncpy(header.linkname, tbInfo->hlInfo->name, 267 strncpy(header.linkname, tbInfo->hlInfo->name,
207 sizeof(header.linkname)); 268 sizeof(header.linkname));
269#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
270 /* Write out long linkname if needed */
271 if (header.linkname[sizeof(header.linkname)-1])
272 writeLongname(tbInfo->tarFd, GNULONGLINK,
273 tbInfo->hlInfo->name, 0);
274#endif
208 } else if (S_ISLNK(statbuf->st_mode)) { 275 } else if (S_ISLNK(statbuf->st_mode)) {
209 char *lpath = xreadlink(fileName); 276 char *lpath = xreadlink(fileName);
210 if (!lpath) /* Already printed err msg inside xreadlink() */ 277 if (!lpath) /* Already printed err msg inside xreadlink() */
211 return FALSE; 278 return FALSE;
212 header.typeflag = SYMTYPE; 279 header.typeflag = SYMTYPE;
213 strncpy(header.linkname, lpath, sizeof(header.linkname)); 280 strncpy(header.linkname, lpath, sizeof(header.linkname));
281#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
282 /* Write out long linkname if needed */
283 if (header.linkname[sizeof(header.linkname)-1])
284 writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0);
285#else
214 /* If it is larger than 100 bytes, bail out */ 286 /* If it is larger than 100 bytes, bail out */
215 if (header.linkname[sizeof(header.linkname)-1] /* at least 100? */ 287 if (header.linkname[sizeof(header.linkname)-1]) {
216 && lpath[sizeof(header.linkname)] /* and 101th is also not zero */
217 ) {
218 free(lpath); 288 free(lpath);
219 bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); 289 bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
220 return FALSE; 290 return FALSE;
221 } 291 }
292#endif
222 free(lpath); 293 free(lpath);
223 } else if (S_ISDIR(statbuf->st_mode)) { 294 } else if (S_ISDIR(statbuf->st_mode)) {
224 header.typeflag = DIRTYPE; 295 header.typeflag = DIRTYPE;
225 strncat(header.name, "/", sizeof(header.name)); 296 /* Append '/' only if there is a space for it */
297 if (!header.name[sizeof(header.name)-1])
298 header.name[strlen(header.name)] = '/';
226 } else if (S_ISCHR(statbuf->st_mode)) { 299 } else if (S_ISCHR(statbuf->st_mode)) {
227 header.typeflag = CHRTYPE; 300 header.typeflag = CHRTYPE;
228 PUT_OCTAL(header.devmajor, major(statbuf->st_rdev)); 301 PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
@@ -247,22 +320,17 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
247 bb_error_msg("%s: unknown file type", fileName); 320 bb_error_msg("%s: unknown file type", fileName);
248 return FALSE; 321 return FALSE;
249 } 322 }
250#undef PUT_OCTAL
251 323
252 /* Calculate and store the checksum (i.e., the sum of all of the bytes of 324#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
253 * the header). The checksum field must be filled with blanks for the 325 /* Write out long name if needed */
254 * calculation. The checksum field is formatted differently from the 326 /* (we, like GNU tar, output long linkname *before* long name) */
255 * other fields: it has [6] digits, a null, then a space -- rather than 327 if (header.name[sizeof(header.name)-1])
256 * digits, followed by a null like the other fields... */ 328 writeLongname(tbInfo->tarFd, GNULONGNAME,
257 memset(header.chksum, ' ', sizeof(header.chksum)); 329 header_name, S_ISDIR(statbuf->st_mode));
258 cp = (const unsigned char *) &header; 330#endif
259 chksum = 0;
260 size = sizeof(struct TarHeader);
261 do { chksum += *cp++; } while (--size);
262 putOctal(header.chksum, sizeof(header.chksum)-1, chksum);
263 331
264 /* Now write the header out to disk */ 332 /* Now write the header out to disk */
265 xwrite(tbInfo->tarFd, &header, sizeof(struct TarHeader)); 333 chksum_and_xwrite(tbInfo->tarFd, &header);
266 334
267 /* Now do the verbose thing (or not) */ 335 /* Now do the verbose thing (or not) */
268 if (tbInfo->verboseFlag) { 336 if (tbInfo->verboseFlag) {
@@ -272,7 +340,9 @@ static int writeTarHeader(struct TarBallInfo *tbInfo,
272 vbFd = stderr; 340 vbFd = stderr;
273 /* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */ 341 /* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */
274 /* We don't have such excesses here: for us "v" == "vv" */ 342 /* We don't have such excesses here: for us "v" == "vv" */
275 fprintf(vbFd, "%s\n", header.name); 343 /* '/' is probably a GNUism */
344 fprintf(vbFd, "%s%s\n", header_name,
345 S_ISDIR(statbuf->st_mode) ? "/" : "");
276 } 346 }
277 347
278 return TRUE; 348 return TRUE;
@@ -352,10 +422,12 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf,
352 header_name++; 422 header_name++;
353 } 423 }
354 424
425#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS
355 if (strlen(fileName) >= NAME_SIZE) { 426 if (strlen(fileName) >= NAME_SIZE) {
356 bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); 427 bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
357 return TRUE; 428 return TRUE;
358 } 429 }
430#endif
359 431
360 if (header_name[0] == '\0') 432 if (header_name[0] == '\0')
361 return TRUE; 433 return TRUE;
@@ -384,7 +456,8 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf,
384 456
385 /* write the file to the archive */ 457 /* write the file to the archive */
386 readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); 458 readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
387 if (readSize != statbuf->st_size) { 459 /* readSize < 0 means that error was already reported */
460 if (readSize != statbuf->st_size && readSize >= 0) {
388 /* Deadly. We record size into header first, */ 461 /* Deadly. We record size into header first, */
389 /* and then write out file. If file shrinks in between, */ 462 /* and then write out file. If file shrinks in between, */
390 /* tar will be corrupted. So bail out. */ 463 /* tar will be corrupted. So bail out. */