void chomp(char *s) { char *lc = last_char_is(s, '\n'); if(lc) *lc = 0; }
/* * "/" -> "/" * "abc" -> "abc" * "abc/def" -> "def" * "abc/def/" -> "def" !! */ char* FAST_FUNC bb_get_last_path_component_strip(char *path) { char *slash = last_char_is(path, '/'); if (slash) while (*slash == '/' && slash != path) *slash-- = '\0'; return bb_get_last_path_component_nostrip(path); }
char *concat_path_file(const char *path, const char *filename) { char *lc; if (!path) path = ""; lc = last_char_is(path, '/'); while (*filename == '/') filename++; return bb_xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename); }
int modinfo_main(int argc UNUSED_PARAM, char **argv) { struct modinfo_env env; char name[MODULE_NAME_LEN]; struct utsname uts; parser_t *parser; char *colon, *tokens[2]; unsigned opts; unsigned i; env.field = NULL; opt_complementary = "-1"; /* minimum one param */ opts = getopt32(argv, "fdalvpF:0", &env.field); env.tags = opts & OPT_TAGS ? opts & OPT_TAGS : OPT_TAGS; argv += optind; uname(&uts); parser = config_open2( xasprintf("%s/%s/%s", CONFIG_DEFAULT_MODULES_DIR, uts.release, CONFIG_DEFAULT_DEPMOD_FILE), fopen_for_read ); if (!parser) { parser = config_open2( xasprintf("%s/%s", CONFIG_DEFAULT_MODULES_DIR, CONFIG_DEFAULT_DEPMOD_FILE), xfopen_for_read ); } while (config_read(parser, tokens, 2, 1, "# \t", PARSE_NORMAL)) { colon = last_char_is(tokens[0], ':'); if (colon == NULL) continue; *colon = '\0'; filename2modname(tokens[0], name); for (i = 0; argv[i]; i++) { if (fnmatch(argv[i], name, 0) == 0) { modinfo(tokens[0], uts.release, &env); argv[i] = (char *) ""; } } } if (ENABLE_FEATURE_CLEAN_UP) config_close(parser); for (i = 0; argv[i]; i++) { if (argv[i][0]) { modinfo(argv[i], uts.release, &env); } } return 0; }
char *concat_path_file(const char *path, const char *filename) { char *outbuf; char *lc; if (!path) path=""; lc = last_char_is(path, '/'); while (*filename == '/') filename++; bb_xasprintf(&outbuf, "%s%s%s", path, (lc==NULL)? "/" : "", filename); return outbuf; }
extern char *concat_path_file(const char *path, const char *filename) { char *outbuf; char *lc; if (!path) path=""; lc = last_char_is(path, '/'); while (*filename == '/') filename++; outbuf = xmalloc(strlen(path)+strlen(filename)+1+(lc==NULL)); sprintf(outbuf, "%s%s%s", path, (lc==NULL)? "/" : "", filename); return outbuf; }
/* concatenate path and file name to new allocation buffer, * not adding '/' if path name already has '/' */ static char *concat_path_file(const char *path, const char *filename) { char *lc, *str; if (!path) path = ""; lc = last_char_is(path, '/'); while (*filename == '/') filename++; str = xmalloc(strlen(path) + (lc==0 ? 1 : 0) + strlen(filename) + 1); sprintf(str, "%s%s%s", path, (lc==NULL ? "/" : ""), filename); return str; }
// check for an address inside angle brackets, if not found fall back to normal static char *angle_address(char *str) { char *s, *e; trim(str); e = last_char_is(str, '>'); if (e) { s = strrchr(str, '<'); if (s) { *e = '\0'; str = s + 1; } } return sane_address(str); }
static llist_t *append_file_list_to_list(llist_t *list) { FILE *src_stream; char *line; llist_t *newlist = NULL; while (list) { src_stream = xfopen_for_read(llist_pop(&list)); while ((line = xmalloc_fgetline(src_stream)) != NULL) { /* kill trailing '/' unless the string is just "/" */ char *cp = last_char_is(line, '/'); if (cp > line) *cp = '\0'; llist_add_to(&newlist, line); } fclose(src_stream); } return newlist; }
static void add_user_to_group(char **args, const char *path, FILE *(*fopen_func)(const char *fileName, const char *mode)) { char *line; int len = strlen(args[1]); llist_t *plist = NULL; FILE *group_file; group_file = fopen_func(path, "r"); if (!group_file) return; while ((line = xmalloc_fgetline(group_file)) != NULL) { /* Find the group */ if (!strncmp(line, args[1], len) && line[len] == ':' ) { /* Add the new user */ line = xasprintf("%s%s%s", line, last_char_is(line, ':') ? "" : ",", args[0]); } llist_add_to_end(&plist, line); } if (ENABLE_FEATURE_CLEAN_UP) { fclose(group_file); group_file = fopen_func(path, "w"); while ((line = llist_pop(&plist))) { if (group_file) fprintf(group_file, "%s\n", line); free(line); } if (group_file) fclose(group_file); } else { group_file = fopen_func(path, "w"); if (group_file) while ((line = llist_pop(&plist))) fprintf(group_file, "%s\n", line); } }
static llist_t *append_file_list_to_list(llist_t *list) { FILE *src_stream; llist_t *cur = list; llist_t *tmp; char *line; llist_t *newlist = NULL; while (cur) { src_stream = xfopen(cur->data, "r"); tmp = cur; cur = cur->link; free(tmp); while ((line = xmalloc_getline(src_stream)) != NULL) { /* kill trailing '/' unless the string is just "/" */ char *cp = last_char_is(line, '/'); if (cp > line) *cp = '\0'; llist_add_to(&newlist, line); } fclose(src_stream); } return newlist; }
int tar_main(int argc UNUSED_PARAM, char **argv) { char FAST_FUNC (*get_header_ptr)(archive_handle_t *) = get_header_tar; archive_handle_t *tar_handle; char *base_dir = NULL; const char *tar_filename = "-"; unsigned opt; int verboseFlag = 0; #if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM llist_t *excludes = NULL; #endif /* Initialise default values */ tar_handle = init_handle(); tar_handle->ah_flags = ARCHIVE_CREATE_LEADING_DIRS | ARCHIVE_PRESERVE_DATE | ARCHIVE_EXTRACT_UNCONDITIONAL; /* Apparently only root's tar preserves perms (see bug 3844) */ if (getuid() != 0) tar_handle->ah_flags |= ARCHIVE_NOPRESERVE_PERM; /* Prepend '-' to the first argument if required */ opt_complementary = "--:" // first arg is options "tt:vv:" // count -t,-v "?:" // bail out with usage instead of error return "X::T::" // cumulative lists #if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM "\xff::" // cumulative lists for --exclude #endif IF_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive IF_NOT_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive #if ENABLE_FEATURE_TAR_LONG_OPTIONS applet_long_options = tar_longopts; #endif opt = getopt32(argv, "txC:f:Opvk" IF_FEATURE_TAR_CREATE( "ch" ) IF_FEATURE_SEAMLESS_BZ2( "j" ) IF_FEATURE_SEAMLESS_LZMA("a" ) IF_FEATURE_TAR_FROM( "T:X:") IF_FEATURE_SEAMLESS_GZ( "z" ) IF_FEATURE_SEAMLESS_Z( "Z" ) , &base_dir // -C dir , &tar_filename // -f filename IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T IF_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X #if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM , &excludes // --exclude #endif , &verboseFlag // combined count for -t and -v , &verboseFlag // combined count for -t and -v ); argv += optind; if (verboseFlag) tar_handle->action_header = header_verbose_list; if (verboseFlag == 1) tar_handle->action_header = header_list; if (opt & OPT_EXTRACT) tar_handle->action_data = data_extract_all; if (opt & OPT_2STDOUT) tar_handle->action_data = data_extract_to_stdout; if (opt & OPT_KEEP_OLD) tar_handle->ah_flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL; if (opt & OPT_NOPRESERVE_OWN) tar_handle->ah_flags |= ARCHIVE_NOPRESERVE_OWN; if (opt & OPT_NOPRESERVE_PERM) tar_handle->ah_flags |= ARCHIVE_NOPRESERVE_PERM; if (opt & OPT_NUMERIC_OWNER) tar_handle->ah_flags |= ARCHIVE_NUMERIC_OWNER; if (opt & OPT_GZIP) get_header_ptr = get_header_tar_gz; if (opt & OPT_BZIP2) get_header_ptr = get_header_tar_bz2; if (opt & OPT_LZMA) get_header_ptr = get_header_tar_lzma; if (opt & OPT_COMPRESS) get_header_ptr = get_header_tar_Z; #if ENABLE_FEATURE_TAR_FROM tar_handle->reject = append_file_list_to_list(tar_handle->reject); #if ENABLE_FEATURE_TAR_LONG_OPTIONS /* Append excludes to reject */ while (excludes) { llist_t *next = excludes->link; excludes->link = tar_handle->reject; tar_handle->reject = excludes; excludes = next; } #endif tar_handle->accept = append_file_list_to_list(tar_handle->accept); #endif /* Setup an array of filenames to work with */ /* TODO: This is the same as in ar, separate function ? */ while (*argv) { /* kill trailing '/' unless the string is just "/" */ char *cp = last_char_is(*argv, '/'); if (cp > *argv) *cp = '\0'; llist_add_to_end(&tar_handle->accept, *argv); argv++; } if (tar_handle->accept || tar_handle->reject) tar_handle->filter = filter_accept_reject_list; /* Open the tar file */ { FILE *tar_stream; int flags; if (opt & OPT_CREATE) { /* Make sure there is at least one file to tar up. */ if (tar_handle->accept == NULL) bb_error_msg_and_die("empty archive"); tar_stream = stdout; /* Mimicking GNU tar 1.15.1: */ flags = O_WRONLY | O_CREAT | O_TRUNC; } else { tar_stream = stdin; flags = O_RDONLY; } if (LONE_DASH(tar_filename)) { tar_handle->src_fd = fileno(tar_stream); tar_handle->seek = seek_by_read; } else { if (ENABLE_FEATURE_TAR_AUTODETECT && flags == O_RDONLY) { get_header_ptr = get_header_tar; tar_handle->src_fd = open_zipped(tar_filename); if (tar_handle->src_fd < 0) bb_perror_msg_and_die("can't open '%s'", tar_filename); } else { tar_handle->src_fd = xopen(tar_filename, flags); } } } if (base_dir) xchdir(base_dir); #ifdef CHECK_FOR_CHILD_EXITCODE /* We need to know whether child (gzip/bzip/etc) exits abnormally */ signal(SIGCHLD, handle_SIGCHLD); #endif /* create an archive */ if (opt & OPT_CREATE) { #if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 int zipMode = 0; if (ENABLE_FEATURE_SEAMLESS_GZ && (opt & OPT_GZIP)) zipMode = 1; if (ENABLE_FEATURE_SEAMLESS_BZ2 && (opt & OPT_BZIP2)) zipMode = 2; #endif /* NB: writeTarFile() closes tar_handle->src_fd */ return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE, tar_handle->accept, tar_handle->reject, zipMode); } while (get_header_ptr(tar_handle) == EXIT_SUCCESS) continue; /* Check that every file that should have been extracted was */ while (tar_handle->accept) { if (!find_list_entry(tar_handle->reject, tar_handle->accept->data) && !find_list_entry(tar_handle->passed, tar_handle->accept->data) ) { bb_error_msg_and_die("%s: not found in archive", tar_handle->accept->data); } tar_handle->accept = tar_handle->accept->link; } if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */) close(tar_handle->src_fd); return EXIT_SUCCESS; }
extern char get_header_tar(archive_handle_t *archive_handle) { file_header_t *file_header = archive_handle->file_header; union { /* ustar header, Posix 1003.1 */ unsigned char raw[512]; struct { char name[100]; /* 0-99 */ char mode[8]; /* 100-107 */ char uid[8]; /* 108-115 */ char gid[8]; /* 116-123 */ char size[12]; /* 124-135 */ char mtime[12]; /* 136-147 */ char chksum[8]; /* 148-155 */ char typeflag; /* 156-156 */ char linkname[100]; /* 157-256 */ char magic[6]; /* 257-262 */ char version[2]; /* 263-264 */ char uname[32]; /* 265-296 */ char gname[32]; /* 297-328 */ char devmajor[8]; /* 329-336 */ char devminor[8]; /* 337-344 */ char prefix[155]; /* 345-499 */ char padding[12]; /* 500-512 */ } formated; } tar; long sum = 0; long i; /* Align header */ data_align(archive_handle, 512); if (bb_full_read(archive_handle->src_fd, tar.raw, 512) != 512) { /* Assume end of file */ return(EXIT_FAILURE); } archive_handle->offset += 512; /* If there is no filename its an empty header */ if (tar.formated.name[0] == 0) { return(EXIT_SUCCESS); } /* Check header has valid magic, "ustar" is for the proper tar * 0's are for the old tar format */ if (strncmp(tar.formated.magic, "ustar", 5) != 0) { #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0) #endif bb_error_msg_and_die("Invalid tar magic"); } /* Do checksum on headers */ for (i = 0; i < 148 ; i++) { sum += tar.raw[i]; } sum += ' ' * 8; for (i = 156; i < 512 ; i++) { sum += tar.raw[i]; } if (sum != strtol(tar.formated.chksum, NULL, 8)) { bb_error_msg("Invalid tar header checksum"); return(EXIT_FAILURE); } #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS if (longname) { file_header->name = longname; longname = NULL; } else if (linkname) { file_header->name = linkname; linkname = NULL; } else #endif if (tar.formated.prefix[0] == 0) { file_header->name = strdup(tar.formated.name); } else { file_header->name = concat_path_file(tar.formated.prefix, tar.formated.name); } file_header->uid = strtol(tar.formated.uid, NULL, 8); file_header->gid = strtol(tar.formated.gid, NULL, 8); file_header->size = strtol(tar.formated.size, NULL, 8); file_header->mtime = strtol(tar.formated.mtime, NULL, 8); file_header->link_name = (tar.formated.linkname[0] != '\0') ? bb_xstrdup(tar.formated.linkname) : NULL; file_header->device = (dev_t) ((strtol(tar.formated.devmajor, NULL, 8) << 8) + strtol(tar.formated.devminor, NULL, 8)); /* Set bits 0-11 of the files mode */ file_header->mode = 07777 & strtol(tar.formated.mode, NULL, 8); /* Set bits 12-15 of the files mode */ switch (tar.formated.typeflag) { /* busybox identifies hard links as being regular files with 0 size and a link name */ case '1': file_header->mode |= S_IFREG; break; case 'x': case 'g': bb_error_msg_and_die("pax is not tar"); break; case '7': /* Reserved for high performance files, treat as normal file */ case 0: case '0': #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY if (last_char_is(file_header->name, '/')) { file_header->mode |= S_IFDIR; } else #endif file_header->mode |= S_IFREG; break; case '2': file_header->mode |= S_IFLNK; break; case '3': file_header->mode |= S_IFCHR; break; case '4': file_header->mode |= S_IFBLK; break; case '5': file_header->mode |= S_IFDIR; break; case '6': file_header->mode |= S_IFIFO; break; #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS case 'L': { longname = xmalloc(file_header->size + 1); archive_xread_all(archive_handle, longname, file_header->size); longname[file_header->size] = '\0'; archive_handle->offset += file_header->size; return(get_header_tar(archive_handle)); } case 'K': { linkname = xmalloc(file_header->size + 1); archive_xread_all(archive_handle, linkname, file_header->size); linkname[file_header->size] = '\0'; archive_handle->offset += file_header->size; file_header->name = linkname; return(get_header_tar(archive_handle)); } case 'D': /* GNU dump dir */ case 'M': /* Continuation of multi volume archive*/ case 'N': /* Old GNU for names > 100 characters */ case 'S': /* Sparse file */ case 'V': /* Volume header */ bb_error_msg("Ignoring GNU extension type %c", tar.formated.typeflag); #endif default: bb_error_msg("Unknown typeflag: 0x%x", tar.formated.typeflag); } { /* Strip trailing '/' in directories */ /* Must be done after mode is set as '/' is used to check if its a directory */ char *tmp = last_char_is(file_header->name, '/'); if (tmp) { *tmp = '\0'; } } if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { archive_handle->action_header(archive_handle->file_header); archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; archive_handle->action_data(archive_handle); archive_handle->passed = llist_add_to(archive_handle->passed, file_header->name); } else { data_skip(archive_handle); } archive_handle->offset += file_header->size; free(file_header->link_name); return(EXIT_SUCCESS); }
/* tiny recursive du */ static long du(const char * const filename) { struct stat statbuf; long sum; if (lstat(filename, &statbuf) != 0) { bb_perror_msg("%s", filename); status = EXIT_FAILURE; return 0; } if (one_file_system) { if (du_depth == 0) { dir_dev = statbuf.st_dev; } else if (dir_dev != statbuf.st_dev) { return 0; } } sum = statbuf.st_blocks; if (S_ISLNK(statbuf.st_mode)) { if (slink_depth > du_depth) { /* -H or -L */ if (stat(filename, &statbuf) != 0) { bb_perror_msg("%s", filename); status = EXIT_FAILURE; return 0; } sum = statbuf.st_blocks; if (slink_depth == 1) { slink_depth = INT_MAX; /* Convert -H to -L. */ } } } if (statbuf.st_nlink > count_hardlinks) { /* Add files/directories with links only once */ if (is_in_ino_dev_hashtable(&statbuf)) { return 0; } add_to_ino_dev_hashtable(&statbuf, NULL); } if (S_ISDIR(statbuf.st_mode)) { DIR *dir; struct dirent *entry; char *newfile; dir = warn_opendir(filename); if (!dir) { status = EXIT_FAILURE; return sum; } newfile = last_char_is(filename, '/'); if (newfile) *newfile = '\0'; while ((entry = readdir(dir))) { char *name = entry->d_name; newfile = concat_subpath_file(filename, name); if (newfile == NULL) continue; ++du_depth; sum += du(newfile); --du_depth; free(newfile); } closedir(dir); } else if (du_depth > print_files) { return sum; } if (du_depth <= max_print_depth) { print(sum, filename); } return sum; }
int unzip_main(int argc, char **argv) { enum { O_PROMPT, O_NEVER, O_ALWAYS }; zip_header_t zip_header; smallint verbose = 1; smallint listing = 0; smallint overwrite = O_PROMPT; unsigned total_size; unsigned total_entries; int src_fd = -1; int dst_fd = -1; char *src_fn = NULL; char *dst_fn = NULL; llist_t *zaccept = NULL; llist_t *zreject = NULL; char *base_dir = NULL; int i, opt; int opt_range = 0; char key_buf[80]; struct stat stat_buf; /* '-' makes getopt return 1 for non-options */ while ((opt = getopt(argc, argv, "-d:lnopqx")) != -1) { switch (opt_range) { case 0: /* Options */ switch (opt) { case 'l': /* List */ listing = 1; break; case 'n': /* Never overwrite existing files */ overwrite = O_NEVER; break; case 'o': /* Always overwrite existing files */ overwrite = O_ALWAYS; break; case 'p': /* Extract files to stdout and fall through to set verbosity */ dst_fd = STDOUT_FILENO; case 'q': /* Be quiet */ verbose = 0; break; case 1: /* The zip file */ /* +5: space for ".zip" and NUL */ src_fn = xmalloc(strlen(optarg) + 5); strcpy(src_fn, optarg); opt_range++; break; default: bb_show_usage(); } break; case 1: /* Include files */ if (opt == 1) { llist_add_to(&zaccept, optarg); break; } if (opt == 'd') { base_dir = optarg; opt_range += 2; break; } if (opt == 'x') { opt_range++; break; } bb_show_usage(); case 2 : /* Exclude files */ if (opt == 1) { llist_add_to(&zreject, optarg); break; } if (opt == 'd') { /* Extract to base directory */ base_dir = optarg; opt_range++; break; } /* fall through */ default: bb_show_usage(); } } if (src_fn == NULL) { bb_show_usage(); } /* Open input file */ if (LONE_DASH(src_fn)) { src_fd = STDIN_FILENO; /* Cannot use prompt mode since zip data is arriving on STDIN */ if (overwrite == O_PROMPT) overwrite = O_NEVER; } else { static const char extn[][5] = {"", ".zip", ".ZIP"}; int orig_src_fn_len = strlen(src_fn); for (i = 0; (i < 3) && (src_fd == -1); i++) { strcpy(src_fn + orig_src_fn_len, extn[i]); src_fd = open(src_fn, O_RDONLY); } if (src_fd == -1) { src_fn[orig_src_fn_len] = '\0'; bb_error_msg_and_die("can't open %s, %s.zip, %s.ZIP", src_fn, src_fn, src_fn); } } /* Change dir if necessary */ if (base_dir) xchdir(base_dir); if (verbose) { printf("Archive: %s\n", src_fn); if (listing){ puts(" Length Date Time Name\n" " -------- ---- ---- ----"); } } total_size = 0; total_entries = 0; while (1) { uint32_t magic; /* Check magic number */ xread(src_fd, &magic, 4); if (magic == ZIP_CDS_MAGIC) break; if (magic != ZIP_FILEHEADER_MAGIC) bb_error_msg_and_die("invalid zip magic %08X", magic); /* Read the file header */ xread(src_fd, zip_header.raw, ZIP_HEADER_LEN); FIX_ENDIANNESS(zip_header); if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) { bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method); } /* Read filename */ free(dst_fn); dst_fn = xzalloc(zip_header.formatted.filename_len + 1); xread(src_fd, dst_fn, zip_header.formatted.filename_len); /* Skip extra header bytes */ unzip_skip(src_fd, zip_header.formatted.extra_len); /* Filter zip entries */ if (find_list_entry(zreject, dst_fn) || (zaccept && !find_list_entry(zaccept, dst_fn)) ) { /* Skip entry */ i = 'n'; } else { /* Extract entry */ if (listing) { /* List entry */ if (verbose) { unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16); printf("%9u %02u-%02u-%02u %02u:%02u %s\n", zip_header.formatted.ucmpsize, (dostime & 0x01e00000) >> 21, (dostime & 0x001f0000) >> 16, (((dostime & 0xfe000000) >> 25) + 1980) % 100, (dostime & 0x0000f800) >> 11, (dostime & 0x000007e0) >> 5, dst_fn); total_size += zip_header.formatted.ucmpsize; total_entries++; } else { /* short listing -- filenames only */ puts(dst_fn); } i = 'n'; } else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */ i = -1; } else if (last_char_is(dst_fn, '/')) { /* Extract directory */ if (stat(dst_fn, &stat_buf) == -1) { if (errno != ENOENT) { bb_perror_msg_and_die("cannot stat '%s'",dst_fn); } if (verbose) { printf(" creating: %s\n", dst_fn); } unzip_create_leading_dirs(dst_fn); if (bb_make_directory(dst_fn, 0777, 0)) { bb_error_msg_and_die("exiting"); } } else { if (!S_ISDIR(stat_buf.st_mode)) { bb_error_msg_and_die("'%s' exists but is not directory", dst_fn); } } i = 'n'; } else { /* Extract file */ _check_file: if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */ if (errno != ENOENT) { bb_perror_msg_and_die("cannot stat '%s'",dst_fn); } i = 'y'; } else { /* File already exists */ if (overwrite == O_NEVER) { i = 'n'; } else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */ if (overwrite == O_ALWAYS) { i = 'y'; } else { printf("replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", dst_fn); if (!fgets(key_buf, sizeof(key_buf), stdin)) { bb_perror_msg_and_die("cannot read input"); } i = key_buf[0]; } } else { /* File is not regular file */ bb_error_msg_and_die("'%s' exists but is not regular file",dst_fn); } } } }
static file_header_t *get_header_tar(struct gzip_handle *tar_stream) { union { unsigned char raw[512]; struct { char name[100]; /* 0-99 */ char mode[8]; /* 100-107 */ char uid[8]; /* 108-115 */ char gid[8]; /* 116-123 */ char size[12]; /* 124-135 */ char mtime[12]; /* 136-147 */ char chksum[8]; /* 148-155 */ char typeflag; /* 156-156 */ char linkname[100]; /* 157-256 */ char magic[6]; /* 257-262 */ char version[2]; /* 263-264 */ char uname[32]; /* 265-296 */ char gname[32]; /* 297-328 */ char devmajor[8]; /* 329-336 */ char devminor[8]; /* 337-344 */ char prefix[155]; /* 345-499 */ char padding[12]; /* 500-512 */ } formated; } tar; file_header_t *tar_entry = NULL; long i; long sum = 0; if (archive_offset % 512 != 0) { seek_forward(tar_stream, 512 - (archive_offset % 512)); } if (gzip_read(tar_stream, tar.raw, 512) != 512) { /* Unfortunately its common for tar files to have all sorts of * trailing garbage, fail silently */ // error_msg("Couldnt read header"); return (NULL); } archive_offset += 512; /* Check header has valid magic, unfortunately some tar files * have empty (0'ed) tar entries at the end, which will * cause this to fail, so fail silently for now */ if (strncmp(tar.formated.magic, "ustar", 5) != 0) { #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0) #endif return (NULL); } /* Do checksum on headers */ for (i = 0; i < 148; i++) { sum += tar.raw[i]; } sum += ' ' * 8; for (i = 156; i < 512; i++) { sum += tar.raw[i]; } if (sum != strtol(tar.formated.chksum, NULL, 8)) { if (strtol(tar.formated.chksum, NULL, 8) != 0) error_msg("Invalid tar header checksum"); return (NULL); } /* convert to type'ed variables */ tar_entry = xcalloc(1, sizeof(file_header_t)); // tar_entry->name = xstrdup(tar.formated.name); /* parse_mode(tar.formated.mode, &tar_entry->mode); */ tar_entry->mode = 07777 & strtol(tar.formated.mode, NULL, 8); tar_entry->uid = strtol(tar.formated.uid, NULL, 8); tar_entry->gid = strtol(tar.formated.gid, NULL, 8); tar_entry->size = strtol(tar.formated.size, NULL, 8); tar_entry->mtime = strtol(tar.formated.mtime, NULL, 8); tar_entry->device = (strtol(tar.formated.devmajor, NULL, 8) << 8) + strtol(tar.formated.devminor, NULL, 8); /* Fix mode, used by the old format */ switch (tar.formated.typeflag) { /* hard links are detected as regular files with 0 size and a link name */ case '1': tar_entry->mode |= S_IFREG; break; case 0: case '0': #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY if (last_char_is(tar_entry->name, '/')) { tar_entry->mode |= S_IFDIR; } else #endif tar_entry->mode |= S_IFREG; break; case '2': tar_entry->mode |= S_IFLNK; break; case '3': tar_entry->mode |= S_IFCHR; break; case '4': tar_entry->mode |= S_IFBLK; break; case '5': tar_entry->mode |= S_IFDIR; break; case '6': tar_entry->mode |= S_IFIFO; break; #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS case 'L':{ longname = xmalloc(tar_entry->size + 1); if (gzip_read(tar_stream, longname, tar_entry->size) != tar_entry->size) return NULL; longname[tar_entry->size] = '\0'; archive_offset += tar_entry->size; return (get_header_tar(tar_stream)); } case 'K':{ linkname = xmalloc(tar_entry->size + 1); if (gzip_read(tar_stream, linkname, tar_entry->size) != tar_entry->size) return NULL; linkname[tar_entry->size] = '\0'; archive_offset += tar_entry->size; return (get_header_tar(tar_stream)); } case 'D': case 'M': case 'N': case 'S': case 'V': perror_msg("Ignoring GNU extension type %c", tar.formated.typeflag); #endif default: perror_msg("Unknown typeflag: 0x%x", tar.formated.typeflag); break; } #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS if (longname) { tar_entry->name = longname; longname = NULL; } else #endif { tar_entry->name = xstrndup(tar.formated.name, 100); if (tar.formated.prefix[0]) { char *temp = tar_entry->name; char *prefixTemp = xstrndup(tar.formated.prefix, 155); tar_entry->name = concat_path_file(prefixTemp, temp); free(temp); free(prefixTemp); } } if (linkname) { tar_entry->link_name = linkname; linkname = NULL; } else { tar_entry->link_name = *tar.formated.linkname != '\0' ? xstrndup(tar.formated.linkname, 100) : NULL; } return (tar_entry); }
/* 1) add a user: update_passwd(FILE, USER, REMAINING_PWLINE, NULL) only if CONFIG_ADDUSER=y and applet_name[0] == 'a' like in adduser 2) add a group: update_passwd(FILE, GROUP, REMAINING_GRLINE, NULL) only if CONFIG_ADDGROUP=y and applet_name[0] == 'a' like in addgroup 3) add a user to a group: update_passwd(FILE, GROUP, NULL, MEMBER) only if CONFIG_FEATURE_ADDUSER_TO_GROUP=y, applet_name[0] == 'a' like in addgroup and member != NULL 4) delete a user: update_passwd(FILE, USER, NULL, NULL) 5) delete a group: update_passwd(FILE, GROUP, NULL, NULL) 6) delete a user from a group: update_passwd(FILE, GROUP, NULL, MEMBER) only if CONFIG_FEATURE_DEL_USER_FROM_GROUP=y and member != NULL 7) change user's password: update_passwd(FILE, USER, NEW_PASSWD, NULL) only if CONFIG_PASSWD=y and applet_name[0] == 'p' like in passwd or if CONFIG_CHPASSWD=y and applet_name[0] == 'c' like in chpasswd 8) delete a user from all groups: update_passwd(FILE, NULL, NULL, MEMBER) This function does not validate the arguments fed to it so the calling program should take care of that. Returns number of lines changed, or -1 on error. */ int FAST_FUNC update_passwd(const char *filename, const char *name, const char *new_passwd, const char *member) { #if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP) #define member NULL #endif struct stat sb; struct flock lock; FILE *old_fp; FILE *new_fp; char *fnamesfx; char *sfx_char; char *name_colon; int old_fd; int new_fd; int i; int changed_lines; int ret = -1; /* failure */ /* used as a bool: "are we modifying /etc/shadow?" */ #if ENABLE_FEATURE_SHADOWPASSWDS const char *shadow = strstr(filename, "shadow"); #else # define shadow NULL #endif filename = xmalloc_follow_symlinks(filename); if (filename == NULL) return ret; if (name) check_selinux_update_passwd(name); /* New passwd file, "/etc/passwd+" for now */ fnamesfx = xasprintf("%s+", filename); sfx_char = &fnamesfx[strlen(fnamesfx)-1]; name_colon = xasprintf("%s:", name ? name : ""); if (shadow) old_fp = fopen(filename, "r+"); else old_fp = fopen_or_warn(filename, "r+"); if (!old_fp) { if (shadow) ret = 0; /* missing shadow is not an error */ goto free_mem; } old_fd = fileno(old_fp); selinux_preserve_fcontext(old_fd); /* Try to create "/etc/passwd+". Wait if it exists. */ i = 30; do { // FIXME: on last iteration try w/o O_EXCL but with O_TRUNC? new_fd = open(fnamesfx, O_WRONLY|O_CREAT|O_EXCL, 0600); if (new_fd >= 0) goto created; if (errno != EEXIST) break; usleep(100000); /* 0.1 sec */ } while (--i); bb_perror_msg("can't create '%s'", fnamesfx); goto close_old_fp; created: if (fstat(old_fd, &sb) == 0) { fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */ fchown(new_fd, sb.st_uid, sb.st_gid); } errno = 0; new_fp = xfdopen_for_write(new_fd); /* Backup file is "/etc/passwd-" */ *sfx_char = '-'; /* Delete old backup */ i = (unlink(fnamesfx) && errno != ENOENT); /* Create backup as a hardlink to current */ if (i || link(filename, fnamesfx)) bb_perror_msg("warning: can't create backup copy '%s'", fnamesfx); *sfx_char = '+'; /* Lock the password file before updating */ lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; if (fcntl(old_fd, F_SETLK, &lock) < 0) bb_perror_msg("warning: can't lock '%s'", filename); lock.l_type = F_UNLCK; /* Read current password file, write updated /etc/passwd+ */ changed_lines = 0; while (1) { char *cp, *line; line = xmalloc_fgetline(old_fp); if (!line) /* EOF/error */ break; if (!name && member) { /* Delete member from all groups */ /* line is "GROUP:PASSWD:[member1[,member2]...]" */ unsigned member_len = strlen(member); char *list = strrchr(line, ':'); while (list) { list++; next_list_element: if (is_prefixed_with(list, member)) { char c; changed_lines++; c = list[member_len]; if (c == '\0') { if (list[-1] == ',') list--; *list = '\0'; break; } if (c == ',') { overlapping_strcpy(list, list + member_len + 1); goto next_list_element; } changed_lines--; } list = strchr(list, ','); } fprintf(new_fp, "%s\n", line); goto next; } cp = is_prefixed_with(line, name_colon); if (!cp) { fprintf(new_fp, "%s\n", line); goto next; } /* We have a match with "name:"... */ /* cp points past "name:" */ #if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP if (member) { /* It's actually /etc/group+, not /etc/passwd+ */ if (ENABLE_FEATURE_ADDUSER_TO_GROUP && applet_name[0] == 'a' ) { /* Add user to group */ fprintf(new_fp, "%s%s%s\n", line, last_char_is(line, ':') ? "" : ",", member); changed_lines++; } else if (ENABLE_FEATURE_DEL_USER_FROM_GROUP /* && applet_name[0] == 'd' */ ) { /* Delete user from group */ char *tmp; const char *fmt = "%s"; /* find the start of the member list: last ':' */ cp = strrchr(line, ':'); /* cut it */ *cp++ = '\0'; /* write the cut line name:passwd:gid: * or name:!:: */ fprintf(new_fp, "%s:", line); /* parse the tokens of the member list */ tmp = cp; while ((cp = strsep(&tmp, ",")) != NULL) { if (strcmp(member, cp) != 0) { fprintf(new_fp, fmt, cp); fmt = ",%s"; } else { /* found member, skip it */ changed_lines++; } } fprintf(new_fp, "\n"); } } else #endif if ((ENABLE_PASSWD && applet_name[0] == 'p') || (ENABLE_CHPASSWD && applet_name[0] == 'c') ) { /* Change passwd */ cp = strchrnul(cp, ':'); /* move past old passwd */ if (shadow && *cp == ':') { /* /etc/shadow's field 3 (passwd change date) needs updating */ /* move past old change date */ cp = strchrnul(cp + 1, ':'); /* "name:" + "new_passwd" + ":" + "change date" + ":rest of line" */ fprintf(new_fp, "%s%s:%u%s\n", name_colon, new_passwd, (unsigned)(time(NULL)) / (24*60*60), cp); } else { /* "name:" + "new_passwd" + ":rest of line" */ fprintf(new_fp, "%s%s%s\n", name_colon, new_passwd, cp); } changed_lines++; } /* else delete user or group: skip the line */ next: free(line); } if (changed_lines == 0) { #if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP if (member) { if (ENABLE_ADDGROUP && applet_name[0] == 'a') bb_error_msg("can't find %s in %s", name, filename); if (ENABLE_DELGROUP && applet_name[0] == 'd') bb_error_msg("can't find %s in %s", member, filename); } #endif if ((ENABLE_ADDUSER || ENABLE_ADDGROUP) && applet_name[0] == 'a' && !member ) { /* add user or group */ fprintf(new_fp, "%s%s\n", name_colon, new_passwd); changed_lines++; } } fcntl(old_fd, F_SETLK, &lock); /* We do want all of them to execute, thus | instead of || */ errno = 0; if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp)) || rename(fnamesfx, filename) ) { /* At least one of those failed */ bb_perror_nomsg(); goto unlink_new; } /* Success: ret >= 0 */ ret = changed_lines; unlink_new: if (ret < 0) unlink(fnamesfx); close_old_fp: fclose(old_fp); free_mem: free(fnamesfx); free((char *)filename); free(name_colon); return ret; }
int tar_main(int argc, char **argv) { char (*get_header_ptr)(archive_handle_t *) = get_header_tar; archive_handle_t *tar_handle; char *base_dir = NULL; const char *tar_filename = "-"; unsigned long opt; unsigned long ctx_flag = 0; if (argc < 2) { bb_show_usage(); } /* Prepend '-' to the first argument if required */ if (argv[1][0] != '-') { char *tmp; bb_xasprintf(&tmp, "-%s", argv[1]); argv[1] = tmp; } /* Initialise default values */ tar_handle = init_handle(); tar_handle->flags = ARCHIVE_CREATE_LEADING_DIRS | ARCHIVE_PRESERVE_DATE | ARCHIVE_EXTRACT_UNCONDITIONAL; bb_opt_complementaly = "c~tx:t~cx:x~ct:X*:T*"; #ifdef CONFIG_FEATURE_TAR_LONG_OPTIONS bb_applet_long_options = tar_long_options; #endif opt = bb_getopt_ulflags(argc, argv, tar_options, &base_dir, /* Change to dir <optarg> */ &tar_filename /* archive filename */ #ifdef CONFIG_FEATURE_TAR_FROM , NULL, &(tar_handle->reject) #endif ); /* Check one and only one context option was given */ if(opt & 0x80000000UL) { bb_show_usage(); } #ifdef CONFIG_FEATURE_TAR_CREATE ctx_flag = opt & (CTX_CREATE | CTX_TEST | CTX_EXTRACT); #else ctx_flag = opt & (CTX_TEST | CTX_EXTRACT); #endif if (ctx_flag == 0) { bb_show_usage(); } if(ctx_flag & CTX_TEST) { if ((tar_handle->action_header == header_list) || (tar_handle->action_header == header_verbose_list)) { tar_handle->action_header = header_verbose_list; } else { tar_handle->action_header = header_list; } } if(ctx_flag & CTX_EXTRACT) { if (tar_handle->action_data != data_extract_to_stdout) tar_handle->action_data = data_extract_all; } if(opt & TAR_OPT_2STDOUT) { /* To stdout */ tar_handle->action_data = data_extract_to_stdout; } if(opt & TAR_OPT_VERBOSE) { if ((tar_handle->action_header == header_list) || (tar_handle->action_header == header_verbose_list)) { tar_handle->action_header = header_verbose_list; } else { tar_handle->action_header = header_list; } } if (opt & TAR_OPT_KEEP_OLD) { tar_handle->flags &= ~ARCHIVE_EXTRACT_UNCONDITIONAL; } #ifdef CONFIG_FEATURE_TAR_GZIP if(opt & TAR_OPT_GZIP) { get_header_ptr = get_header_tar_gz; } #endif #ifdef CONFIG_FEATURE_TAR_BZIP2 if(opt & TAR_OPT_BZIP2) { get_header_ptr = get_header_tar_bz2; } #endif #ifdef CONFIG_FEATURE_TAR_COMPRESS if(opt & TAR_OPT_UNCOMPRESS) { get_header_ptr = get_header_tar_Z; } #endif #ifdef CONFIG_FEATURE_TAR_FROM if(opt & TAR_OPT_EXCLUDE_FROM) { tar_handle->reject = append_file_list_to_list(tar_handle->reject); } #endif /* Check if we are reading from stdin */ if ((argv[optind]) && (*argv[optind] == '-')) { /* Default is to read from stdin, so just skip to next arg */ optind++; } /* Setup an array of filenames to work with */ /* TODO: This is the same as in ar, separate function ? */ while (optind < argc) { char *filename_ptr = last_char_is(argv[optind], '/'); if (filename_ptr) { *filename_ptr = '\0'; } tar_handle->accept = llist_add_to(tar_handle->accept, argv[optind]); optind++; } if ((tar_handle->accept) || (tar_handle->reject)) { tar_handle->filter = filter_accept_reject_list; } /* Open the tar file */ { FILE *tar_stream; int flags; #ifdef CONFIG_FEATURE_TAR_CREATE if (opt & CTX_CREATE) { /* Make sure there is at least one file to tar up. */ if (tar_handle->accept == NULL) { bb_error_msg_and_die("Cowardly refusing to create an empty archive"); } tar_stream = stdout; flags = O_WRONLY | O_CREAT | O_EXCL; unlink(tar_filename); } else #endif { tar_stream = stdin; flags = O_RDONLY; } if ((tar_filename[0] == '-') && (tar_filename[1] == '\0')) { tar_handle->src_fd = fileno(tar_stream); tar_handle->seek = seek_by_char; } else { tar_handle->src_fd = bb_xopen(tar_filename, flags); } } if ((base_dir) && (chdir(base_dir))) { bb_perror_msg_and_die("Couldnt chdir to %s", base_dir); } #ifdef CONFIG_FEATURE_TAR_CREATE /* create an archive */ if (opt & CTX_CREATE) { int verboseFlag = FALSE; int gzipFlag = FALSE; # ifdef CONFIG_FEATURE_TAR_GZIP if (get_header_ptr == get_header_tar_gz) { gzipFlag = TRUE; } # endif /* CONFIG_FEATURE_TAR_GZIP */ # ifdef CONFIG_FEATURE_TAR_BZIP2 if (get_header_ptr == get_header_tar_bz2) { bb_error_msg_and_die("Creating bzip2 compressed archives is not currently supported."); } # endif /* CONFIG_FEATURE_TAR_BZIP2 */ if ((tar_handle->action_header == header_list) || (tar_handle->action_header == header_verbose_list)) { verboseFlag = TRUE; } writeTarFile(tar_handle->src_fd, verboseFlag, opt & TAR_OPT_DEREFERNCE, tar_handle->accept, tar_handle->reject, gzipFlag); } else #endif /* CONFIG_FEATURE_TAR_CREATE */ { while (get_header_ptr(tar_handle) == EXIT_SUCCESS); /* Ckeck that every file that should have been extracted was */ while (tar_handle->accept) { if (find_list_entry(tar_handle->reject, tar_handle->accept->data) == NULL) { if (find_list_entry(tar_handle->passed, tar_handle->accept->data) == NULL) { bb_error_msg_and_die("%s: Not found in archive\n", tar_handle->accept->data); } } tar_handle->accept = tar_handle->accept->link; } } #ifdef CONFIG_FEATURE_CLEAN_UP if (tar_handle->src_fd != STDIN_FILENO) { close(tar_handle->src_fd); } #endif /* CONFIG_FEATURE_CLEAN_UP */ return(EXIT_SUCCESS); }
extern int unzip_main(int argc, char **argv) { union { unsigned char raw[26]; struct { unsigned short version; /* 0-1 */ unsigned short flags; /* 2-3 */ unsigned short method; /* 4-5 */ unsigned short modtime; /* 6-7 */ unsigned short moddate; /* 8-9 */ unsigned int crc32 __attribute__ ((packed)); /* 10-13 */ unsigned int cmpsize __attribute__ ((packed));; /* 14-17 */ unsigned int ucmpsize __attribute__ ((packed));; /* 18-21 */ unsigned short filename_len; /* 22-23 */ unsigned short extra_len; /* 24-25 */ } formated __attribute__ ((packed)); } zip_header; archive_handle_t *archive_handle; unsigned int total_size = 0; unsigned int total_entries = 0; char *base_dir = NULL; int opt = 0; /* Initialise */ archive_handle = init_handle(); archive_handle->action_data = NULL; archive_handle->action_header = header_list_unzip; while ((opt = getopt(argc, argv, "lnopqd:")) != -1) { switch (opt) { case 'l': /* list */ archive_handle->action_header = header_verbose_list_unzip; archive_handle->action_data = data_skip; break; case 'n': /* never overwright existing files */ break; case 'o': archive_handle->flags = ARCHIVE_EXTRACT_UNCONDITIONAL; break; case 'p': /* extract files to stdout */ archive_handle->action_data = data_extract_to_stdout; break; case 'q': /* Extract files quietly */ archive_handle->action_header = header_skip; break; case 'd': /* Extract files to specified base directory*/ base_dir = optarg; break; #if 0 case 'x': /* Exclude the specified files */ archive_handle->filter = filter_accept_reject_list; break; #endif default: bb_show_usage(); } } if (argc == optind) { bb_show_usage(); } printf("Archive: %s\n", argv[optind]); if (archive_handle->action_header == header_verbose_list_unzip) { printf(" Length Date Time Name\n"); printf(" -------- ---- ---- ----\n"); } if (*argv[optind] == '-') { archive_handle->src_fd = fileno(stdin); archive_handle->seek = seek_by_char; } else { archive_handle->src_fd = bb_xopen(argv[optind++], O_RDONLY); } if ((base_dir) && (chdir(base_dir))) { bb_perror_msg_and_die("Couldnt chdir"); } while (optind < argc) { archive_handle->filter = filter_accept_list; archive_handle->accept = llist_add_to(archive_handle->accept, argv[optind]); optind++; } while (1) { unsigned int magic; int dst_fd; /* TODO Endian issues */ archive_xread_all(archive_handle, &magic, 4); archive_handle->offset += 4; if (magic == ZIP_CDS_MAGIC) { break; } else if (magic != ZIP_FILEHEADER_MAGIC) { bb_error_msg_and_die("Invlaide zip magic"); } /* Read the file header */ archive_xread_all(archive_handle, zip_header.raw, 26); archive_handle->offset += 26; archive_handle->file_header->mode = S_IFREG | 0777; if (zip_header.formated.method != 8) { bb_error_msg_and_die("Unsupported compression method %d\n", zip_header.formated.method); } /* Read filename */ archive_handle->file_header->name = xmalloc(zip_header.formated.filename_len + 1); archive_xread_all(archive_handle, archive_handle->file_header->name, zip_header.formated.filename_len); archive_handle->offset += zip_header.formated.filename_len; archive_handle->file_header->name[zip_header.formated.filename_len] = '\0'; /* Skip extra header bits */ archive_handle->file_header->size = zip_header.formated.extra_len; data_skip(archive_handle); archive_handle->offset += zip_header.formated.extra_len; /* Handle directories */ archive_handle->file_header->mode = S_IFREG | 0777; if (last_char_is(archive_handle->file_header->name, '/')) { archive_handle->file_header->mode ^= S_IFREG; archive_handle->file_header->mode |= S_IFDIR; } /* Data section */ archive_handle->file_header->size = zip_header.formated.cmpsize; if (archive_handle->action_data) { archive_handle->action_data(archive_handle); } else { dst_fd = bb_xopen(archive_handle->file_header->name, O_WRONLY | O_CREAT); inflate(archive_handle->src_fd, dst_fd); close(dst_fd); chmod(archive_handle->file_header->name, archive_handle->file_header->mode); /* Validate decompression - crc */ if (zip_header.formated.crc32 != (gunzip_crc ^ 0xffffffffL)) { bb_error_msg("Invalid compressed data--crc error"); } /* Validate decompression - size */ if (gunzip_bytes_out != zip_header.formated.ucmpsize) { bb_error_msg("Invalid compressed data--length error"); } } /* local file descriptor section */ archive_handle->offset += zip_header.formated.cmpsize; /* This ISNT unix time */ archive_handle->file_header->mtime = zip_header.formated.modtime | (zip_header.formated.moddate << 16); archive_handle->file_header->size = zip_header.formated.ucmpsize; total_size += archive_handle->file_header->size; total_entries++; archive_handle->action_header(archive_handle->file_header); /* Data descriptor section */ if (zip_header.formated.flags & 4) { /* skip over duplicate crc, compressed size and uncompressed size */ unsigned short i; for (i = 0; i != 12; i++) { archive_xread_char(archive_handle); } archive_handle->offset += 12; } } /* Central directory section */ if (archive_handle->action_header == header_verbose_list_unzip) { printf(" -------- -------\n"); printf("%9d %d files\n", total_size, total_entries); } return(EXIT_SUCCESS); }
char get_header_tar(archive_handle_t *archive_handle) { static int end; file_header_t *file_header = archive_handle->file_header; struct { /* ustar header, Posix 1003.1 */ char name[100]; /* 0-99 */ char mode[8]; /* 100-107 */ char uid[8]; /* 108-115 */ char gid[8]; /* 116-123 */ char size[12]; /* 124-135 */ char mtime[12]; /* 136-147 */ char chksum[8]; /* 148-155 */ char typeflag; /* 156-156 */ char linkname[100]; /* 157-256 */ char magic[6]; /* 257-262 */ char version[2]; /* 263-264 */ char uname[32]; /* 265-296 */ char gname[32]; /* 297-328 */ char devmajor[8]; /* 329-336 */ char devminor[8]; /* 337-344 */ char prefix[155]; /* 345-499 */ char padding[12]; /* 500-512 */ } tar; char *cp; int i, sum_u, sum_s, sum; int parse_names; if (sizeof(tar) != 512) BUG_tar_header_size(); #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS again: #endif /* Align header */ data_align(archive_handle, 512); again_after_align: xread(archive_handle->src_fd, &tar, 512); archive_handle->offset += 512; /* If there is no filename its an empty header */ if (tar.name[0] == 0) { if (end) { /* This is the second consecutive empty header! End of archive! * Read until the end to empty the pipe from gz or bz2 */ while (full_read(archive_handle->src_fd, &tar, 512) == 512) /* repeat */; return EXIT_FAILURE; } end = 1; return EXIT_SUCCESS; } end = 0; /* Check header has valid magic, "ustar" is for the proper tar * 0's are for the old tar format */ if (strncmp(tar.magic, "ustar", 5) != 0) { #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY if (memcmp(tar.magic, "\0\0\0\0", 5) != 0) #endif bb_error_msg_and_die("invalid tar magic"); } /* Do checksum on headers. * POSIX says that checksum is done on unsigned bytes, but * Sun and HP-UX gets it wrong... more details in * GNU tar source. */ sum_s = sum_u = ' ' * sizeof(tar.chksum); for (i = 0; i < 148 ; i++) { sum_u += ((unsigned char*)&tar)[i]; sum_s += ((signed char*)&tar)[i]; } for (i = 156; i < 512 ; i++) { sum_u += ((unsigned char*)&tar)[i]; sum_s += ((signed char*)&tar)[i]; } /* This field does not need special treatment (getOctal) */ sum = xstrtoul(tar.chksum, 8); if (sum_u != sum && sum_s != sum) { bb_error_msg_and_die("invalid tar header checksum"); } /* 0 is reserved for high perf file, treat as normal file */ if (!tar.typeflag) tar.typeflag = '0'; parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7'); /* getOctal trashes subsequent field, therefore we call it * on fields in reverse order */ if (tar.devmajor[0]) { unsigned minor = GET_OCTAL(tar.devminor); unsigned major = GET_OCTAL(tar.devmajor); file_header->device = makedev(major, minor); } file_header->link_name = NULL; if (!linkname && parse_names && tar.linkname[0]) { /* we trash magic[0] here, it's ok */ tar.linkname[sizeof(tar.linkname)] = '\0'; file_header->link_name = xstrdup(tar.linkname); /* FIXME: what if we have non-link object with link_name? */ /* Will link_name be free()ed? */ } file_header->mtime = GET_OCTAL(tar.mtime); file_header->size = GET_OCTAL(tar.size); file_header->gid = GET_OCTAL(tar.gid); file_header->uid = GET_OCTAL(tar.uid); /* Set bits 0-11 of the files mode */ file_header->mode = 07777 & GET_OCTAL(tar.mode); file_header->name = NULL; if (!longname && parse_names) { /* we trash mode[0] here, it's ok */ tar.name[sizeof(tar.name)] = '\0'; if (tar.prefix[0]) { /* and padding[0] */ tar.prefix[sizeof(tar.prefix)] = '\0'; file_header->name = concat_path_file(tar.prefix, tar.name); } else file_header->name = xstrdup(tar.name); } /* Set bits 12-15 of the files mode */ /* (typeflag was not trashed because chksum does not use getOctal) */ switch (tar.typeflag) { /* busybox identifies hard links as being regular files with 0 size and a link name */ case '1': file_header->mode |= S_IFREG; break; case '7': /* case 0: */ case '0': #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY if (last_char_is(file_header->name, '/')) { file_header->mode |= S_IFDIR; } else #endif file_header->mode |= S_IFREG; break; case '2': file_header->mode |= S_IFLNK; break; case '3': file_header->mode |= S_IFCHR; break; case '4': file_header->mode |= S_IFBLK; break; case '5': file_header->mode |= S_IFDIR; break; case '6': file_header->mode |= S_IFIFO; break; #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS case 'L': /* free: paranoia: tar with several consecutive longnames */ free(longname); /* For paranoia reasons we allocate extra NUL char */ longname = xzalloc(file_header->size + 1); /* We read ASCIZ string, including NUL */ xread(archive_handle->src_fd, longname, file_header->size); archive_handle->offset += file_header->size; /* return get_header_tar(archive_handle); */ /* gcc 4.1.1 didn't optimize it into jump */ /* so we will do it ourself, this also saves stack */ goto again; case 'K': free(linkname); linkname = xzalloc(file_header->size + 1); xread(archive_handle->src_fd, linkname, file_header->size); archive_handle->offset += file_header->size; /* return get_header_tar(archive_handle); */ goto again; case 'D': /* GNU dump dir */ case 'M': /* Continuation of multi volume archive */ case 'N': /* Old GNU for names > 100 characters */ case 'S': /* Sparse file */ case 'V': /* Volume header */ #endif case 'g': /* pax global header */ case 'x': { /* pax extended header */ off_t sz; bb_error_msg("warning: skipping header '%c'", tar.typeflag); sz = (file_header->size + 511) & ~(off_t)511; archive_handle->offset += sz; sz >>= 9; /* sz /= 512 but w/o contortions for signed div */ while (sz--) xread(archive_handle->src_fd, &tar, 512); /* return get_header_tar(archive_handle); */ goto again_after_align; } default: bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag); } #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS if (longname) { file_header->name = longname; longname = NULL; } if (linkname) { file_header->link_name = linkname; linkname = NULL; } #endif if (!strncmp(file_header->name, "/../"+1, 3) || strstr(file_header->name, "/../") ) { bb_error_msg_and_die("name with '..' encountered: '%s'", file_header->name); } /* Strip trailing '/' in directories */ /* Must be done after mode is set as '/' is used to check if it's a directory */ cp = last_char_is(file_header->name, '/'); if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { archive_handle->action_header(archive_handle->file_header); /* Note that we kill the '/' only after action_header() */ /* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */ if (cp) *cp = '\0'; archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; archive_handle->action_data(archive_handle); llist_add_to(&(archive_handle->passed), file_header->name); } else { data_skip(archive_handle); free(file_header->name); } archive_handle->offset += file_header->size; free(file_header->link_name); /* Do not free(file_header->name)! */ return EXIT_SUCCESS; }
extern char get_header_tar(archive_handle_t *archive_handle) { file_header_t *file_header = archive_handle->file_header; union { unsigned char raw[512]; struct { char name[100]; /* 0-99 */ char mode[8]; /* 100-107 */ char uid[8]; /* 108-115 */ char gid[8]; /* 116-123 */ char size[12]; /* 124-135 */ char mtime[12]; /* 136-147 */ char chksum[8]; /* 148-155 */ char typeflag; /* 156-156 */ char linkname[100]; /* 157-256 */ char magic[6]; /* 257-262 */ char version[2]; /* 263-264 */ char uname[32]; /* 265-296 */ char gname[32]; /* 297-328 */ char devmajor[8]; /* 329-336 */ char devminor[8]; /* 337-344 */ char prefix[155]; /* 345-499 */ char padding[12]; /* 500-512 */ } formated; } tar; long sum = 0; long i; char *tmp; /* Align header */ data_align(archive_handle, 512); if (archive_xread(archive_handle, tar.raw, 512) != 512) { /* Assume end of file */ return(EXIT_FAILURE); } archive_handle->offset += 512; /* If there is no filename its an empty header */ if (tar.formated.name[0] == 0) { return(EXIT_SUCCESS); } /* Check header has valid magic, "ustar" is for the proper tar * 0's are for the old tar format */ if (strncmp(tar.formated.magic, "ustar", 5) != 0) { #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0) #endif bb_error_msg_and_die("Invalid tar magic"); } /* Do checksum on headers */ for (i = 0; i < 148 ; i++) { sum += tar.raw[i]; } sum += ' ' * 8; for (i = 156; i < 512 ; i++) { sum += tar.raw[i]; } if (sum != strtol(tar.formated.chksum, NULL, 8)) { bb_error_msg("Invalid tar header checksum"); return(EXIT_FAILURE); } #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS if (longname) { file_header->name = longname; longname = NULL; } else if (linkname) { file_header->name = linkname; linkname = NULL; } else #endif if (tar.formated.prefix[0] == 0) { file_header->name = strdup(tar.formated.name); } else { file_header->name = concat_path_file(tar.formated.prefix, tar.formated.name); } tmp = last_char_is(archive_handle->file_header->name, '/'); if (tmp) { *tmp = '\0'; } file_header->mode = strtol(tar.formated.mode, NULL, 8); file_header->uid = strtol(tar.formated.uid, NULL, 8); file_header->gid = strtol(tar.formated.gid, NULL, 8); file_header->size = strtol(tar.formated.size, NULL, 8); file_header->mtime = strtol(tar.formated.mtime, NULL, 8); file_header->link_name = (tar.formated.linkname[0] != '\0') ? bb_xstrdup(tar.formated.linkname) : NULL; file_header->device = (dev_t) ((strtol(tar.formated.devmajor, NULL, 8) << 8) + strtol(tar.formated.devminor, NULL, 8)); if (tar.formated.typeflag == '1') { bb_error_msg("WARNING: Converting hard link to symlink"); file_header->mode |= S_IFLNK; } #if defined CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY || defined CONFIG_FEATURE_TAR_GNU_EXTENSIONS /* Fix mode, used by the old format */ switch (tar.formated.typeflag) { # ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY case 0: case '0': file_header->mode |= S_IFREG; break; case '1': // bb_error_msg("Internal hard link not supported"); break; case '2': file_header->mode |= S_IFLNK; break; case '3': file_header->mode |= S_IFCHR; break; case '4': file_header->mode |= S_IFBLK; break; case '5': file_header->mode |= S_IFDIR; break; case '6': file_header->mode |= S_IFIFO; break; # endif # ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS case 'L': { longname = xmalloc(file_header->size + 1); archive_xread_all(archive_handle, longname, file_header->size); longname[file_header->size] = '\0'; archive_handle->offset += file_header->size; return(get_header_tar(archive_handle)); } case 'K': { linkname = xmalloc(file_header->size + 1); archive_xread_all(archive_handle, linkname, file_header->size); linkname[file_header->size] = '\0'; archive_handle->offset += file_header->size; file_header->name = linkname; return(get_header_tar(archive_handle)); } case 'D': case 'M': case 'N': case 'S': case 'V': bb_error_msg("Ignoring GNU extension type %c", tar.formated.typeflag); # endif } #endif if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { archive_handle->action_header(archive_handle->file_header); archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; archive_handle->action_data(archive_handle); archive_handle->passed = llist_add_to(archive_handle->passed, archive_handle->file_header->name); } else { data_skip(archive_handle); } archive_handle->offset += file_header->size; return(EXIT_SUCCESS); }
/* * Read a tar file and extract or list the specified files within it. * If the list is empty than all files are extracted or listed. */ static int readTarFile(int tarFd, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag, char** extractList, char** excludeList) { int status; int errorFlag=FALSE; int skipNextHeaderFlag=FALSE; TarHeader rawHeader; TarInfo header; /* Read the tar file, and iterate over it one file at a time */ while ( (status = full_read(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) { /* Try to read the header */ if ( readTarHeader(&rawHeader, &header) == FALSE ) { if ( *(header.name) == '\0' ) { goto endgame; } else { errorFlag=TRUE; error_msg("Bad tar header, skipping"); continue; } } if ( *(header.name) == '\0' ) continue; header.tarFd = tarFd; /* Skip funky extra GNU headers that precede long files */ if ( (header.type == GNULONGNAME) || (header.type == GNULONGLINK) ) { skipNextHeaderFlag=TRUE; if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) errorFlag = TRUE; continue; } if ( skipNextHeaderFlag == TRUE ) { skipNextHeaderFlag=FALSE; error_msg(name_longer_than_foo, NAME_SIZE); if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) errorFlag = TRUE; continue; } #if defined BB_FEATURE_TAR_EXCLUDE if (exclude_file(excludeList, header.name)) { /* There are not the droids you're looking for, move along */ /* If it is a regular file, pretend to extract it with * the extractFlag set to FALSE, so the junk in the tarball * is properly skipped over */ if ( header.type==REGTYPE || header.type==REGTYPE0 ) { if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) errorFlag = TRUE; } continue; } #endif if (!extract_file(extractList, header.name)) { /* There are not the droids you're looking for, move along */ /* If it is a regular file, pretend to extract it with * the extractFlag set to FALSE, so the junk in the tarball * is properly skipped over */ if ( header.type==REGTYPE || header.type==REGTYPE0 ) { if (tarExtractRegularFile(&header, FALSE, FALSE) == FALSE) errorFlag = TRUE; } continue; } if (listFlag == TRUE) { /* Special treatment if the list (-t) flag is on */ if (verboseFlag == TRUE) { int len, len1; char buf[35]; struct tm *tm = localtime (&(header.mtime)); len=printf("%s ", mode_string(header.mode)); my_getpwuid(buf, header.uid); if (! *buf) len+=printf("%d", header.uid); else len+=printf("%s", buf); my_getgrgid(buf, header.gid); if (! *buf) len+=printf("/%-d ", header.gid); else len+=printf("/%-s ", buf); if (header.type==CHRTYPE || header.type==BLKTYPE) { len1=snprintf(buf, sizeof(buf), "%ld,%-ld ", header.devmajor, header.devminor); } else { len1=snprintf(buf, sizeof(buf), "%lu ", (long)header.size); } /* Jump through some hoops to make the columns match up */ for(;(len+len1)<31;len++) printf(" "); printf(buf); /* Use ISO 8610 time format */ if (tm) { printf ("%04d-%02d-%02d %02d:%02d:%02d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); } } printf("%s", header.name); if (verboseFlag == TRUE) { if (header.type==LNKTYPE) /* If this is a link, say so */ printf(" link to %s", header.linkname); else if (header.type==SYMTYPE) printf(" -> %s", header.linkname); } printf("\n"); } /* List contents if we are supposed to do that */ if (verboseFlag == TRUE && extractFlag == TRUE) { /* Now the normal listing */ FILE *vbFd = stdout; if (tostdoutFlag == TRUE) // If the archive goes to stdout, verbose to stderr vbFd = stderr; fprintf(vbFd, "%s\n", header.name); } /* Remove files if we would overwrite them */ if (extractFlag == TRUE && tostdoutFlag == FALSE) unlink(header.name); /* If we got here, we can be certain we have a legitimate * header to work with. So work with it. */ switch ( header.type ) { case REGTYPE: case REGTYPE0: /* If the name ends in a '/' then assume it is * supposed to be a directory, and fall through */ if (!last_char_is(header.name,'/')) { if (tarExtractRegularFile(&header, extractFlag, tostdoutFlag)==FALSE) errorFlag=TRUE; break; } case DIRTYPE: if (tarExtractDirectory( &header, extractFlag, tostdoutFlag)==FALSE) errorFlag=TRUE; break; case LNKTYPE: if (tarExtractHardLink( &header, extractFlag, tostdoutFlag)==FALSE) errorFlag=TRUE; break; case SYMTYPE: if (tarExtractSymLink( &header, extractFlag, tostdoutFlag)==FALSE) errorFlag=TRUE; break; case CHRTYPE: case BLKTYPE: case FIFOTYPE: if (tarExtractSpecial( &header, extractFlag, tostdoutFlag)==FALSE) errorFlag=TRUE; break; #if 0 /* Handled earlier */ case GNULONGNAME: case GNULONGLINK: skipNextHeaderFlag=TRUE; break; #endif default: error_msg("Unknown file type '%c' in tar file", header.type); close( tarFd); return( FALSE); } } close(tarFd); if (status > 0) { /* Bummer - we read a partial header */ perror_msg("Error reading tar file"); return ( FALSE); } else if (errorFlag==TRUE) { error_msg( "Error exit delayed from previous errors"); return( FALSE); } else return( status); /* Stuff to do when we are done */ endgame: close( tarFd); if ( *(header.name) == '\0' ) { if (errorFlag==TRUE) error_msg( "Error exit delayed from previous errors"); else return( TRUE); } return( FALSE); }