make compressed help code NOMMU- and NOFORK-friendly - no forking anymore, bunzip2 unpack routine now does all it in memory.
No binary (build failed)
—
(log yok)
| @@ -403,44 +403,32 @@ static void check_suid(const struct bb_applet *applet) | |
| static const char *unpack_usage_messages(void) | |
| { | |
| - | int input[2], output[2], pid; |
| - | char *buf; |
| - | |
| - | if (pipe(input) < 0 || pipe(output) < 0) |
| - | exit(1); |
| - | |
| - | //TODO: not NOMMU friendly! |
| - | pid = fork(); |
| - | switch (pid) { |
| - | case -1: /* error */ |
| - | exit(1); |
| - | case 0: /* child */ |
| - | close(input[1]); |
| - | close(output[0]); |
| - | uncompressStream(input[0], output[1]); |
| - | exit(0); |
| - | } |
| - | /* parent */ |
| - | |
| - | close(input[0]); |
| - | close(output[1]); |
| - | pid = fork(); |
| - | switch (pid) { |
| - | case -1: /* error */ |
| - | exit(1); |
| - | case 0: /* child */ |
| - | full_write(input[1], packed_usage, sizeof(packed_usage)); |
| - | exit(0); |
| - | } |
| - | /* parent */ |
| - | close(input[1]); |
| + | char *outbuf = NULL; |
| + | bunzip_data *bd; |
| + | int i; |
| - | buf = xmalloc(SIZEOF_usage_messages); |
| - | full_read(output[0], buf, SIZEOF_usage_messages); |
| - | return buf; |
| + | i = start_bunzip(&bd, |
| + | /* src_fd: */ -1, |
| + | /* inbuf: */ packed_usage, |
| + | /* len: */ sizeof(packed_usage)); |
| + | /* read_bunzip can longjmp to start_bunzip, and ultimately |
| + | * end up here with i != 0 on read data errors! Not trivial */ |
| + | if (!i) { |
| + | /* Cannot use xmalloc: will leak bd in NOFORK case! */ |
| + | outbuf = malloc_or_warn(SIZEOF_usage_messages); |
| + | if (outbuf) |
| + | read_bunzip(bd, outbuf, SIZEOF_usage_messages); |
| + | } |
| + | dealloc_bunzip(bd); |
| + | return outbuf; |
| } | |
| + | #define dealloc_usage_messages(s) free(s) |
| + | |
| #else | |
| + | |
| #define unpack_usage_messages() usage_messages | |
| + | #define dealloc_usage_messages(s) ((void)(s)) |
| + | |
| #endif /* FEATURE_COMPRESS_USAGE */ | |
| @@ -448,22 +436,23 @@ void bb_show_usage(void) | |
| { | |
| if (ENABLE_SHOW_USAGE) { | |
| const char *format_string; | |
| - | const char *usage_string = unpack_usage_messages(); |
| + | const char *p; |
| + | const char *usage_string = p = unpack_usage_messages(); |
| int i; | |
| i = current_applet - applets; | |
| while (i) { | |
| - | while (*usage_string++) continue; |
| + | while (*p++) continue; |
| i--; | |
| } | |
| format_string = "%s\n\nUsage: %s %s\n\n"; | |
| - | if (*usage_string == '\b') |
| + | if (*p == '\b') |
| format_string = "%s\n\nNo help available.\n\n"; | |
| fprintf(stderr, format_string, bb_msg_full_version, | |
| - | applet_name, usage_string); |
| + | applet_name, p); |
| + | dealloc_usage_messages((char*)usage_string); |
| } | |
| - | |
| xfunc_die(); | |
| } | |
| @@ -2079,7 +2079,7 @@ int gzip_main(int argc, char **argv) | |
| ALLOC(ush, G1.prev, 1L << BITS); | |
| /* Initialise the CRC32 table */ | |
| - | G1.crc_32_tab = crc32_filltable(0); |
| + | G1.crc_32_tab = crc32_filltable(NULL, 0); |
| return bbunpack(argv, make_new_name_gzip, pack_gzip); | |
| } | |
| @@ -62,34 +62,31 @@ struct group_data { | |
| /* Structure holding all the housekeeping data, including IO buffers and | |
| memory that persists between calls to bunzip */ | |
| - | typedef struct { |
| + | struct bunzip_data { |
| /* State for interrupting output loop */ | |
| - | |
| int writeCopies, writePos, writeRunCountdown, writeCount, writeCurrent; | |
| /* I/O tracking data (file handles, buffers, positions, etc.) */ | |
| - | |
| int in_fd, out_fd, inbufCount, inbufPos /*, outbufPos*/; | |
| unsigned char *inbuf /*,*outbuf*/; | |
| unsigned inbufBitCount, inbufBits; | |
| /* The CRC values stored in the block header and calculated from the data */ | |
| - | |
| uint32_t headerCRC, totalCRC, writeCRC; | |
| - | uint32_t *crc32Table; |
| - | /* Intermediate buffer and its size (in bytes) */ |
| + | /* Intermediate buffer and its size (in bytes) */ |
| unsigned *dbuf, dbufSize; | |
| - | /* These things are a bit too big to go on the stack */ |
| + | /* For I/O error handling */ |
| + | jmp_buf jmpbuf; |
| + | /* Big things go last (register-relative addressing can be larger for big offsets */ |
| + | uint32_t crc32Table[256]; |
| unsigned char selectors[32768]; /* nSelectors=15 bits */ | |
| struct group_data groups[MAX_GROUPS]; /* Huffman coding tables */ | |
| + | }; |
| + | /* typedef struct bunzip_data bunzip_data; -- done in .h file */ |
| - | /* For I/O error handling */ |
| - | |
| - | jmp_buf jmpbuf; |
| - | } bunzip_data; |
| /* Return the next nnn bits of input. All reads from the compressed input | |
| are done through this function. All reads are big endian */ | |
| @@ -106,6 +103,7 @@ static unsigned get_bits(bunzip_data *bd, char bits_wanted) | |
| /* If we need to read more data from file into byte buffer, do so */ | |
| if (bd->inbufPos == bd->inbufCount) { | |
| + | /* if "no input fd" case: in_fd == -1, read fails, we jump */ |
| bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE); | |
| if (bd->inbufCount <= 0) | |
| longjmp(bd->jmpbuf, RETVAL_UNEXPECTED_INPUT_EOF); | |
| @@ -519,7 +517,7 @@ static int get_next_block(bunzip_data *bd) | |
| are ignored, data is written to out_fd and return is RETVAL_OK or error. | |
| */ | |
| - | static int read_bunzip(bunzip_data *bd, char *outbuf, int len) |
| + | int read_bunzip(bunzip_data *bd, char *outbuf, int len) |
| { | |
| const unsigned *dbuf; | |
| int pos, current, previous, gotcount; | |
| @@ -627,11 +625,16 @@ static int read_bunzip(bunzip_data *bd, char *outbuf, int len) | |
| goto decode_next_byte; | |
| } | |
| + | |
| /* Allocate the structure, read file header. If in_fd==-1, inbuf must contain | |
| a complete bunzip file (len bytes long). If in_fd!=-1, inbuf and len are | |
| ignored, and data is read from file handle into temporary buffer. */ | |
| - | static int start_bunzip(bunzip_data **bdp, int in_fd, unsigned char *inbuf, |
| + | /* Because bunzip2 is used for help text unpacking, and because bb_show_usage() |
| + | should work for NOFORK applets too, we must be extremely careful to not leak |
| + | any allocations! */ |
| + | |
| + | int start_bunzip(bunzip_data **bdp, int in_fd, const unsigned char *inbuf, |
| int len) | |
| { | |
| bunzip_data *bd; | |
| @@ -653,14 +656,15 @@ static int start_bunzip(bunzip_data **bdp, int in_fd, unsigned char *inbuf, | |
| bd->in_fd = in_fd; | |
| if (-1 == in_fd) { | |
| - | bd->inbuf = inbuf; |
| + | /* in this case, bd->inbuf is read-only */ |
| + | bd->inbuf = (void*)inbuf; /* cast away const-ness */ |
| bd->inbufCount = len; | |
| } else | |
| bd->inbuf = (unsigned char *)(bd + 1); | |
| /* Init the CRC32 table (big endian) */ | |
| - | bd->crc32Table = crc32_filltable(1); |
| + | crc32_filltable(bd->crc32Table, 1); |
| /* Setup for I/O error handling via longjmp */ | |
| @@ -670,19 +674,30 @@ static int start_bunzip(bunzip_data **bdp, int in_fd, unsigned char *inbuf, | |
| /* Ensure that file starts with "BZh['1'-'9']." */ | |
| i = get_bits(bd, 32); | |
| - | if (((unsigned)(i - BZh0 - 1)) >= 9) return RETVAL_NOT_BZIP_DATA; |
| + | if ((unsigned)(i - BZh0 - 1) >= 9) return RETVAL_NOT_BZIP_DATA; |
| /* Fourth byte (ascii '1'-'9'), indicates block size in units of 100k of | |
| uncompressed data. Allocate intermediate buffer for block. */ | |
| bd->dbufSize = 100000 * (i - BZh0); | |
| - | bd->dbuf = xmalloc(bd->dbufSize * sizeof(int)); |
| + | /* Cannot use xmalloc - may leak bd in NOFORK case! */ |
| + | bd->dbuf = malloc_or_warn(bd->dbufSize * sizeof(int)); |
| + | if (!bd->dbuf) { |
| + | free(bd); |
| + | xfunc_die(); |
| + | } |
| return RETVAL_OK; | |
| } | |
| - | /* Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data, |
| - | not end of file.) */ |
| + | void dealloc_bunzip(bunzip_data *bd) |
| + | { |
| + | free(bd->dbuf); |
| + | free(bd); |
| + | } |
| + | |
| + | |
| + | /* Decompress src_fd to dst_fd. Stops at end of bzip data, not end of file. */ |
| USE_DESKTOP(long long) int | |
| uncompressStream(int src_fd, int dst_fd) | |
| @@ -693,7 +708,7 @@ uncompressStream(int src_fd, int dst_fd) | |
| int i; | |
| outbuf = xmalloc(IOBUF_SIZE); | |
| - | i = start_bunzip(&bd, src_fd, 0, 0); |
| + | i = start_bunzip(&bd, src_fd, NULL, 0); |
| if (!i) { | |
| for (;;) { | |
| i = read_bunzip(bd, outbuf, IOBUF_SIZE); | |
| @@ -719,8 +734,7 @@ uncompressStream(int src_fd, int dst_fd) | |
| } else { | |
| bb_error_msg("decompression failed"); | |
| } | |
| - | free(bd->dbuf); |
| - | free(bd); |
| + | dealloc_bunzip(bd); |
| free(outbuf); | |
| return i ? i : USE_DESKTOP(total_written) + 0; | |
| @@ -1000,7 +1000,7 @@ inflate_unzip_internal(STATE_PARAM int in, int out) | |
| gunzip_bb = 0; | |
| /* Create the crc table */ | |
| - | gunzip_crc_table = crc32_filltable(0); |
| + | gunzip_crc_table = crc32_filltable(NULL, 0); |
| gunzip_crc = ~0; | |
| /* Allocate space for buffer */ | |
| @@ -3,8 +3,6 @@ | |
| * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | |
| */ | |
| - | #include <stdlib.h> |
| - | |
| #include "libbb.h" | |
| #include "unarchive.h" | |
| @@ -11,8 +11,7 @@ | |
| int cksum_main(int argc, char **argv); | |
| int cksum_main(int argc, char **argv) | |
| { | |
| - | |
| - | uint32_t *crc32_table = crc32_filltable(1); |
| + | uint32_t *crc32_table = crc32_filltable(NULL, 1); |
| FILE *fp; | |
| uint32_t crc; | |
| @@ -403,9 +403,10 @@ extern char *xasprintf(const char *format, ...) __attribute__ ((format (printf, | |
| /* dmalloc will redefine these to it's own implementation. It is safe | |
| * to have the prototypes here unconditionally. */ | |
| + | extern void *malloc_or_warn(size_t size); |
| extern void *xmalloc(size_t size); | |
| - | extern void *xrealloc(void *old, size_t size); |
| extern void *xzalloc(size_t size); | |
| + | extern void *xrealloc(void *old, size_t size); |
| extern ssize_t safe_read(int fd, void *buf, size_t count); | |
| extern ssize_t full_read(int fd, void *buf, size_t count); | |
| @@ -862,7 +863,7 @@ void md5_begin(md5_ctx_t *ctx); | |
| void md5_hash(const void *data, size_t length, md5_ctx_t *ctx); | |
| void *md5_end(void *resbuf, md5_ctx_t *ctx); | |
| - | uint32_t *crc32_filltable(int endian); |
| + | uint32_t *crc32_filltable(uint32_t *tbl256, int endian); |
| enum { /* DO NOT CHANGE THESE VALUES! cp.c, mv.c, install.c depend on them. */ | |
| @@ -102,6 +102,11 @@ extern const llist_t *find_list_entry(const llist_t *list, const char *filename) | |
| extern const llist_t *find_list_entry2(const llist_t *list, const char *filename); | |
| extern USE_DESKTOP(long long) int uncompressStream(int src_fd, int dst_fd); | |
| + | /* A bit of bunzip2 internals are exposed for compressed help support: */ |
| + | typedef struct bunzip_data bunzip_data; |
| + | int start_bunzip(bunzip_data **bdp, int in_fd, const unsigned char *inbuf, int len); |
| + | int read_bunzip(bunzip_data *bd, char *outbuf, int len); |
| + | void dealloc_bunzip(bunzip_data *bd); |
| typedef struct inflate_unzip_result { | |
| off_t bytes_out; | |
| @@ -16,14 +16,15 @@ | |
| #include "libbb.h" | |
| - | uint32_t *crc32_filltable(int endian) |
| + | uint32_t *crc32_filltable(uint32_t *crc_table, int endian) |
| { | |
| - | |
| - | uint32_t *crc_table = xmalloc(256 * sizeof(uint32_t)); |
| uint32_t polynomial = endian ? 0x04c11db7 : 0xedb88320; | |
| uint32_t c; | |
| int i, j; | |
| + | if (!crc_table) |
| + | crc_table = xmalloc(256 * sizeof(uint32_t)); |
| + | |
| for (i = 0; i < 256; i++) { | |
| c = endian ? (i << 24) : i; | |
| for (j = 8; j; j--) { | |
| @@ -102,120 +102,68 @@ int wait_pid(int *wstat, int pid) | |
| int spawn_and_wait(char **argv) | |
| { | |
| + | #if ENABLE_FEATURE_EXEC_PREFER_APPLETS |
| int rc; | |
| + | const struct bb_applet *a = find_applet_by_name(argv[0]); |
| - | #if ENABLE_FEATURE_EXEC_PREFER_APPLETS |
| - | { |
| - | const struct bb_applet *a = find_applet_by_name(argv[0]); |
| - | if (a && (a->nofork |
| + | if (a && (a->nofork |
| #ifndef BB_NOMMU | |
| - | || a->noexec /* NOEXEC cannot be used on NOMMU */ |
| + | || a->noexec /* NOEXEC cannot be used on NOMMU */ |
| #endif | |
| - | )) { |
| - | int argc = 1; |
| - | char **pp = argv; |
| - | while (*++pp) |
| - | argc++; |
| + | )) { |
| + | int argc = 1; |
| + | char **pp = argv; |
| + | while (*++pp) |
| + | argc++; |
| #ifndef BB_NOMMU | |
| - | if (a->nofork) |
| + | if (a->nofork) |
| #endif | |
| - | { |
| - | int old_sleep = die_sleep; |
| - | int old_x = xfunc_error_retval; |
| - | die_sleep = -1; /* special flag */ |
| - | /* xfunc_die() checks for it */ |
| - | |
| - | rc = setjmp(die_jmp); |
| - | if (!rc) { |
| - | const struct bb_applet *old_a = current_applet; |
| - | current_applet = a; |
| - | applet_name = a->name; |
| + | { |
| + | int old_sleep = die_sleep; |
| + | int old_x = xfunc_error_retval; |
| + | die_sleep = -1; /* special flag */ |
| + | /* xfunc_die() checks for it */ |
| + | |
| + | rc = setjmp(die_jmp); |
| + | if (!rc) { |
| + | const struct bb_applet *old_a = current_applet; |
| + | current_applet = a; |
| + | applet_name = a->name; |
| // what else should we save/restore? | |
| - | rc = a->main(argc, argv); |
| - | current_applet = old_a; |
| - | applet_name = old_a->name; |
| - | } else { |
| - | /* xfunc died in NOFORK applet */ |
| - | if (rc == -111) |
| - | rc = 0; |
| - | } |
| - | |
| - | die_sleep = old_sleep; |
| - | xfunc_error_retval = old_x; |
| - | return rc; |
| + | // TODO: what if applet will mangle argv vector? |
| + | // xargs needs argv untouched because it frees the vector! |
| + | // shouldn't we pass a copy? |
| + | rc = a->main(argc, argv); |
| + | current_applet = old_a; |
| + | applet_name = old_a->name; |
| + | } else { |
| + | /* xfunc died in NOFORK applet */ |
| + | if (rc == -111) |
| + | rc = 0; |
| } | |
| + | |
| + | die_sleep = old_sleep; |
| + | xfunc_error_retval = old_x; |
| + | return rc; |
| + | } |
| #ifndef BB_NOMMU /* MMU only */ | |
| - | /* a->noexec is true */ |
| - | rc = fork(); |
| - | if (rc) |
| - | goto w; |
| - | /* child */ |
| - | current_applet = a; |
| - | run_current_applet_and_exit(argc, argv); |
| + | /* a->noexec is true */ |
| + | rc = fork(); |
| + | if (rc) |
| + | goto w; |
| + | /* child */ |
| + | current_applet = a; |
| + | run_current_applet_and_exit(argc, argv); |
| #endif | |
| - | } |
| - | |
| } | |
| rc = spawn(argv); | |
| w: | |
| - | #else /* !FEATURE_EXEC_PREFER_APPLETS */ |
| - | rc = spawn(argv); |
| - | #endif /* FEATURE_EXEC_PREFER_APPLETS */ |
| return wait4pid(rc); | |
| - | } |
| - | |
| - | |
| - | #if 0 //ndef BB_NOMMU |
| - | // Die with an error message if we can't daemonize. |
| - | void xdaemon(int nochdir, int noclose) |
| - | { |
| - | if (daemon(nochdir, noclose)) |
| - | bb_perror_msg_and_die("daemon"); |
| - | } |
| + | #else /* !FEATURE_EXEC_PREFER_APPLETS */ |
| + | return wait4pid(spawn(argv)); |
| #endif | |
| - | |
| - | #if 0 // def BB_NOMMU |
| - | void vfork_daemon_rexec(int nochdir, int noclose, char **argv) |
| - | { |
| - | int fd; |
| - | |
| - | /* Maybe we are already re-execed and come here again? */ |
| - | if (re_execed) |
| - | return; |
| - | |
| - | setsid(); |
| - | |
| - | if (!nochdir) |
| - | xchdir("/"); |
| - | |
| - | if (!noclose) { |
| - | /* if "/dev/null" doesn't exist, bail out! */ |
| - | fd = xopen(bb_dev_null, O_RDWR); |
| - | dup2(fd, STDIN_FILENO); |
| - | dup2(fd, STDOUT_FILENO); |
| - | dup2(fd, STDERR_FILENO); |
| - | while (fd > 2) |
| - | close(fd--); |
| - | } |
| - | |
| - | switch (vfork()) { |
| - | case 0: /* child */ |
| - | /* Make certain we are not a session leader, or else we |
| - | * might reacquire a controlling terminal */ |
| - | if (vfork()) |
| - | _exit(0); |
| - | /* High-order bit of first char in argv[0] is a hidden |
| - | * "we have (alrealy) re-execed, don't do it again" flag */ |
| - | argv[0][0] |= 0x80; |
| - | execv(CONFIG_BUSYBOX_EXEC_PATH, argv); |
| - | bb_perror_msg_and_die("exec %s", CONFIG_BUSYBOX_EXEC_PATH); |
| - | case -1: /* error */ |
| - | bb_perror_msg_and_die("vfork"); |
| - | default: /* parent */ |
| - | exit(0); |
| - | } |
| } | |
| - | #endif /* BB_NOMMU */ |
| + | |
| #ifdef BB_NOMMU | |
| void forkexit_or_rexec(char **argv) | |
| @@ -20,6 +20,15 @@ | |
| * Since dmalloc's prototypes overwrite the impls here as they are | |
| * included after these prototypes in libbb.h, all is well. | |
| */ | |
| + | // Warn if we can't allocate size bytes of memory. |
| + | void *malloc_or_warn(size_t size) |
| + | { |
| + | void *ptr = malloc(size); |
| + | if (ptr == NULL && size != 0) |
| + | bb_error_msg(bb_msg_memory_exhausted); |
| + | return ptr; |
| + | } |
| + | |
| // Die if we can't allocate size bytes of memory. | |
| void *xmalloc(size_t size) | |
| { | |