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
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
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
#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);
/* 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();
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) {
{
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
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++) {
}
#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++;
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
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" */
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,
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)
#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;
puts(line);
#else
fwrite(line, 1, line_len, stdout);
- putchar('\n');
+ putchar(NUL_DELIMITED ? '\0' : '\n');
#endif
}
}
{
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? */
#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
* 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:
*
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;
"\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"
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( \
"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" \
"\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" \
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 -
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);
* 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 */
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