no forking anymore, bunzip2 unpack routine now does all it in memory.
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 */
{
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();
}
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);
}
/* 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 */
/* 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);
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;
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;
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 */
/* 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)
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);
} else {
bb_error_msg("decompression failed");
}
- free(bd->dbuf);
- free(bd);
+ dealloc_bunzip(bd);
free(outbuf);
return i ? i : USE_DESKTOP(total_written) + 0;
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 */
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
-#include <stdlib.h>
-
#include "libbb.h"
#include "unarchive.h"
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;
/* 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);
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. */
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;
#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--) {
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)
* 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)
{