make compressed help code NOMMU- and NOFORK-friendly -
authorDenis Vlasenko <vda.linux@googlemail.com>
Tue, 10 Apr 2007 21:40:19 +0000 (21:40 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Tue, 10 Apr 2007 21:40:19 +0000 (21:40 -0000)
no forking anymore, bunzip2 unpack routine now does all it in memory.

applets/applets.c
archival/gzip.c
archival/libunarchive/decompress_bunzip2.c
archival/libunarchive/decompress_unzip.c
archival/libunarchive/get_header_tar_gz.c
coreutils/cksum.c
include/libbb.h
include/unarchive.h
libbb/crc32.c
libbb/vfork_daemon_rexec.c
libbb/xfuncs.c

index 3441a7886314fa863b9f0b887442d2f690db58ba..2f677372df663f5ef3ad773aa1c2922aebc76fb8 100644 (file)
@@ -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();
 }
 
index 39391dfda8b0e9c3b0a64e04f37ef3ca693b5b6d..561f1088c12f26b9503601fe1f078982e75b36de 100644 (file)
@@ -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);
 }
index d4db40ecea53e7ef5ca00b0a26255bdb7c613ec3..3f0b0f6f22ba481ff75297806cf723e8f23c576a 100644 (file)
@@ -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;
index 331fe34d844eb2bc1d9c862bfede989ba68834bb..19ce5097ab46df13ad77e0850e146e0f57156cbb 100644 (file)
@@ -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 */
index 1f07e4e6488aaa3defe3d0f54de8496884263816..69126e0ba2bf81772dd877cb7509aaf024852550 100644 (file)
@@ -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"
 
index 66a255c4e4dc50094829f427ce26d6d870017c59..70febdf3ad2250d8665f3f14e8a47a5e2981cab3 100644 (file)
@@ -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;
index 6fff441c548534fb6003e157a8a91d7e6dc90929..2be13891cdbaf67eae992951d0856f85d201a041 100644 (file)
@@ -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. */
index 36b56a925d8938f0ad7031a9e12458701fcf2b71..8aa90beccfd59a46da922b972173e494b71b7823 100644 (file)
@@ -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;
index 1e4a57e8a27ef6d6f75ce5498102c42331930a53..acbc4582756763ee621326e8ed53d47f8091acd9 100644 (file)
 
 #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--) {
index dabd1a6d65f5bfcb107cd431818284d7fdc94203..cf88a2b28dfbc735c524a77e23ca38b8da7d536a 100644 (file)
@@ -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)
index b9d013a24b63388e918c7410d66729424cfd3b85..dde91a2bad5355f8f625e4415cca426a7662fc92 100644 (file)
  * 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)
 {