diff options
author | kientzle <kientzle@FreeBSD.org> | 2004-03-06 05:44:13 +0000 |
---|---|---|
committer | kientzle <kientzle@FreeBSD.org> | 2004-03-06 05:44:13 +0000 |
commit | f7afdba4b37e2450f10409676d148c7aa7c3bdf5 (patch) | |
tree | 5a99f404cc2142ae531473f27a1e473424f17a49 /lib/libarchive | |
parent | e795b7939d0e6a89ca34a05f6658f74df6c7644c (diff) | |
download | FreeBSD-src-f7afdba4b37e2450f10409676d148c7aa7c3bdf5.zip FreeBSD-src-f7afdba4b37e2450f10409676d148c7aa7c3bdf5.tar.gz |
Minor API tweak: The format-specific write_header function now sets
the size in the archive_entry object to zero if that format doesn't
store a body for that file type. This allows the client to determine
whether or not it should feed the file body to the archive. In
particular, cpio stores the file body for hardlinks, tar and shar
don't. With this change, bsdtar now correctly archives hardlinks in all
supported formats.
While I'm here, make shar output be more aggressive about creating directories.
Before this, commands such as:
bsdtar -cv -F shar some/explicit/path/to/a/file
wouldn't create the directory. Some simple logic to remember the last
directory creation helps reduce unnecessary mkdirs here.
At this point, I think the only flaw in libarchive's cpio support is
the failure to recognize hardlinks when reading.
Diffstat (limited to 'lib/libarchive')
-rw-r--r-- | lib/libarchive/archive_write_set_format_pax.c | 39 | ||||
-rw-r--r-- | lib/libarchive/archive_write_set_format_shar.c | 91 | ||||
-rw-r--r-- | lib/libarchive/archive_write_set_format_ustar.c | 16 |
3 files changed, 110 insertions, 36 deletions
diff --git a/lib/libarchive/archive_write_set_format_pax.c b/lib/libarchive/archive_write_set_format_pax.c index bee95d3..9b3d7819 100644 --- a/lib/libarchive/archive_write_set_format_pax.c +++ b/lib/libarchive/archive_write_set_format_pax.c @@ -458,6 +458,31 @@ archive_write_pax_header(struct archive *a, st_main->st_nlink); } + /* Only regular files have data. */ + if (!S_ISREG(archive_entry_mode(entry_main))) + archive_entry_set_size(entry_main, 0); + + /* + * Pax-restricted does not store data for hardlinks, in order + * to improve compatibility with ustar. + */ + if (a->archive_format != ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE && + archive_entry_hardlink(entry_main) != NULL) + archive_entry_set_size(entry_main, 0); + + /* + * XXX Full pax interchange format does permit a hardlink + * entry to have data associated with it. I'm not supporting + * that here because the client expects me to tell them whether + * or not this format expects data for hardlinks. If I + * don't check here, then every pax archive will end up with + * duplicated data for hardlinks. Someday, there may be + * need to select this behavior, in which case the following + * will need to be revisited. XXX + */ + if (archive_entry_hardlink(entry_main) != NULL) + archive_entry_set_size(entry_main, 0); + /* Format 'ustar' header for main entry. */ /* We don't care if this returns an error. */ __archive_write_format_header_ustar(a, ustarbuff, entry_main); @@ -528,13 +553,13 @@ archive_write_pax_header(struct archive *a, if (ret != ARCHIVE_OK) ret = (r < 512) ? ARCHIVE_FATAL : ARCHIVE_OK; - /* Only regular files have data. Note that pax, unlike ustar, - * does permit a hardlink to have data associated with it. */ - if (!S_ISREG(archive_entry_mode(entry_main))) - pax->entry_bytes_remaining = 0; - else - pax->entry_bytes_remaining = archive_entry_size(entry_main); - + /* + * Inform the client of the on-disk size we're using, so + * they can avoid unnecessarily writing a body for something + * that we're just going to ignore. + */ + archive_entry_set_size(entry_original, archive_entry_size(entry_main)); + pax->entry_bytes_remaining = archive_entry_size(entry_main); pax->entry_padding = 0x1ff & (- pax->entry_bytes_remaining); archive_entry_free(entry_main); diff --git a/lib/libarchive/archive_write_set_format_shar.c b/lib/libarchive/archive_write_set_format_shar.c index 52f5509..676f997 100644 --- a/lib/libarchive/archive_write_set_format_shar.c +++ b/lib/libarchive/archive_write_set_format_shar.c @@ -56,6 +56,7 @@ struct shar { int end_of_line; struct archive_entry *entry; int has_data; + char *last_dir; char outbuff[1024]; size_t outbytes; size_t outpos; @@ -130,6 +131,7 @@ archive_write_shar_header(struct archive *a, struct archive_entry *entry) { const char *linkname; const char *name; + char *p, *pp; struct shar *shar; const struct stat *st; @@ -140,28 +142,74 @@ archive_write_shar_header(struct archive *a, struct archive_entry *entry) shar->wrote_header = 1; } - /* Save the entry for the closing */ + /* Save the entry for the closing. */ if (shar->entry) archive_entry_free(shar->entry); shar->entry = archive_entry_clone(entry); name = archive_entry_pathname(entry); st = archive_entry_stat(entry); + /* Handle some preparatory issues. */ + switch(st->st_mode & S_IFMT) { + case S_IFREG: + /* Only regular files have non-zero size. */ + break; + case S_IFDIR: + case S_IFIFO: + case S_IFCHR: + case S_IFBLK: + /* All other file types have zero size in the archive. */ + archive_entry_set_size(entry, 0); + break; + default: + archive_entry_set_size(entry, 0); + if (archive_entry_hardlink(entry) == NULL && + archive_entry_symlink(entry) == NULL) { + archive_set_error(a, -1, + "shar format cannot archive this"); + return (ARCHIVE_WARN); + } + } + + /* Stock preparation for all file types. */ + shar_printf(a, "echo x %s\n", name); + + if (!S_ISDIR(st->st_mode)) { + /* Try to create the dir. */ + p = strdup(name); + pp = strrchr(p, '/'); + if (pp != NULL) + *pp = '\0'; + + if (shar->last_dir == NULL) { + shar_printf(a, "mkdir -p %s > /dev/null 2>&1\n", p); + shar->last_dir = p; + } else if (strcmp(p, shar->last_dir) == 0) { + /* We've already created this exact dir. */ + free(p); + } else if (strlen(p) < strlen(shar->last_dir) && + strncmp(p, shar->last_dir, strlen(p)) == 0) { + /* We've already created a subdir. */ + free(p); + } else { + shar_printf(a, "mkdir -p %s > /dev/null 2>&1\n", p); + free(shar->last_dir); + shar->last_dir = p; + } + } + + /* Handle file-type specific issues. */ shar->has_data = 0; - if ((linkname = archive_entry_hardlink(entry)) != NULL) { - shar_printf(a, "echo x %s\n", name); + if ((linkname = archive_entry_hardlink(entry)) != NULL) shar_printf(a, "ln -f %s %s\n", linkname, name); - } else if ((linkname = archive_entry_symlink(entry)) != NULL) { - shar_printf(a, "echo x %s\n", name); + else if ((linkname = archive_entry_symlink(entry)) != NULL) shar_printf(a, "ln -fs %s %s\n", linkname, name); - } else { + else { switch(st->st_mode & S_IFMT) { case S_IFREG: - shar_printf(a, "echo x %s\n", name); - if (archive_entry_size(entry) == 0) { + if (archive_entry_size(entry) == 0) shar_printf(a, "touch %s\n", name); - shar->has_data = 0; - } else { + else { if (shar->dump) { shar_printf(a, "uudecode -o %s << 'SHAR_END'\n", @@ -180,36 +228,35 @@ archive_write_shar_header(struct archive *a, struct archive_entry *entry) } break; case S_IFDIR: - shar_printf(a, "echo x %s\n", name); shar_printf(a, "mkdir -p %s > /dev/null 2>&1\n", name); + /* Record that we just created this directory. */ + if (shar->last_dir != NULL) + free(shar->last_dir); + + shar->last_dir = strdup(name); + /* Trim a trailing '/'. */ + pp = strrchr(shar->last_dir, '/'); + if (pp != NULL && pp[1] == '\0') + *pp = '\0'; /* * TODO: Put dir name/mode on a list to be fixed * up at end of archive. */ break; case S_IFIFO: - shar_printf(a, "echo x %s\n", name); shar_printf(a, "mkfifo %s\n", name); break; case S_IFCHR: - shar_printf(a, "echo x %s\n", name); shar_printf(a, "mknod %s c %d %d\n", name, archive_entry_devmajor(entry), archive_entry_devminor(entry)); break; case S_IFBLK: - shar_printf(a, "echo x %s\n", name); shar_printf(a, "mknod %s b %d %d\n", name, archive_entry_devmajor(entry), archive_entry_devminor(entry)); break; - case S_IFSOCK: - archive_set_error(a, -1, - "shar format cannot archive socket"); - return (ARCHIVE_WARN); default: - archive_set_error(a, -1, - "shar format cannot archive this"); return (ARCHIVE_WARN); } } @@ -395,8 +442,10 @@ archive_write_shar_finish(struct archive *a) * uncompressed data within gzip/bzip2 streams. */ } - if (shar->entry) + if (shar->entry != NULL) archive_entry_free(shar->entry); + if (shar->last_dir != NULL) + free(shar->last_dir); free(shar); a->format_data = NULL; return (ARCHIVE_OK); diff --git a/lib/libarchive/archive_write_set_format_ustar.c b/lib/libarchive/archive_write_set_format_ustar.c index 1a67a1d..649be5e 100644 --- a/lib/libarchive/archive_write_set_format_ustar.c +++ b/lib/libarchive/archive_write_set_format_ustar.c @@ -125,6 +125,13 @@ archive_write_ustar_header(struct archive *a, struct archive_entry *entry) ustar = a->format_data; ustar->written = 1; + + /* Only regular files (not hardlinks) have data. */ + if (archive_entry_hardlink(entry) != NULL || + archive_entry_symlink(entry) != NULL || + !S_ISREG(archive_entry_mode(entry))) + archive_entry_set_size(entry, 0); + ret = __archive_write_format_header_ustar(a, buff, entry); if (ret != ARCHIVE_OK) return (ret); @@ -132,14 +139,7 @@ archive_write_ustar_header(struct archive *a, struct archive_entry *entry) if (ret < 512) return (ARCHIVE_FATAL); - /* Only regular files (not hardlinks) have data. */ - if (archive_entry_hardlink(entry) != NULL || - archive_entry_symlink(entry) != NULL || - !S_ISREG(archive_entry_mode(entry))) - ustar->entry_bytes_remaining = 0; - else - ustar->entry_bytes_remaining = archive_entry_size(entry); - + ustar->entry_bytes_remaining = archive_entry_size(entry); ustar->entry_padding = 0x1ff & (- ustar->entry_bytes_remaining); return (ARCHIVE_OK); } |