From 83518d18a34a3ddfcaac1739930d8469f5bc2442 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Fri, 20 Mar 2009 22:17:13 +0000 Subject: [PATCH] Compatibility fixes: grep: support -z find: support --mindepth together +45 bytes cpio: support -p (configurable, +230 bytes) libbb: tweaks for cpio --- TODO_config_nommu | 1 + archival/Config.in | 7 ++++ archival/cpio.c | 81 ++++++++++++++++++++++++++++++-------- findutils/find.c | 22 ++++++----- findutils/grep.c | 17 ++++++-- include/libbb.h | 10 ++--- include/usage.h | 35 ++++++++++------ libbb/vfork_daemon_rexec.c | 19 +++++---- miscutils/setsid.c | 3 +- scripts/defconfig | 1 + 10 files changed, 137 insertions(+), 59 deletions(-) diff --git a/TODO_config_nommu b/TODO_config_nommu index 428d9b300..2061bfd1c 100644 --- a/TODO_config_nommu +++ b/TODO_config_nommu @@ -110,6 +110,7 @@ CONFIG_BUNZIP2=y CONFIG_BZIP2=y CONFIG_CPIO=y CONFIG_FEATURE_CPIO_O=y +CONFIG_FEATURE_CPIO_P=y CONFIG_DPKG=y CONFIG_DPKG_DEB=y CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY=y diff --git a/archival/Config.in b/archival/Config.in index 0b5cf3750..64b44c218 100644 --- a/archival/Config.in +++ b/archival/Config.in @@ -110,6 +110,13 @@ config FEATURE_CPIO_O This implementation of cpio can create cpio archives in the "newc" format only. +config FEATURE_CPIO_P + bool "Support for passthrough mode" + default n + depends on FEATURE_CPIO_O + help + Passthrough mode. Rarely used. + config DPKG bool "dpkg" default n diff --git a/archival/cpio.c b/archival/cpio.c index 1c30d89a8..929d544ff 100644 --- a/archival/cpio.c +++ b/archival/cpio.c @@ -259,8 +259,9 @@ int cpio_main(int argc UNUSED_PARAM, char **argv) CPIO_OPT_FILE = (1 << 4), CPIO_OPT_CREATE_LEADING_DIR = (1 << 5), CPIO_OPT_PRESERVE_MTIME = (1 << 6), - CPIO_OPT_CREATE = (1 << 7), - CPIO_OPT_FORMAT = (1 << 8), + CPIO_OPT_CREATE = (1 << 7) * ENABLE_FEATURE_CPIO_O, + CPIO_OPT_FORMAT = (1 << 8) * ENABLE_FEATURE_CPIO_O, + CPIO_OPT_PASSTHROUGH = (1 << 9) * ENABLE_FEATURE_CPIO_P, }; #if ENABLE_GETOPT_LONG @@ -270,21 +271,66 @@ int cpio_main(int argc UNUSED_PARAM, char **argv) #if ENABLE_FEATURE_CPIO_O "create\0" No_argument "o" "format\0" Required_argument "H" +#if ENABLE_FEATURE_CPIO_P + "pass-through\0" No_argument "p" +#endif #endif ; #endif - /* Initialize */ - archive_handle = init_handle(); - archive_handle->src_fd = STDIN_FILENO; - archive_handle->seek = seek_by_read; - archive_handle->ah_flags = ARCHIVE_EXTRACT_NEWER; - -#if ENABLE_FEATURE_CPIO_O - opt = getopt32(argv, "ituvF:dmoH:", &cpio_filename, &cpio_fmt); + /* As of now we do not enforce this: */ + /* -i,-t,-o,-p are mutually exclusive */ + /* -u,-d,-m make sense only with -i or -p */ + /* -F makes sense only with -o */ +#if !ENABLE_FEATURE_CPIO_O + opt = getopt32(argv, "ituvF:dm", &cpio_filename); +#else + opt = getopt32(argv, "ituvF:dmoH:" USE_FEATURE_CPIO_P("p"), &cpio_filename, &cpio_fmt); + if (opt & CPIO_OPT_PASSTHROUGH) { + pid_t pid; + struct fd_pair pp; + if (argv[optind] == NULL) + bb_show_usage(); + if (opt & CPIO_OPT_CREATE_LEADING_DIR) + mkdir(argv[optind], 0777); + /* Crude existence check: + * close(xopen(argv[optind], O_RDONLY | O_DIRECTORY)); + * We can also xopen, fstat, IS_DIR, later fchdir. + * This would check for existence earlier and cleaner. + * As it stands now, if we fail xchdir later, + * child dies on EPIPE, unless it caught + * a diffrerent problem earlier. + * This is good enough for now. + */ +#if !BB_MMU + pp.rd = 3; + pp.wr = 4; + if (!re_execed) { + close(3); + close(4); + xpiped_pair(pp); + } +#else + xpiped_pair(pp); +#endif + pid = fork_or_rexec(argv); + if (pid == 0) { /* child */ + close(pp.rd); + xmove_fd(pp.wr, STDOUT_FILENO); + goto dump; + } + /* parent */ + xchdir(argv[optind++]); + close(pp.wr); + xmove_fd(pp.rd, STDIN_FILENO); + opt &= ~CPIO_OPT_PASSTHROUGH; + opt |= CPIO_OPT_EXTRACT; + goto skip; + } + /* -o */ if (opt & CPIO_OPT_CREATE) { - if (*cpio_fmt != 'n') + if (*cpio_fmt != 'n') /* we _require_ "-H newc" */ bb_show_usage(); if (opt & CPIO_OPT_FILE) { fclose(stdout); @@ -292,13 +338,18 @@ int cpio_main(int argc UNUSED_PARAM, char **argv) /* Paranoia: I don't trust libc that much */ xdup2(fileno(stdout), STDOUT_FILENO); } + dump: return cpio_o(); } -#else - opt = getopt32(argv, "ituvF:dm", &cpio_filename); + skip: #endif argv += optind; + archive_handle = init_handle(); + archive_handle->src_fd = STDIN_FILENO; + archive_handle->seek = seek_by_read; + archive_handle->ah_flags = ARCHIVE_EXTRACT_NEWER; + /* One of either extract or test options must be given */ if ((opt & (CPIO_OPT_TEST | CPIO_OPT_EXTRACT)) == 0) { bb_show_usage(); @@ -306,9 +357,7 @@ int cpio_main(int argc UNUSED_PARAM, char **argv) if (opt & CPIO_OPT_TEST) { /* if both extract and test options are given, ignore extract option */ - if (opt & CPIO_OPT_EXTRACT) { - opt &= ~CPIO_OPT_EXTRACT; - } + opt &= ~CPIO_OPT_EXTRACT; archive_handle->action_header = header_list; } if (opt & CPIO_OPT_EXTRACT) { diff --git a/findutils/find.c b/findutils/find.c index f2b89746f..df632f219 100644 --- a/findutils/find.c +++ b/findutils/find.c @@ -381,9 +381,11 @@ static int FAST_FUNC fileAction(const char *fileName, { int i; #if ENABLE_FEATURE_FIND_MAXDEPTH - int maxdepth = (int)(ptrdiff_t)userData; +#define minmaxdepth ((int*)userData) - if (depth > maxdepth) return SKIP; + if (depth < minmaxdepth[0]) return TRUE; + if (depth > minmaxdepth[1]) return SKIP; +#undef minmaxdepth #endif #if ENABLE_FEATURE_FIND_XDEV @@ -812,19 +814,21 @@ int find_main(int argc, char **argv) static const char options[] ALIGN1 = "-follow\0" USE_FEATURE_FIND_XDEV( "-xdev\0" ) -USE_FEATURE_FIND_MAXDEPTH("-maxdepth\0") +USE_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0") ; enum { OPT_FOLLOW, USE_FEATURE_FIND_XDEV( OPT_XDEV ,) -USE_FEATURE_FIND_MAXDEPTH(OPT_MAXDEPTH,) +USE_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,) }; char *arg; char **argp; int i, firstopt, status = EXIT_SUCCESS; #if ENABLE_FEATURE_FIND_MAXDEPTH - int maxdepth = INT_MAX; + int minmaxdepth[2] = { 0, INT_MAX }; +#else +#define minmaxdepth NULL #endif for (firstopt = 1; firstopt < argc; firstopt++) { @@ -875,10 +879,10 @@ USE_FEATURE_FIND_MAXDEPTH(OPT_MAXDEPTH,) } #endif #if ENABLE_FEATURE_FIND_MAXDEPTH - if (opt == OPT_MAXDEPTH) { + if (opt == OPT_MINDEPTH || opt == OPT_MINDEPTH + 1) { if (!argp[1]) bb_show_usage(); - maxdepth = xatoi_u(argp[1]); + minmaxdepth[opt - OPT_MINDEPTH] = xatoi_u(argp[1]); argp[0] = (char*)"-a"; argp[1] = (char*)"-a"; argp++; @@ -895,9 +899,7 @@ USE_FEATURE_FIND_MAXDEPTH(OPT_MAXDEPTH,) fileAction, /* file action */ fileAction, /* dir action */ #if ENABLE_FEATURE_FIND_MAXDEPTH - /* double cast suppresses - * "cast to ptr from int of different size" */ - (void*)(ptrdiff_t)maxdepth,/* user data */ + minmaxdepth, /* user data */ #else NULL, /* user data */ #endif diff --git a/findutils/grep.c b/findutils/grep.c index 6a6ddb679..723c3511a 100644 --- a/findutils/grep.c +++ b/findutils/grep.c @@ -28,7 +28,9 @@ USE_FEATURE_GREP_CONTEXT("A:B:C:") \ USE_FEATURE_GREP_EGREP_ALIAS("E") \ USE_DESKTOP("w") \ + USE_EXTRA_COMPAT("z") \ "aI" + /* ignored: -a "assume all files to be text" */ /* ignored: -I "assume binary files have no matches" */ @@ -54,6 +56,7 @@ enum { USE_FEATURE_GREP_CONTEXT( OPTBIT_C ,) /* -C NUM: -A and -B combined */ USE_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) /* extended regexp */ USE_DESKTOP( OPTBIT_w ,) /* whole word match */ + USE_EXTRA_COMPAT( OPTBIT_z ,) /* input is NUL terminated */ OPT_l = 1 << OPTBIT_l, OPT_n = 1 << OPTBIT_n, OPT_q = 1 << OPTBIT_q, @@ -75,6 +78,7 @@ enum { OPT_C = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_C)) + 0, OPT_E = USE_FEATURE_GREP_EGREP_ALIAS((1 << OPTBIT_E)) + 0, OPT_w = USE_DESKTOP( (1 << OPTBIT_w)) + 0, + OPT_z = USE_EXTRA_COMPAT( (1 << OPTBIT_z)) + 0, }; #define PRINT_FILES_WITH_MATCHES (option_mask32 & OPT_l) @@ -84,6 +88,7 @@ enum { #define PRINT_MATCH_COUNTS (option_mask32 & OPT_c) #define FGREP_FLAG (option_mask32 & OPT_F) #define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & OPT_L) +#define NUL_DELIMITED (option_mask32 & OPT_z) struct globals { int max_matches; @@ -186,7 +191,7 @@ static void print_line(const char *line, size_t line_len, int linenum, char deco puts(line); #else fwrite(line, 1, line_len, stdout); - putchar('\n'); + putchar(NUL_DELIMITED ? '\0' : '\n'); #endif } } @@ -197,12 +202,13 @@ static ssize_t FAST_FUNC bb_getline(char **line_ptr, size_t *line_alloc_len, FIL { ssize_t res_sz; char *line; + int delim = (NUL_DELIMITED ? '\0' : '\n'); - res_sz = getline(line_ptr, line_alloc_len, file); + res_sz = getdelim(line_ptr, line_alloc_len, delim, file); line = *line_ptr; if (res_sz > 0) { - if (line[res_sz - 1] == '\n') + if (line[res_sz - 1] == delim) line[--res_sz] = '\0'; } else { free(line); /* uclibc allocates a buffer even on EOF. WTF? */ @@ -407,8 +413,11 @@ static int grep_file(FILE *file) #endif /* Did we print all context after last requested match? */ if ((option_mask32 & OPT_m) - && !print_n_lines_after && nmatches == max_matches) + && !print_n_lines_after + && nmatches == max_matches + ) { break; + } } /* while (read line) */ /* special-case file post-processing for options where we don't print line diff --git a/include/libbb.h b/include/libbb.h index 8ea493b18..a2042fe5c 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -797,9 +797,9 @@ int run_nofork_applet_prime(struct nofork_save_area *old, int applet_no, char ** * Both of the above will redirect fd 0,1,2 to /dev/null and drop ctty * (will do setsid()). * - * forkexit_or_rexec(argv) = bare-bones "fork + parent exits" on MMU, + * fork_or_rexec(argv) = bare-bones "fork" on MMU, * "vfork + re-exec ourself" on NOMMU. No fd redirection, no setsid(). - * Currently used for setsid only. On MMU ignores argv. + * On MMU ignores argv. * * Helper for network daemons in foreground mode: * @@ -813,14 +813,14 @@ enum { DAEMON_ONLY_SANITIZE = 8, /* internal use */ }; #if BB_MMU - void forkexit_or_rexec(void) FAST_FUNC; + pid_t fork_or_rexec(void) FAST_FUNC; enum { re_execed = 0 }; -# define forkexit_or_rexec(argv) forkexit_or_rexec() +# define fork_or_rexec(argv) fork_or_rexec() # define bb_daemonize_or_rexec(flags, argv) bb_daemonize_or_rexec(flags) # define bb_daemonize(flags) bb_daemonize_or_rexec(flags, bogus) #else void re_exec(char **argv) NORETURN FAST_FUNC; - void forkexit_or_rexec(char **argv) FAST_FUNC; + pid_t fork_or_rexec(char **argv) FAST_FUNC; extern bool re_execed; int BUG_fork_is_unavailable_on_nommu(void) FAST_FUNC; int BUG_daemon_is_unavailable_on_nommu(void) FAST_FUNC; diff --git a/include/usage.h b/include/usage.h index e78754315..2b5d34ea6 100644 --- a/include/usage.h +++ b/include/usage.h @@ -531,24 +531,29 @@ "\n -l,-s Create (sym)links" \ #define cpio_trivial_usage \ - "-[dim" USE_FEATURE_CPIO_O("o") "tuv][F cpiofile]" \ - USE_FEATURE_CPIO_O( "[H newc]" ) + "-[ti" USE_FEATURE_CPIO_O("o") USE_FEATURE_CPIO_P("p") "dmvu] [-F FILE]" \ + USE_FEATURE_CPIO_O( " [-H newc]" ) #define cpio_full_usage "\n\n" \ "Extract or list files from a cpio archive" \ USE_FEATURE_CPIO_O( ", or create a cpio archive" ) \ - "\n" \ - "Main operation mode:" \ - "\n d Make leading directories" \ - "\n i Extract" \ - "\n m Preserve mtime" \ + "\nMain operation mode:" \ + "\n -t List" \ + "\n -i Extract" \ USE_FEATURE_CPIO_O( \ - "\n o Create" \ - "\n H newc Define format" \ + "\n -o Create" \ + ) \ + USE_FEATURE_CPIO_P( \ + "\n -p Passthrough" \ + ) \ + "\nOptions:" \ + "\n -d Make leading directories" \ + "\n -m Preserve mtime" \ + "\n -v Verbose" \ + "\n -u Overwrite" \ + "\n -F Input file" \ + USE_FEATURE_CPIO_O( \ + "\n -H Define format" \ ) \ - "\n t List" \ - "\n v Verbose" \ - "\n u Unconditional overwrite" \ - "\n F Input from file" \ #define crond_trivial_usage \ "-fbS -l N " USE_FEATURE_CROND_D("-d N ") "-L LOGFILE -c DIR" @@ -1167,6 +1172,7 @@ USE_FEATURE_FIND_MAXDEPTH( \ "\n -maxdepth N Descend at most N levels. -maxdepth 0 applies" \ "\n tests/actions to command line arguments only") \ + "\n -mindepth N Do not act on first N levels" \ "\n -name PATTERN File name (w/o directory name) matches PATTERN" \ "\n -iname PATTERN Case insensitive -name" \ USE_FEATURE_FIND_PATH( \ @@ -1425,6 +1431,7 @@ "eF" \ USE_FEATURE_GREP_EGREP_ALIAS("E") \ USE_FEATURE_GREP_CONTEXT("ABC") \ + USE_EXTRA_COMPAT("z") \ "] PATTERN [FILEs...]" #define grep_full_usage "\n\n" \ "Search for PATTERN in each FILE or standard input\n" \ @@ -1453,6 +1460,8 @@ "\n -A Print NUM lines of trailing context" \ "\n -B Print NUM lines of leading context" \ "\n -C Print NUM lines of output context") \ + USE_EXTRA_COMPAT( \ + "\n -z Input is NUL terminated") \ #define grep_example_usage \ "$ grep root /etc/passwd\n" \ diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c index 50dc3affe..f64239a96 100644 --- a/libbb/vfork_daemon_rexec.c +++ b/libbb/vfork_daemon_rexec.c @@ -251,35 +251,33 @@ void FAST_FUNC re_exec(char **argv) bb_perror_msg_and_die("exec %s", bb_busybox_exec_path); } -void FAST_FUNC forkexit_or_rexec(char **argv) +pid_t FAST_FUNC fork_or_rexec(char **argv) { pid_t pid; /* Maybe we are already re-execed and come here again? */ if (re_execed) - return; + return 0; /* child */ pid = vfork(); if (pid < 0) /* wtf? */ bb_perror_msg_and_die("vfork"); if (pid) /* parent */ - exit(EXIT_SUCCESS); + return pid; /* child - re-exec ourself */ re_exec(argv); } #else /* Dance around (void)...*/ -#undef forkexit_or_rexec -void FAST_FUNC forkexit_or_rexec(void) +#undef fork_or_rexec +pid_t FAST_FUNC fork_or_rexec(void) { pid_t pid; pid = fork(); if (pid < 0) /* wtf? */ bb_perror_msg_and_die("fork"); - if (pid) /* parent */ - exit(EXIT_SUCCESS); - /* child */ + return pid; } -#define forkexit_or_rexec(argv) forkexit_or_rexec() +#define fork_or_rexec(argv) fork_or_rexec() #endif /* Due to a #define in libbb.h on MMU systems we actually have 1 argument - @@ -310,7 +308,8 @@ void FAST_FUNC bb_daemonize_or_rexec(int flags, char **argv) fd = dup(fd); /* have 0,1,2 open at least to /dev/null */ if (!(flags & DAEMON_ONLY_SANITIZE)) { - forkexit_or_rexec(argv); + if (fork_or_rexec(argv)) + exit(EXIT_SUCCESS); /* parent */ /* if daemonizing, make sure we detach from stdio & ctty */ setsid(); dup2(fd, 0); diff --git a/miscutils/setsid.c b/miscutils/setsid.c index 127adf6f2..d7de1f149 100644 --- a/miscutils/setsid.c +++ b/miscutils/setsid.c @@ -26,7 +26,8 @@ int setsid_main(int argc UNUSED_PARAM, char **argv) * Otherwise our PID serves as PGID of some existing process group * and cannot be used as PGID of a new process group. */ if (getpgrp() == getpid()) - forkexit_or_rexec(argv); + if (fork_or_rexec(argv)) + exit(EXIT_SUCCESS); /* parent */ setsid(); /* no error possible */ diff --git a/scripts/defconfig b/scripts/defconfig index 6bf1a4a28..166989bba 100644 --- a/scripts/defconfig +++ b/scripts/defconfig @@ -112,6 +112,7 @@ CONFIG_BUNZIP2=y CONFIG_BZIP2=y CONFIG_CPIO=y CONFIG_FEATURE_CPIO_O=y +CONFIG_FEATURE_CPIO_P=y # CONFIG_DPKG is not set # CONFIG_DPKG_DEB is not set # CONFIG_FEATURE_DPKG_DEB_EXTRACT_ONLY is not set