]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Fix a handful of bugs in untar.c and significantly extend the opening
authorTim Kientzle <kientzle@gmail.com>
Sat, 29 Aug 2009 03:42:55 +0000 (23:42 -0400)
committerTim Kientzle <kientzle@gmail.com>
Sat, 29 Aug 2009 03:42:55 +0000 (23:42 -0400)
commentary, which hasn't really been updated since 2006 or so.  In
particular, I went back and rechecked the sizes on modern FreeBSD and
found that things have bloated quite a bit: statically compiled,
untar.c is now about 25% larger than hello.c, where it used to be
almost 25% smaller.

SVN-Revision: 1396

examples/untar.c

index 468c1079e1d9a6810ccc1cb663e6ed7b5c780c6f..d6870de45657e686f7d59caf31d4ab6c7cf506dc 100644 (file)
@@ -6,28 +6,50 @@
 /*
  * This is a compact tar extraction program using libarchive whose
  * primary goal is small executable size.  Statically linked, it can
- * be under 64k, depending on how cleanly factored your system
- * libraries are.  Note that this uses the standard libarchive,
+ * be very small, depending in large part on how cleanly factored your
+ * system libraries are.  Note that this uses the standard libarchive,
  * without any special recompilation.  The only functional concession
  * is that this program uses the uid/gid from the archive instead of
- * doing uname/gname lookups.  (Call
+ * doing uname/gname lookups.  (Add a call to
  * archive_write_disk_set_standard_lookup() to enable uname/gname
  * lookups, but be aware that this can add 500k or more to a static
- * executable, depending on the system libraries.)
+ * executable, depending on the system libraries, since user/group
+ * lookups frequently pull in password, YP/LDAP, networking, and DNS
+ * resolver libraries.)
  *
  * To build:
- * gcc -static -Wall -o untar untar.c -larchive
- * strip untar
+ * $ gcc -static -Wall -o untar untar.c -larchive
+ * $ strip untar
+ *
+ * NOTE: On some systems, you may need to add additional flags
+ * to ensure that untar.c is compiled the same way as libarchive
+ * was compiled.  In particular, Linux users will probably
+ * have to add -D_FILE_OFFSET_BITS=64 to the command line above.
  *
  * For fun, statically compile the following simple hello.c program
- * and compare the size.  (On my system, the result is 89k, untar is
- * 69k.)
+ * using the same flags as for untar and compare the size:
  *
  * #include <stdio.h>
  * int main(int argc, char **argv) {
  *    printf("hello, world\n");
  *    return(0);
  * }
+ *
+ * You may be even more surprised by the compiled size of true.c listed here:
+ *
+ * int main(int argc, char **argv) {
+ *    return (0);
+ * }
+ *
+ * On a slightly customized FreeBSD 5 system that I used around
+ * 2005, hello above compiled to 89k compared to untar of 69k.  So at
+ * that time, libarchive's tar reader and extract-to-disk routines
+ * compiled to less code than printf().
+ *
+ * On my FreeBSD development system today (August, 2009):
+ *  hello: 195024 bytes
+ *  true: 194912 bytes
+ *  untar: 259924 bytes
  */
 
 #include <sys/types.h>
@@ -45,9 +67,11 @@ __FBSDID("$FreeBSD$");
 
 static void    errmsg(const char *);
 static void    extract(const char *filename, int do_extract, int flags);
+static void    fail(const char *, const char *, int);
 static int     copy_data(struct archive *, struct archive *);
 static void    msg(const char *);
 static void    usage(void);
+static void    warn(const char *, const char *);
 
 static int verbose = 0;
 
@@ -134,20 +158,16 @@ extract(const char *filename, int do_extract, int flags)
         */
        if (filename != NULL && strcmp(filename, "-") == 0)
                filename = NULL;
-       if ((r = archive_read_open_file(a, filename, 10240))) {
-               errmsg(archive_error_string(a));
-               errmsg("\n");
-               exit(r);
-       }
+       if ((r = archive_read_open_file(a, filename, 10240)))
+               fail("archive_read_open_file()",
+                   archive_error_string(a), r);
        for (;;) {
                r = archive_read_next_header(a, &entry);
                if (r == ARCHIVE_EOF)
                        break;
-               if (r != ARCHIVE_OK) {
-                       errmsg(archive_error_string(a));
-                       errmsg("\n");
-                       exit(1);
-               }
+               if (r != ARCHIVE_OK)
+                       fail("archive_read_next_header()",
+                           archive_error_string(a), 1);
                if (verbose && do_extract)
                        msg("x ");
                if (verbose || !do_extract)
@@ -155,9 +175,16 @@ extract(const char *filename, int do_extract, int flags)
                if (do_extract) {
                        r = archive_write_header(ext, entry);
                        if (r != ARCHIVE_OK)
-                               errmsg(archive_error_string(a));
-                       else
+                               warn("archive_write_header()",
+                                   archive_error_string(a));
+                       else {
                                copy_data(a, ext);
+                               r = archive_write_finish_entry(ext);
+                               if (r != ARCHIVE_OK)
+                                       fail("archive_write_finish_entry()",
+                                           archive_error_string(ext), 1);
+                       }
+
                }
                if (verbose || !do_extract)
                        msg("\n");
@@ -177,20 +204,27 @@ copy_data(struct archive *ar, struct archive *aw)
 
        for (;;) {
                r = archive_read_data_block(ar, &buff, &size, &offset);
-               if (r == ARCHIVE_EOF) {
-                       errmsg(archive_error_string(ar));
+               if (r == ARCHIVE_EOF)
                        return (ARCHIVE_OK);
-               }
                if (r != ARCHIVE_OK)
                        return (r);
                r = archive_write_data_block(aw, buff, size, offset);
                if (r != ARCHIVE_OK) {
-                       errmsg(archive_error_string(ar));
+                       warn("archive_write_data_block()",
+                           archive_error_string(aw));
                        return (r);
                }
        }
 }
 
+/*
+ * These reporting functions use low-level I/O; on some systems, this
+ * is a significant code reduction.  Of course, on many server and
+ * desktop operating systems, malloc() and even crt rely on printf(),
+ * which in turn pulls in most of the rest of stdio, so this is not an
+ * optimization at all there.  (If you're going to pay 100k or more
+ * for printf() anyway, you may as well use it!)
+ */
 static void
 msg(const char *m)
 {
@@ -203,6 +237,22 @@ errmsg(const char *m)
        write(2, m, strlen(m));
 }
 
+static void
+warn(const char *f, const char *m)
+{
+       errmsg(f);
+       errmsg(" failed: ");
+       errmsg(m);
+       errmsg("\n");
+}
+
+static void
+fail(const char *f, const char *m, int r)
+{
+       warn(f, m);
+       exit(r);
+}
+
 static void
 usage(void)
 {