diff --git a/Config.h b/Config.h index a86f20a3d..b92540b3b 100644 --- a/Config.h +++ b/Config.h @@ -19,6 +19,7 @@ #define BB_CLEAR //#define BB_CMP #define BB_CP +//#define BB_CPIO #define BB_CUT #define BB_DATE //#define BB_DC diff --git a/applets.h b/applets.h index 0fd89c71a..88aec8ad1 100644 --- a/applets.h +++ b/applets.h @@ -83,6 +83,9 @@ #ifdef BB_CP APPLET(cp, cp_main, _BB_DIR_BIN) #endif +#ifdef BB_CPIO + APPLET(cpio, cpio_main, _BB_DIR_BIN) +#endif #ifdef BB_CUT APPLET(cut, cut_main, _BB_DIR_USR_BIN) #endif diff --git a/applets/usage.h b/applets/usage.h index 9fd3a2e1c..51a06b977 100644 --- a/applets/usage.h +++ b/applets/usage.h @@ -129,6 +129,18 @@ "\t-f\tforce (implied; ignored) - always set\n" \ "\t-R\tCopies directories recursively" +#define cpio_trivial_usage \ + "-[dimtuv][F cpiofile]" +#define cpio_full_usage \ + "Extract or list files from a cpio archive\n" \ + "Main operation mode:\n" \ + "\td\t\tmake directories (assumed)\n" \ + "\ti\t\textract\n" \ + "\tm\t\tpreserve time\n" \ + "\tt\t\tlist\n" \ + "\tu\t\tunconditional (assumed)\t" \ + "\tF\t\tinput from file\t" + #define cut_trivial_usage \ "[OPTION]... [FILE]..." #define cut_full_usage \ diff --git a/ar.c b/ar.c index fd98d86f5..09a4a8894 100644 --- a/ar.c +++ b/ar.c @@ -34,7 +34,7 @@ extern int ar_main(int argc, char **argv) FILE *src_stream = NULL; char **extract_names = NULL; char ar_magic[8]; - int extract_function = 0; + int extract_function = extract_unconditional; int opt; int num_of_entries = 0; extern off_t archive_offset; diff --git a/archival/ar.c b/archival/ar.c index fd98d86f5..09a4a8894 100644 --- a/archival/ar.c +++ b/archival/ar.c @@ -34,7 +34,7 @@ extern int ar_main(int argc, char **argv) FILE *src_stream = NULL; char **extract_names = NULL; char ar_magic[8]; - int extract_function = 0; + int extract_function = extract_unconditional; int opt; int num_of_entries = 0; extern off_t archive_offset; diff --git a/archival/cpio.c b/archival/cpio.c new file mode 100644 index 000000000..ecd6f534a --- /dev/null +++ b/archival/cpio.c @@ -0,0 +1,93 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cpio implementation for busybox + * + * Copyright (C) 2001 by Glenn McGrath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Limitations: + * Doesn't check CRC's + * Only supports new ASCII and CRC formats + * Doesnt support hard links + * + */ +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int cpio_main(int argc, char **argv) +{ + FILE *src_stream = stdin; + char **extract_names = NULL; + int extract_function = 0; + int num_of_entries = 0; + int opt = 0; + mode_t oldmask = 0; + + while ((opt = getopt(argc, argv, "idmuvtF:")) != -1) { + switch (opt) { + case 'i': // extract + extract_function |= extract_all_to_fs; + break; + case 'd': // create directories + extract_function |= extract_create_dirs; + oldmask = umask(077); /* Make create_path act like GNU cpio */ + break; + case 'm': // preserve modification time + extract_function |= extract_preserve_date; + break; + case 'v': // verbosly list files + extract_function |= extract_verbose_list; + break; + case 'u': // unconditional + extract_function |= extract_unconditional; + break; + case 't': // list files + extract_function |= extract_list; + break; + case 'F': + src_stream = xfopen(optarg, "r"); + break; + default: + show_usage(); + } + } + + if (extract_function & extract_all_to_fs && extract_function & extract_list) { + extract_function ^= extract_all_to_fs; /* If specify t, don't extract*/ + } + + if (extract_function & extract_all_to_fs && extract_function & extract_verbose_list) { /* The meaning of v changes on extract */ + extract_function ^= extract_verbose_list; + extract_function |= extract_list; + } + + extract_names = malloc(4); + while (optind < argc) { + num_of_entries++; + *extract_names = realloc(*extract_names, num_of_entries); + extract_names[num_of_entries - 1] = xstrdup(argv[optind]); + optind++; + } + + unarchive(src_stream, &get_header_cpio, extract_function, "./", extract_names); + if (oldmask) umask(oldmask); /* Restore umask if we changed it */ + return EXIT_SUCCESS; +} + diff --git a/archival/dpkg_deb.c b/archival/dpkg_deb.c index 77172b0f6..b1cbb1bbc 100644 --- a/archival/dpkg_deb.c +++ b/archival/dpkg_deb.c @@ -26,7 +26,7 @@ extern int dpkg_deb_main(int argc, char **argv) char *output_buffer = NULL; int opt = 0; int arg_type = 0; - int deb_extract_funct = extract_create_dirs; + int deb_extract_funct = extract_create_dirs | extract_unconditional; const int arg_type_prefix = 1; const int arg_type_field = 2; @@ -92,6 +92,7 @@ extern int dpkg_deb_main(int argc, char **argv) strcat(prefix, "/"); } } + mkdir(prefix, 0777); } if (arg_type == arg_type_filename) { diff --git a/cpio.c b/cpio.c new file mode 100644 index 000000000..ecd6f534a --- /dev/null +++ b/cpio.c @@ -0,0 +1,93 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini cpio implementation for busybox + * + * Copyright (C) 2001 by Glenn McGrath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Limitations: + * Doesn't check CRC's + * Only supports new ASCII and CRC formats + * Doesnt support hard links + * + */ +#include +#include +#include +#include +#include +#include "busybox.h" + +extern int cpio_main(int argc, char **argv) +{ + FILE *src_stream = stdin; + char **extract_names = NULL; + int extract_function = 0; + int num_of_entries = 0; + int opt = 0; + mode_t oldmask = 0; + + while ((opt = getopt(argc, argv, "idmuvtF:")) != -1) { + switch (opt) { + case 'i': // extract + extract_function |= extract_all_to_fs; + break; + case 'd': // create directories + extract_function |= extract_create_dirs; + oldmask = umask(077); /* Make create_path act like GNU cpio */ + break; + case 'm': // preserve modification time + extract_function |= extract_preserve_date; + break; + case 'v': // verbosly list files + extract_function |= extract_verbose_list; + break; + case 'u': // unconditional + extract_function |= extract_unconditional; + break; + case 't': // list files + extract_function |= extract_list; + break; + case 'F': + src_stream = xfopen(optarg, "r"); + break; + default: + show_usage(); + } + } + + if (extract_function & extract_all_to_fs && extract_function & extract_list) { + extract_function ^= extract_all_to_fs; /* If specify t, don't extract*/ + } + + if (extract_function & extract_all_to_fs && extract_function & extract_verbose_list) { /* The meaning of v changes on extract */ + extract_function ^= extract_verbose_list; + extract_function |= extract_list; + } + + extract_names = malloc(4); + while (optind < argc) { + num_of_entries++; + *extract_names = realloc(*extract_names, num_of_entries); + extract_names[num_of_entries - 1] = xstrdup(argv[optind]); + optind++; + } + + unarchive(src_stream, &get_header_cpio, extract_function, "./", extract_names); + if (oldmask) umask(oldmask); /* Restore umask if we changed it */ + return EXIT_SUCCESS; +} + diff --git a/dpkg_deb.c b/dpkg_deb.c index 77172b0f6..b1cbb1bbc 100644 --- a/dpkg_deb.c +++ b/dpkg_deb.c @@ -26,7 +26,7 @@ extern int dpkg_deb_main(int argc, char **argv) char *output_buffer = NULL; int opt = 0; int arg_type = 0; - int deb_extract_funct = extract_create_dirs; + int deb_extract_funct = extract_create_dirs | extract_unconditional; const int arg_type_prefix = 1; const int arg_type_field = 2; @@ -92,6 +92,7 @@ extern int dpkg_deb_main(int argc, char **argv) strcat(prefix, "/"); } } + mkdir(prefix, 0777); } if (arg_type == arg_type_filename) { diff --git a/include/applets.h b/include/applets.h index 0fd89c71a..88aec8ad1 100644 --- a/include/applets.h +++ b/include/applets.h @@ -83,6 +83,9 @@ #ifdef BB_CP APPLET(cp, cp_main, _BB_DIR_BIN) #endif +#ifdef BB_CPIO + APPLET(cpio, cpio_main, _BB_DIR_BIN) +#endif #ifdef BB_CUT APPLET(cut, cut_main, _BB_DIR_USR_BIN) #endif diff --git a/include/usage.h b/include/usage.h index 9fd3a2e1c..51a06b977 100644 --- a/include/usage.h +++ b/include/usage.h @@ -129,6 +129,18 @@ "\t-f\tforce (implied; ignored) - always set\n" \ "\t-R\tCopies directories recursively" +#define cpio_trivial_usage \ + "-[dimtuv][F cpiofile]" +#define cpio_full_usage \ + "Extract or list files from a cpio archive\n" \ + "Main operation mode:\n" \ + "\td\t\tmake directories (assumed)\n" \ + "\ti\t\textract\n" \ + "\tm\t\tpreserve time\n" \ + "\tt\t\tlist\n" \ + "\tu\t\tunconditional (assumed)\t" \ + "\tF\t\tinput from file\t" + #define cut_trivial_usage \ "[OPTION]... [FILE]..." #define cut_full_usage \ diff --git a/libbb/unarchive.c b/libbb/unarchive.c index 635dcae35..20609ded7 100644 --- a/libbb/unarchive.c +++ b/libbb/unarchive.c @@ -119,10 +119,11 @@ char *extract_archive(FILE *src_stream, FILE *out_stream, const file_header_t *f } } else if (function & extract_all_to_fs) { -#if 0 struct stat oldfile; - if ( (S_ISLNK(file_entry->mode) ? lstat (full_name, &oldfile) : stat (full_name, &oldfile)) == 0) { /* The file already exists */ - if (function & extract_unconditional || oldfile.st_mtime < file_entry->mtime) { + int stat_res; + stat_res = lstat (full_name, &oldfile); + if (stat_res == 0) { /* The file already exists */ + if ((function & extract_unconditional) || (oldfile.st_mtime < file_entry->mtime)) { if (!S_ISDIR(oldfile.st_mode)) { unlink(full_name); /* Directories might not be empty etc */ } @@ -134,7 +135,6 @@ char *extract_archive(FILE *src_stream, FILE *out_stream, const file_header_t *f return (NULL); } } -#endif switch(file_entry->mode & S_IFMT) { case S_IFREG: if (file_entry->link_name) { /* Found a cpio hard link */ @@ -153,10 +153,11 @@ char *extract_archive(FILE *src_stream, FILE *out_stream, const file_header_t *f } break; case S_IFDIR: - /* Use make_directory instead of mkdir in case prefix path hasn't been created */ - if (function & extract_create_dirs) { - if (make_directory(full_name, file_entry->mode, FILEUTILS_RECUR) < 0) { - return NULL; + if ((function & extract_create_dirs) && (stat_res != 0)) { + /* Make sure the prefix component of full_name was create + * in applet before getting here*/ + if (mkdir(full_name, file_entry->mode) < 0) { + perror_msg("extract_archive: "); } } break; @@ -335,14 +336,38 @@ void *get_header_ar(FILE *src_stream) #endif #ifdef L_get_header_cpio +struct hardlinks { + file_header_t *entry; + int inode; + struct hardlinks *next; +}; + void *get_header_cpio(FILE *src_stream) { file_header_t *cpio_entry = NULL; char cpio_header[110]; - char dummy[14]; int namesize; - int major, minor, nlink; + char dummy[16]; + int major, minor, nlink, inode; + static struct hardlinks *saved_hardlinks = NULL; + static int pending_hardlinks = 0; + if (pending_hardlinks) { /* Deal with any pending hardlinks */ + struct hardlinks *tmp = saved_hardlinks, *oldtmp = NULL; + while (tmp) { + if (tmp->entry->link_name) { /* Found a hardlink ready to be extracted */ + cpio_entry = tmp->entry; + if (oldtmp) oldtmp->next = tmp->next; /* Remove item from linked list */ + else saved_hardlinks = tmp->next; + free(tmp); + return (cpio_entry); + } + oldtmp = tmp; + tmp = tmp->next; + } + pending_hardlinks = 0; /* No more pending hardlinks, read next file entry */ + } + /* There can be padding before archive header */ seek_sub_file(src_stream, (4 - (archive_offset % 4)) % 4); if (fread(cpio_header, 1, 110, src_stream) == 110) { @@ -356,8 +381,8 @@ void *get_header_cpio(FILE *src_stream) /* Doesnt do the crc check yet */ case '1': /* "newc" header format */ cpio_entry = (file_header_t *) xcalloc(1, sizeof(file_header_t)); - sscanf(cpio_header, "%14c%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c", - dummy, &cpio_entry->mode, &cpio_entry->uid, &cpio_entry->gid, + sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c", + dummy, &inode, &cpio_entry->mode, &cpio_entry->uid, &cpio_entry->gid, &nlink, &cpio_entry->mtime, &cpio_entry->size, dummy, &major, &minor, &namesize, dummy); @@ -368,6 +393,19 @@ void *get_header_cpio(FILE *src_stream) seek_sub_file(src_stream, (4 - (archive_offset % 4)) % 4); if (strcmp(cpio_entry->name, "TRAILER!!!") == 0) { printf("%d blocks\n", (int) (archive_offset % 512 ? (archive_offset / 512) + 1 : archive_offset / 512)); /* Always round up */ + if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */ + struct hardlinks *tmp = saved_hardlinks, *oldtmp = NULL; + while (tmp) { + error_msg("%s not created: cannot resolve hardlink", tmp->entry->name); + oldtmp = tmp; + tmp = tmp->next; + free (oldtmp->entry->name); + free (oldtmp->entry); + free (oldtmp); + } + saved_hardlinks = NULL; + pending_hardlinks = 0; + } return(NULL); } @@ -376,9 +414,26 @@ void *get_header_cpio(FILE *src_stream) fread(cpio_entry->link_name, 1, cpio_entry->size, src_stream); archive_offset += cpio_entry->size; } - if (nlink > 1 && !S_ISDIR(cpio_entry->mode) && cpio_entry->size == 0) { - error_msg("%s not extracted: Cannot handle hard links yet", cpio_entry->name); + if (nlink > 1 && !S_ISDIR(cpio_entry->mode)) { + if (cpio_entry->size == 0) { /* Put file on a linked list for later */ + struct hardlinks *new = xmalloc(sizeof(struct hardlinks)); + new->next = saved_hardlinks; + new->inode = inode; + new->entry = cpio_entry; + saved_hardlinks = new; return(get_header_cpio(src_stream)); /* Recurse to next file */ + } else { /* Found the file with data in */ + struct hardlinks *tmp = saved_hardlinks; + pending_hardlinks = 1; + while (tmp) { + if (tmp->inode == inode) { + tmp->entry->link_name = xstrdup(cpio_entry->name); + nlink--; + } + tmp = tmp->next; + } + if (nlink > 1) error_msg("error resolving hardlink: did you create the archive with GNU cpio 2.0-2.2?"); + } } cpio_entry->device = (major << 8) | minor; break; diff --git a/usage.h b/usage.h index 9fd3a2e1c..51a06b977 100644 --- a/usage.h +++ b/usage.h @@ -129,6 +129,18 @@ "\t-f\tforce (implied; ignored) - always set\n" \ "\t-R\tCopies directories recursively" +#define cpio_trivial_usage \ + "-[dimtuv][F cpiofile]" +#define cpio_full_usage \ + "Extract or list files from a cpio archive\n" \ + "Main operation mode:\n" \ + "\td\t\tmake directories (assumed)\n" \ + "\ti\t\textract\n" \ + "\tm\t\tpreserve time\n" \ + "\tt\t\tlist\n" \ + "\tu\t\tunconditional (assumed)\t" \ + "\tF\t\tinput from file\t" + #define cut_trivial_usage \ "[OPTION]... [FILE]..." #define cut_full_usage \