diff options
author | Tim Kientzle <kientzle@FreeBSD.org> | 2004-02-09 23:22:54 +0000 |
---|---|---|
committer | Tim Kientzle <kientzle@FreeBSD.org> | 2004-02-09 23:22:54 +0000 |
commit | 2710e4d1ef34d45be2425905dd5b4282d29600ef (patch) | |
tree | 20e1d80fd0a1d288a08af1696ee258bcd08f41d0 /lib/libarchive/archive_write_set_format_cpio.c | |
parent | 716563e20c4c36b3b688424563fc83f2b4c6a4b7 (diff) |
Initial import of libarchive.
What it is:
A library for reading and writing various streaming archive
formats, especially tar and cpio. Being a library, it should
be easy to incorporate into pkg_* tools, sysinstall, and any
other place that needs to read or write such archives.
Features:
* Full automatic detection of both compression and archive format.
* Extensible internal architecture to make it easy to add new formats.
* Support for "pax interchange format," a new POSIX-standard tar format
that eliminates essentially all of the restrictions of historic formats.
* BSD license
Thanks to: jkh for pushing me to start this work, gordon for
encouraging me to commit it, bde for answering endless style
questions, and many others for feedback and encouragement.
Status: Pretty good overall, though there are still a few rough edges and
the library could always use more testing. Feedback eagerly solicited.
Notes
Notes:
svn path=/head/; revision=125635
Diffstat (limited to 'lib/libarchive/archive_write_set_format_cpio.c')
-rw-r--r-- | lib/libarchive/archive_write_set_format_cpio.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/lib/libarchive/archive_write_set_format_cpio.c b/lib/libarchive/archive_write_set_format_cpio.c new file mode 100644 index 000000000000..f0df03ef1def --- /dev/null +++ b/lib/libarchive/archive_write_set_format_cpio.c @@ -0,0 +1,244 @@ +/*- + * Copyright (c) 2003-2004 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/stat.h> +#ifdef DMALLOC +#include <dmalloc.h> +#endif +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "archive.h" +#include "archive_entry.h" +#include "archive_private.h" + +static int archive_write_cpio_data(struct archive *, const void *buff, + size_t s); +static int archive_write_cpio_finish(struct archive *); +static int archive_write_cpio_finish_entry(struct archive *); +static int archive_write_cpio_header(struct archive *, + struct archive_entry *); +static int format_octal(int64_t, void *, int); +static int64_t format_octal_recursive(int64_t, char *, int); + +struct cpio { + uint64_t entry_bytes_remaining; +}; + +struct cpio_header { + char c_magic[6]; + char c_dev[6]; + char c_ino[6]; + char c_mode[6]; + char c_uid[6]; + char c_gid[6]; + char c_nlink[6]; + char c_rdev[6]; + char c_mtime[11]; + char c_namesize[6]; + char c_filesize[11]; +}; + +/* + * Set output format to 'cpio' format. + */ +int +archive_write_set_format_cpio(struct archive *a) +{ + struct cpio *cpio; + + /* If someone else was already registered, unregister them. */ + if (a->format_finish != NULL) + (a->format_finish)(a); + + cpio = malloc(sizeof(*cpio)); + if (cpio == NULL) { + archive_set_error(a, ENOMEM, "Can't allocate cpio data"); + return (ARCHIVE_FATAL); + } + memset(cpio, 0, sizeof(*cpio)); + a->format_data = cpio; + + a->pad_uncompressed = 1; + a->format_write_header = archive_write_cpio_header; + a->format_write_data = archive_write_cpio_data; + a->format_finish_entry = archive_write_cpio_finish_entry; + a->format_finish = archive_write_cpio_finish; + a->archive_format = ARCHIVE_FORMAT_CPIO_POSIX; + a->archive_format_name = "POSIX cpio"; + return (ARCHIVE_OK); +} + +static int +archive_write_cpio_header(struct archive *a, struct archive_entry *entry) +{ + struct cpio *cpio; + const char *p, *path; + int pathlength, ret, written; + const struct stat *st; + struct cpio_header h; + + cpio = a->format_data; + ret = 0; + + path = archive_entry_pathname(entry); + pathlength = strlen(path) + 1; /* Include trailing null. */ + st = archive_entry_stat(entry); + + memset(&h, 0, sizeof(h)); + format_octal(070707, &h.c_magic, sizeof(h.c_magic)); + format_octal(st->st_dev, &h.c_dev, sizeof(h.c_dev)); + /* + * TODO: Generate artificial inode numbers rather than just + * re-using the ones off the disk. That way, the 18-bit c_ino + * field only limits the number of files in the archive. + */ + if (st->st_ino > 0777777) { + archive_set_error(a, ERANGE, "large inode number truncated"); + ret = ARCHIVE_WARN; + } + + format_octal(st->st_ino & 0777777, &h.c_ino, sizeof(h.c_ino)); + format_octal(st->st_mode, &h.c_mode, sizeof(h.c_mode)); + format_octal(st->st_uid, &h.c_uid, sizeof(h.c_uid)); + format_octal(st->st_gid, &h.c_gid, sizeof(h.c_gid)); + format_octal(st->st_nlink, &h.c_nlink, sizeof(h.c_nlink)); + if(S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) + format_octal(st->st_rdev, &h.c_rdev, sizeof(h.c_rdev)); + else + format_octal(0, &h.c_rdev, sizeof(h.c_rdev)); + format_octal(st->st_mtime, &h.c_mtime, sizeof(h.c_mtime)); + format_octal(pathlength, &h.c_namesize, sizeof(h.c_namesize)); + + /* Symlinks get the link written as the body of the entry. */ + p = archive_entry_symlink(entry); + if (p != NULL && *p != '\0') + format_octal(strlen(p), &h.c_filesize, sizeof(h.c_filesize)); + else + format_octal(st->st_size, &h.c_filesize, sizeof(h.c_filesize)); + + written = (a->compression_write)(a, &h, sizeof(h)); + if (written < (int)sizeof(h)) + return (ARCHIVE_FATAL); + + written = (a->compression_write)(a, path, pathlength); + if (written < (int)pathlength) + return (ARCHIVE_FATAL); + + cpio->entry_bytes_remaining = st->st_size; + + /* Write the symlink now. */ + if (p != NULL && *p != '\0') + (a->compression_write)(a, p, strlen(p)); + + return (ret); +} + +static int +archive_write_cpio_data(struct archive *a, const void *buff, size_t s) +{ + struct cpio *cpio; + int ret; + + cpio = a->format_data; + if (s > cpio->entry_bytes_remaining) + s = cpio->entry_bytes_remaining; + + ret = (a->compression_write)(a, buff, s); + cpio->entry_bytes_remaining -= s; + return (ret); +} + +/* + * Format a number into the specified field. + */ +static int +format_octal(int64_t v, void *p, int digits) +{ + int64_t max; + int ret; + + max = (((int64_t)1) << (digits * 3)) - 1; + if (v >= 0 && v <= max) { + format_octal_recursive(v, p, digits); + ret = 0; + } else { + format_octal_recursive(max, p, digits); + ret = -1; + } + return (ret); +} + +static int64_t +format_octal_recursive(int64_t v, char *p, int s) +{ + if (s == 0) + return (v); + v = format_octal_recursive(v, p+1, s-1); + *p = '0' + (v & 7); + return (v >>= 3); +} + +static int +archive_write_cpio_finish(struct archive *a) +{ + struct stat st; + int er; + struct archive_entry *trailer; + + trailer = archive_entry_new(); + memset(&st, 0, sizeof(st)); + st.st_nlink = 1; + archive_entry_copy_stat(trailer, &st); + archive_entry_set_pathname(trailer, "TRAILER!!!"); + er = archive_write_cpio_header(a, trailer); + archive_entry_free(trailer); + return (er); +} + +static int +archive_write_cpio_finish_entry(struct archive *a) +{ + struct cpio *cpio; + int to_write, ret; + + cpio = a->format_data; + ret = 0; + while (cpio->entry_bytes_remaining > 0) { + to_write = cpio->entry_bytes_remaining < a->null_length ? + cpio->entry_bytes_remaining : a->null_length; + ret = (a->compression_write)(a, a->nulls, to_write); + if (ret < to_write) + return (-1); + cpio->entry_bytes_remaining -= to_write; + } + return (ret); +} |