Merge pull request #1502 from tih/cpio-bin-formats
Support for PWB and v7 binary cpio formats
This commit is contained in:
24 files changed
+1389
-605
No files matched your search
@@ -224,7 +224,9 @@ libarchive_la_SOURCES= \
|
||||
libarchive/archive_write_set_format_ar.c \
|
||||
libarchive/archive_write_set_format_by_name.c \
|
||||
libarchive/archive_write_set_format_cpio.c \
|
||||
libarchive/archive_write_set_format_cpio_binary.c \
|
||||
libarchive/archive_write_set_format_cpio_newc.c \
|
||||
libarchive/archive_write_set_format_cpio_odc.c \
|
||||
libarchive/archive_write_set_format_filter_by_ext.c \
|
||||
libarchive/archive_write_set_format_iso9660.c \
|
||||
libarchive/archive_write_set_format_mtree.c \
|
||||
|
||||
@@ -79,6 +79,7 @@ Currently, the library automatically detects and reads the following formats:
|
||||
* POSIX octet-oriented cpio
|
||||
* SVR4 ASCII cpio
|
||||
* Binary cpio (big-endian or little-endian)
|
||||
* PWB binary cpio
|
||||
* ISO9660 CD-ROM images (with optional Rockridge or Joliet extensions)
|
||||
* ZIP archives (with uncompressed or "deflate" compressed entries, including support for encrypted Zip archives)
|
||||
* ZIPX archives (with support for bzip2, ppmd8, lzma and xz compressed entries)
|
||||
@@ -110,6 +111,8 @@ The library can create archives in any of the following formats:
|
||||
* Old V7 tar format
|
||||
* POSIX octet-oriented cpio
|
||||
* SVR4 "newc" cpio
|
||||
* Binary cpio (little-endian)
|
||||
* PWB binary cpio
|
||||
* shar archives
|
||||
* ZIP archives (with uncompressed or "deflate" compressed entries)
|
||||
* GNU and BSD 'ar' archives
|
||||
|
||||
@@ -82,6 +82,13 @@ all operating modes.
|
||||
.It Fl 0 , Fl Fl null
|
||||
Read filenames separated by NUL characters instead of newlines.
|
||||
This is necessary if any of the filenames being read might contain newlines.
|
||||
.It Fl 6 , Fl Fl pwb
|
||||
When reading a binary format archive, assume it's the earlier one,
|
||||
from the PWB variant of 6th Edition UNIX.
|
||||
When writing a cpio archive, use the PWB format.
|
||||
.It Fl 7 , Fl Fl binary
|
||||
(o mode only)
|
||||
When writing a cpio archive, use the (newer, non-PWB) binary format.
|
||||
.It Fl A
|
||||
(o mode only)
|
||||
Append to the specified archive.
|
||||
|
||||
+3
-1
@@ -51,7 +51,7 @@ __FBSDID("$FreeBSD: src/usr.bin/cpio/cmdline.c,v 1.5 2008/12/06 07:30:40 kientzl
|
||||
/*
|
||||
* Short options for cpio. Please keep this sorted.
|
||||
*/
|
||||
static const char *short_options = "0AaBC:cdE:F:f:H:hI:iJjLlmnO:opR:rtuVvW:yZz";
|
||||
static const char *short_options = "067AaBC:cdE:F:f:H:hI:iJjLlmnO:opR:rtuVvW:yZz";
|
||||
|
||||
/*
|
||||
* Long options for cpio. Please keep this sorted.
|
||||
@@ -62,6 +62,7 @@ static const struct option {
|
||||
int equivalent; /* Equivalent short option. */
|
||||
} cpio_longopts[] = {
|
||||
{ "b64encode", 0, OPTION_B64ENCODE },
|
||||
{ "binary", 0, '7' },
|
||||
{ "create", 0, 'o' },
|
||||
{ "dereference", 0, 'L' },
|
||||
{ "dot", 0, 'V' },
|
||||
@@ -86,6 +87,7 @@ static const struct option {
|
||||
{ "pass-through", 0, 'p' },
|
||||
{ "preserve-modification-time", 0, 'm' },
|
||||
{ "preserve-owner", 0, OPTION_PRESERVE_OWNER },
|
||||
{ "pwb", 0, '6' },
|
||||
{ "quiet", 0, OPTION_QUIET },
|
||||
{ "unconditional", 0, 'u' },
|
||||
{ "uuencode", 0, OPTION_UUENCODE },
|
||||
|
||||
+17
-6
@@ -192,6 +192,12 @@ main(int argc, char *argv[])
|
||||
case '0': /* GNU convention: --null, -0 */
|
||||
cpio->option_null = 1;
|
||||
break;
|
||||
case '6': /* in/out: assume/create 6th edition (PWB) format */
|
||||
cpio->option_pwb = 1;
|
||||
break;
|
||||
case '7': /* out: create archive using 7th Edition binary format */
|
||||
cpio->format = "bin";
|
||||
break;
|
||||
case 'A': /* NetBSD/OpenBSD */
|
||||
cpio->option_append = 1;
|
||||
break;
|
||||
@@ -400,11 +406,12 @@ main(int argc, char *argv[])
|
||||
|
||||
switch (cpio->mode) {
|
||||
case 'o':
|
||||
/* TODO: Implement old binary format in libarchive,
|
||||
use that here. */
|
||||
if (cpio->format == NULL)
|
||||
cpio->format = "odc"; /* Default format */
|
||||
|
||||
if (cpio->format == NULL) {
|
||||
if (cpio->option_pwb)
|
||||
cpio->format = "pwb";
|
||||
else
|
||||
cpio->format = "cpio";
|
||||
}
|
||||
mode_out(cpio);
|
||||
break;
|
||||
case 'i':
|
||||
@@ -462,7 +469,7 @@ static const char *long_help_msg =
|
||||
" -v Verbose filenames -V one dot per file\n"
|
||||
"Create: %p -o [options] < [list of files] > [archive]\n"
|
||||
" -J,-y,-z,--lzma Compress archive with xz/bzip2/gzip/lzma\n"
|
||||
" --format {odc|newc|ustar} Select archive format\n"
|
||||
" --format {pwb|bin|odc|newc|ustar} Select archive format\n"
|
||||
"List: %p -it < [archive]\n"
|
||||
"Extract: %p -i [options] < [archive]\n";
|
||||
|
||||
@@ -970,6 +977,8 @@ mode_in(struct cpio *cpio)
|
||||
lafe_errc(1, 0, "Couldn't allocate archive object");
|
||||
archive_read_support_filter_all(a);
|
||||
archive_read_support_format_all(a);
|
||||
if (cpio->option_pwb)
|
||||
archive_read_set_options(a, "pwb");
|
||||
if (cpio->passphrase != NULL)
|
||||
r = archive_read_add_passphrase(a, cpio->passphrase);
|
||||
else
|
||||
@@ -1080,6 +1089,8 @@ mode_list(struct cpio *cpio)
|
||||
lafe_errc(1, 0, "Couldn't allocate archive object");
|
||||
archive_read_support_filter_all(a);
|
||||
archive_read_support_format_all(a);
|
||||
if (cpio->option_pwb)
|
||||
archive_read_set_options(a, "pwb");
|
||||
if (cpio->passphrase != NULL)
|
||||
r = archive_read_add_passphrase(a, cpio->passphrase);
|
||||
else
|
||||
|
||||
@@ -62,6 +62,7 @@ struct cpio {
|
||||
int option_list; /* -t */
|
||||
char option_null; /* --null */
|
||||
int option_numeric_uid_gid; /* -n */
|
||||
int option_pwb; /* -6 */
|
||||
int option_rename; /* -r */
|
||||
char *destdir;
|
||||
size_t destdir_len;
|
||||
|
||||
@@ -230,6 +230,8 @@ DEFINE_TEST(test_basic)
|
||||
basic_cpio("copy_odc", "--format=odc", "", msg, msg);
|
||||
basic_cpio("copy_newc", "-H newc", "", result, "2 blocks\n");
|
||||
basic_cpio("copy_cpio", "-H odc", "", msg, msg);
|
||||
msg = "1 block\n";
|
||||
basic_cpio("copy_bin", "-H bin", "", msg, msg);
|
||||
msg = canSymlink() ? "9 blocks\n" : "8 blocks\n";
|
||||
basic_cpio("copy_ustar", "-H ustar", "", msg, msg);
|
||||
|
||||
|
||||
@@ -144,7 +144,9 @@ SET(libarchive_SOURCES
|
||||
archive_write_set_format_ar.c
|
||||
archive_write_set_format_by_name.c
|
||||
archive_write_set_format_cpio.c
|
||||
archive_write_set_format_cpio_binary.c
|
||||
archive_write_set_format_cpio_newc.c
|
||||
archive_write_set_format_cpio_odc.c
|
||||
archive_write_set_format_filter_by_ext.c
|
||||
archive_write_set_format_gnutar.c
|
||||
archive_write_set_format_iso9660.c
|
||||
|
||||
@@ -319,6 +319,7 @@ typedef const char *archive_passphrase_callback(struct archive *,
|
||||
#define ARCHIVE_FORMAT_CPIO_SVR4_NOCRC (ARCHIVE_FORMAT_CPIO | 4)
|
||||
#define ARCHIVE_FORMAT_CPIO_SVR4_CRC (ARCHIVE_FORMAT_CPIO | 5)
|
||||
#define ARCHIVE_FORMAT_CPIO_AFIO_LARGE (ARCHIVE_FORMAT_CPIO | 6)
|
||||
#define ARCHIVE_FORMAT_CPIO_PWB (ARCHIVE_FORMAT_CPIO | 7)
|
||||
#define ARCHIVE_FORMAT_SHAR 0x20000
|
||||
#define ARCHIVE_FORMAT_SHAR_BASE (ARCHIVE_FORMAT_SHAR | 1)
|
||||
#define ARCHIVE_FORMAT_SHAR_DUMP (ARCHIVE_FORMAT_SHAR | 2)
|
||||
@@ -800,7 +801,10 @@ __LA_DECL int archive_write_set_format_7zip(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_ar_bsd(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_ar_svr4(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_cpio(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_cpio_bin(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_cpio_newc(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_cpio_odc(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_cpio_pwb(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_gnutar(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_iso9660(struct archive *);
|
||||
__LA_DECL int archive_write_set_format_mtree(struct archive *);
|
||||
|
||||
@@ -188,9 +188,18 @@ used when translating file names.
|
||||
.El
|
||||
.It Format cpio
|
||||
.Bl -tag -compact -width indent
|
||||
.It Cm compat-2x
|
||||
Libarchive 2.x incorrectly encoded Unicode filenames on
|
||||
some platforms.
|
||||
This option mimics the libarchive 2.x filename handling
|
||||
so that such archives can be read correctly.
|
||||
.It Cm hdrcharset
|
||||
The value is used as a character set name that will be
|
||||
used when translating file names.
|
||||
.It Cm pwb
|
||||
When reading a binary CPIO archive, assume that it is
|
||||
in the original PWB cpio format, and handle file mode
|
||||
bits accordingly. The default is to assume v7 format.
|
||||
.El
|
||||
.It Format iso9660
|
||||
.Bl -tag -compact -width indent
|
||||
|
||||
@@ -185,6 +185,8 @@ struct cpio {
|
||||
struct archive_string_conv *opt_sconv;
|
||||
struct archive_string_conv *sconv_default;
|
||||
int init_default_conversion;
|
||||
|
||||
int option_pwb;
|
||||
};
|
||||
|
||||
static int64_t atol16(const char *, unsigned);
|
||||
@@ -343,6 +345,10 @@ archive_read_format_cpio_options(struct archive_read *a,
|
||||
ret = ARCHIVE_FATAL;
|
||||
}
|
||||
return (ret);
|
||||
} else if (strcmp(key, "pwb") == 0) {
|
||||
if (val != NULL && val[0] != 0)
|
||||
cpio->option_pwb = 1;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/* Note: The "warn" return is just to inform the options
|
||||
@@ -891,6 +897,12 @@ header_bin_le(struct archive_read *a, struct cpio *cpio,
|
||||
archive_entry_set_dev(entry, header[bin_dev_offset] + header[bin_dev_offset + 1] * 256);
|
||||
archive_entry_set_ino(entry, header[bin_ino_offset] + header[bin_ino_offset + 1] * 256);
|
||||
archive_entry_set_mode(entry, header[bin_mode_offset] + header[bin_mode_offset + 1] * 256);
|
||||
if (cpio->option_pwb) {
|
||||
/* turn off random bits left over from V6 inode */
|
||||
archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777);
|
||||
if ((archive_entry_mode(entry) & AE_IFMT) == 0)
|
||||
archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG);
|
||||
}
|
||||
archive_entry_set_uid(entry, header[bin_uid_offset] + header[bin_uid_offset + 1] * 256);
|
||||
archive_entry_set_gid(entry, header[bin_gid_offset] + header[bin_gid_offset + 1] * 256);
|
||||
archive_entry_set_nlink(entry, header[bin_nlink_offset] + header[bin_nlink_offset + 1] * 256);
|
||||
@@ -930,6 +942,12 @@ header_bin_be(struct archive_read *a, struct cpio *cpio,
|
||||
archive_entry_set_dev(entry, header[bin_dev_offset] * 256 + header[bin_dev_offset + 1]);
|
||||
archive_entry_set_ino(entry, header[bin_ino_offset] * 256 + header[bin_ino_offset + 1]);
|
||||
archive_entry_set_mode(entry, header[bin_mode_offset] * 256 + header[bin_mode_offset + 1]);
|
||||
if (cpio->option_pwb) {
|
||||
/* turn off random bits left over from V6 inode */
|
||||
archive_entry_set_mode(entry, archive_entry_mode(entry) & 067777);
|
||||
if ((archive_entry_mode(entry) & AE_IFMT) == 0)
|
||||
archive_entry_set_mode(entry, archive_entry_mode(entry) | AE_IFREG);
|
||||
}
|
||||
archive_entry_set_uid(entry, header[bin_uid_offset] * 256 + header[bin_uid_offset + 1]);
|
||||
archive_entry_set_gid(entry, header[bin_gid_offset] * 256 + header[bin_gid_offset + 1]);
|
||||
archive_entry_set_nlink(entry, header[bin_nlink_offset] * 256 + header[bin_nlink_offset + 1]);
|
||||
|
||||
@@ -35,7 +35,10 @@
|
||||
.Nm archive_write_set_format_ar_svr4 ,
|
||||
.Nm archive_write_set_format_by_name ,
|
||||
.Nm archive_write_set_format_cpio ,
|
||||
.Nm archive_write_set_format_cpio_bin ,
|
||||
.Nm archive_write_set_format_cpio_newc ,
|
||||
.Nm archive_write_set_format_cpio_odc ,
|
||||
.Nm archive_write_set_format_cpio_pwb ,
|
||||
.Nm archive_write_set_format_filter_by_ext ,
|
||||
.Nm archive_write_set_format_filter_by_ext_def ,
|
||||
.Nm archive_write_set_format_gnutar ,
|
||||
@@ -73,8 +76,14 @@ Streaming Archive Library (libarchive, -larchive)
|
||||
.Ft int
|
||||
.Fn archive_write_set_format_cpio "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_format_cpio_bin "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_format_cpio_newc "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_format_cpio_odc "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_format_cpio_pwb "struct archive *"
|
||||
.Ft int
|
||||
.Fn archive_write_set_format_filter_by_ext "struct archive *" "const char *filename"
|
||||
.Ft int
|
||||
.Fn archive_write_set_format_filter_by_ext_def "struct archive *" "const char *filename" "const char *def_ext"
|
||||
@@ -119,17 +128,20 @@ to create a new archive with the same format as an existing archive.
|
||||
.It Fn archive_write_set_format_by_name
|
||||
Sets the corresponding format based on the common name.
|
||||
.It Xo
|
||||
.Fn archive_write_set_format_filter_by_ext ,
|
||||
.Fn archive_write_set_format_filter_by_ext
|
||||
.Fn archive_write_set_format_filter_by_ext_def
|
||||
.Xc
|
||||
Sets both filters and format based on the output filename.
|
||||
Supported extensions: .7z, .zip, .jar, .cpio, .iso, .a, .ar, .tar, .tgz, .tar.gz, .tar.bz2, .tar.xz
|
||||
.It Xo
|
||||
.Fn archive_write_set_format_7zip
|
||||
.Fn archive_write_set_format_ar_bsd ,
|
||||
.Fn archive_write_set_format_ar_svr4 ,
|
||||
.Fn archive_write_set_format_ar_bsd
|
||||
.Fn archive_write_set_format_ar_svr4
|
||||
.Fn archive_write_set_format_cpio
|
||||
.Fn archive_write_set_format_cpio_bin
|
||||
.Fn archive_write_set_format_cpio_newc
|
||||
.Fn archive_write_set_format_cpio_odc
|
||||
.Fn archive_write_set_format_cpio_pwb
|
||||
.Fn archive_write_set_format_gnutar
|
||||
.Fn archive_write_set_format_iso9660
|
||||
.Fn archive_write_set_format_mtree
|
||||
|
||||
@@ -44,7 +44,9 @@ struct { int code; int (*setter)(struct archive *); } codes[] =
|
||||
{
|
||||
{ ARCHIVE_FORMAT_7ZIP, archive_write_set_format_7zip },
|
||||
{ ARCHIVE_FORMAT_CPIO, archive_write_set_format_cpio },
|
||||
{ ARCHIVE_FORMAT_CPIO_POSIX, archive_write_set_format_cpio },
|
||||
{ ARCHIVE_FORMAT_CPIO_BIN_LE, archive_write_set_format_cpio_bin },
|
||||
{ ARCHIVE_FORMAT_CPIO_PWB, archive_write_set_format_cpio_pwb },
|
||||
{ ARCHIVE_FORMAT_CPIO_POSIX, archive_write_set_format_cpio_odc },
|
||||
{ ARCHIVE_FORMAT_CPIO_SVR4_NOCRC, archive_write_set_format_cpio_newc },
|
||||
{ ARCHIVE_FORMAT_ISO9660, archive_write_set_format_iso9660 },
|
||||
{ ARCHIVE_FORMAT_MTREE, archive_write_set_format_mtree },
|
||||
|
||||
@@ -49,6 +49,7 @@ struct { const char *name; int (*setter)(struct archive *); } names[] =
|
||||
{ "arbsd", archive_write_set_format_ar_bsd },
|
||||
{ "argnu", archive_write_set_format_ar_svr4 },
|
||||
{ "arsvr4", archive_write_set_format_ar_svr4 },
|
||||
{ "bin", archive_write_set_format_cpio_bin },
|
||||
{ "bsdtar", archive_write_set_format_pax_restricted },
|
||||
{ "cd9660", archive_write_set_format_iso9660 },
|
||||
{ "cpio", archive_write_set_format_cpio },
|
||||
@@ -58,11 +59,12 @@ struct { const char *name; int (*setter)(struct archive *); } names[] =
|
||||
{ "mtree", archive_write_set_format_mtree },
|
||||
{ "mtree-classic", archive_write_set_format_mtree_classic },
|
||||
{ "newc", archive_write_set_format_cpio_newc },
|
||||
{ "odc", archive_write_set_format_cpio },
|
||||
{ "odc", archive_write_set_format_cpio_odc },
|
||||
{ "oldtar", archive_write_set_format_v7tar },
|
||||
{ "pax", archive_write_set_format_pax },
|
||||
{ "paxr", archive_write_set_format_pax_restricted },
|
||||
{ "posix", archive_write_set_format_pax },
|
||||
{ "pwb", archive_write_set_format_cpio_pwb },
|
||||
{ "raw", archive_write_set_format_raw },
|
||||
{ "rpax", archive_write_set_format_pax_restricted },
|
||||
{ "shar", archive_write_set_format_shar },
|
||||
|
||||
@@ -1,500 +1,10 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007 Tim Kientzle
|
||||
* Copyright (c) 2011-2012 Michihiro NAKAJIMA
|
||||
* 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.
|
||||
* 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 "archive_platform.h"
|
||||
__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio.c 201170 2009-12-29 06:34:23Z kientzle $");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_entry_locale.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_write_private.h"
|
||||
#include "archive_write_set_format_private.h"
|
||||
|
||||
static ssize_t archive_write_cpio_data(struct archive_write *,
|
||||
const void *buff, size_t s);
|
||||
static int archive_write_cpio_close(struct archive_write *);
|
||||
static int archive_write_cpio_free(struct archive_write *);
|
||||
static int archive_write_cpio_finish_entry(struct archive_write *);
|
||||
static int archive_write_cpio_header(struct archive_write *,
|
||||
struct archive_entry *);
|
||||
static int archive_write_cpio_options(struct archive_write *,
|
||||
const char *, const char *);
|
||||
static int format_octal(int64_t, void *, int);
|
||||
static int64_t format_octal_recursive(int64_t, char *, int);
|
||||
static int write_header(struct archive_write *, struct archive_entry *);
|
||||
|
||||
struct cpio {
|
||||
uint64_t entry_bytes_remaining;
|
||||
|
||||
int64_t ino_next;
|
||||
|
||||
struct { int64_t old; int new;} *ino_list;
|
||||
size_t ino_list_size;
|
||||
size_t ino_list_next;
|
||||
|
||||
struct archive_string_conv *opt_sconv;
|
||||
struct archive_string_conv *sconv_default;
|
||||
int init_default_conversion;
|
||||
};
|
||||
|
||||
#define c_magic_offset 0
|
||||
#define c_magic_size 6
|
||||
#define c_dev_offset 6
|
||||
#define c_dev_size 6
|
||||
#define c_ino_offset 12
|
||||
#define c_ino_size 6
|
||||
#define c_mode_offset 18
|
||||
#define c_mode_size 6
|
||||
#define c_uid_offset 24
|
||||
#define c_uid_size 6
|
||||
#define c_gid_offset 30
|
||||
#define c_gid_size 6
|
||||
#define c_nlink_offset 36
|
||||
#define c_nlink_size 6
|
||||
#define c_rdev_offset 42
|
||||
#define c_rdev_size 6
|
||||
#define c_mtime_offset 48
|
||||
#define c_mtime_size 11
|
||||
#define c_namesize_offset 59
|
||||
#define c_namesize_size 6
|
||||
#define c_filesize_offset 65
|
||||
#define c_filesize_size 11
|
||||
|
||||
/*
|
||||
* Set output format to 'cpio' format.
|
||||
* Set output format to the default 'cpio' format.
|
||||
*/
|
||||
int
|
||||
archive_write_set_format_cpio(struct archive *_a)
|
||||
{
|
||||
struct archive_write *a = (struct archive_write *)_a;
|
||||
struct cpio *cpio;
|
||||
|
||||
archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
|
||||
ARCHIVE_STATE_NEW, "archive_write_set_format_cpio");
|
||||
|
||||
/* If someone else was already registered, unregister them. */
|
||||
if (a->format_free != NULL)
|
||||
(a->format_free)(a);
|
||||
|
||||
cpio = (struct cpio *)calloc(1, sizeof(*cpio));
|
||||
if (cpio == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
a->format_data = cpio;
|
||||
a->format_name = "cpio";
|
||||
a->format_options = archive_write_cpio_options;
|
||||
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_close = archive_write_cpio_close;
|
||||
a->format_free = archive_write_cpio_free;
|
||||
a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
|
||||
a->archive.archive_format_name = "POSIX cpio";
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_cpio_options(struct archive_write *a, const char *key,
|
||||
const char *val)
|
||||
{
|
||||
struct cpio *cpio = (struct cpio *)a->format_data;
|
||||
int ret = ARCHIVE_FAILED;
|
||||
|
||||
if (strcmp(key, "hdrcharset") == 0) {
|
||||
if (val == NULL || val[0] == 0)
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"%s: hdrcharset option needs a character-set name",
|
||||
a->format_name);
|
||||
else {
|
||||
cpio->opt_sconv = archive_string_conversion_to_charset(
|
||||
&a->archive, val, 0);
|
||||
if (cpio->opt_sconv != NULL)
|
||||
ret = ARCHIVE_OK;
|
||||
else
|
||||
ret = ARCHIVE_FATAL;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/* Note: The "warn" return is just to inform the options
|
||||
* supervisor that we didn't handle it. It will generate
|
||||
* a suitable error if no one used this option. */
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ino values are as long as 64 bits on some systems; cpio format
|
||||
* only allows 18 bits and relies on the ino values to identify hardlinked
|
||||
* files. So, we can't merely "hash" the ino numbers since collisions
|
||||
* would corrupt the archive. Instead, we generate synthetic ino values
|
||||
* to store in the archive and maintain a map of original ino values to
|
||||
* synthetic ones so we can preserve hardlink information.
|
||||
*
|
||||
* TODO: Make this more efficient. It's not as bad as it looks (most
|
||||
* files don't have any hardlinks and we don't do any work here for those),
|
||||
* but it wouldn't be hard to do better.
|
||||
*
|
||||
* TODO: Work with dev/ino pairs here instead of just ino values.
|
||||
*/
|
||||
static int
|
||||
synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
|
||||
{
|
||||
int64_t ino = archive_entry_ino64(entry);
|
||||
int ino_new;
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* If no index number was given, don't assign one. In
|
||||
* particular, this handles the end-of-archive marker
|
||||
* correctly by giving it a zero index value. (This is also
|
||||
* why we start our synthetic index numbers with one below.)
|
||||
*/
|
||||
if (ino == 0)
|
||||
return (0);
|
||||
|
||||
/* Don't store a mapping if we don't need to. */
|
||||
if (archive_entry_nlink(entry) < 2) {
|
||||
return (int)(++cpio->ino_next);
|
||||
}
|
||||
|
||||
/* Look up old ino; if we have it, this is a hardlink
|
||||
* and we reuse the same value. */
|
||||
for (i = 0; i < cpio->ino_list_next; ++i) {
|
||||
if (cpio->ino_list[i].old == ino)
|
||||
return (cpio->ino_list[i].new);
|
||||
}
|
||||
|
||||
/* Assign a new index number. */
|
||||
ino_new = (int)(++cpio->ino_next);
|
||||
|
||||
/* Ensure space for the new mapping. */
|
||||
if (cpio->ino_list_size <= cpio->ino_list_next) {
|
||||
size_t newsize = cpio->ino_list_size < 512
|
||||
? 512 : cpio->ino_list_size * 2;
|
||||
void *newlist = realloc(cpio->ino_list,
|
||||
sizeof(cpio->ino_list[0]) * newsize);
|
||||
if (newlist == NULL)
|
||||
return (-1);
|
||||
|
||||
cpio->ino_list_size = newsize;
|
||||
cpio->ino_list = newlist;
|
||||
}
|
||||
|
||||
/* Record and return the new value. */
|
||||
cpio->ino_list[cpio->ino_list_next].old = ino;
|
||||
cpio->ino_list[cpio->ino_list_next].new = ino_new;
|
||||
++cpio->ino_list_next;
|
||||
return (ino_new);
|
||||
}
|
||||
|
||||
|
||||
static struct archive_string_conv *
|
||||
get_sconv(struct archive_write *a)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
struct archive_string_conv *sconv;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
sconv = cpio->opt_sconv;
|
||||
if (sconv == NULL) {
|
||||
if (!cpio->init_default_conversion) {
|
||||
cpio->sconv_default =
|
||||
archive_string_default_conversion_for_write(
|
||||
&(a->archive));
|
||||
cpio->init_default_conversion = 1;
|
||||
}
|
||||
sconv = cpio->sconv_default;
|
||||
}
|
||||
return (sconv);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry)
|
||||
{
|
||||
const char *path;
|
||||
size_t len;
|
||||
|
||||
if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
|
||||
archive_set_error(&a->archive, -1, "Filetype required");
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
|
||||
if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
|
||||
&& errno == ENOMEM) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate memory for Pathname");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
if (len == 0 || path == NULL || path[0] == '\0') {
|
||||
archive_set_error(&a->archive, -1, "Pathname required");
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
|
||||
if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0) {
|
||||
archive_set_error(&a->archive, -1, "Size required");
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
return write_header(a, entry);
|
||||
}
|
||||
|
||||
static int
|
||||
write_header(struct archive_write *a, struct archive_entry *entry)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
const char *p, *path;
|
||||
int pathlength, ret, ret_final;
|
||||
int64_t ino;
|
||||
char h[76];
|
||||
struct archive_string_conv *sconv;
|
||||
struct archive_entry *entry_main;
|
||||
size_t len;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
ret_final = ARCHIVE_OK;
|
||||
sconv = get_sconv(a);
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
/* Make sure the path separators in pathname, hardlink and symlink
|
||||
* are all slash '/', not the Windows path separator '\'. */
|
||||
entry_main = __la_win_entry_in_posix_pathseparator(entry);
|
||||
if (entry_main == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate ustar data");
|
||||
return(ARCHIVE_FATAL);
|
||||
}
|
||||
if (entry != entry_main)
|
||||
entry = entry_main;
|
||||
else
|
||||
entry_main = NULL;
|
||||
#else
|
||||
entry_main = NULL;
|
||||
#endif
|
||||
|
||||
ret = archive_entry_pathname_l(entry, &path, &len, sconv);
|
||||
if (ret != 0) {
|
||||
if (errno == ENOMEM) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate memory for Pathname");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Can't translate pathname '%s' to %s",
|
||||
archive_entry_pathname(entry),
|
||||
archive_string_conversion_charset_name(sconv));
|
||||
ret_final = ARCHIVE_WARN;
|
||||
}
|
||||
/* Include trailing null. */
|
||||
pathlength = (int)len + 1;
|
||||
|
||||
memset(h, 0, sizeof(h));
|
||||
format_octal(070707, h + c_magic_offset, c_magic_size);
|
||||
format_octal(archive_entry_dev(entry), h + c_dev_offset, c_dev_size);
|
||||
|
||||
ino = synthesize_ino_value(cpio, entry);
|
||||
if (ino < 0) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"No memory for ino translation table");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
} else if (ino > 0777777) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"Too many files for this cpio format");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
format_octal(ino & 0777777, h + c_ino_offset, c_ino_size);
|
||||
|
||||
/* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */
|
||||
format_octal(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);
|
||||
format_octal(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);
|
||||
format_octal(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);
|
||||
format_octal(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);
|
||||
if (archive_entry_filetype(entry) == AE_IFBLK
|
||||
|| archive_entry_filetype(entry) == AE_IFCHR)
|
||||
format_octal(archive_entry_rdev(entry), h + c_rdev_offset, c_rdev_size);
|
||||
else
|
||||
format_octal(0, h + c_rdev_offset, c_rdev_size);
|
||||
format_octal(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);
|
||||
format_octal(pathlength, h + c_namesize_offset, c_namesize_size);
|
||||
|
||||
/* Non-regular files don't store bodies. */
|
||||
if (archive_entry_filetype(entry) != AE_IFREG)
|
||||
archive_entry_set_size(entry, 0);
|
||||
|
||||
/* Symlinks get the link written as the body of the entry. */
|
||||
ret = archive_entry_symlink_l(entry, &p, &len, sconv);
|
||||
if (ret != 0) {
|
||||
if (errno == ENOMEM) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate memory for Linkname");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Can't translate linkname '%s' to %s",
|
||||
archive_entry_symlink(entry),
|
||||
archive_string_conversion_charset_name(sconv));
|
||||
ret_final = ARCHIVE_WARN;
|
||||
}
|
||||
if (len > 0 && p != NULL && *p != '\0')
|
||||
ret = format_octal(strlen(p), h + c_filesize_offset,
|
||||
c_filesize_size);
|
||||
else
|
||||
ret = format_octal(archive_entry_size(entry),
|
||||
h + c_filesize_offset, c_filesize_size);
|
||||
if (ret) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"File is too large for cpio format.");
|
||||
ret_final = ARCHIVE_FAILED;
|
||||
goto exit_write_header;
|
||||
}
|
||||
|
||||
ret = __archive_write_output(a, h, sizeof(h));
|
||||
if (ret != ARCHIVE_OK) {
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
|
||||
ret = __archive_write_output(a, path, pathlength);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
|
||||
cpio->entry_bytes_remaining = archive_entry_size(entry);
|
||||
|
||||
/* Write the symlink now. */
|
||||
if (p != NULL && *p != '\0') {
|
||||
ret = __archive_write_output(a, p, strlen(p));
|
||||
if (ret != ARCHIVE_OK) {
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
}
|
||||
exit_write_header:
|
||||
archive_entry_free(entry_main);
|
||||
return (ret_final);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
archive_write_cpio_data(struct archive_write *a, const void *buff, size_t s)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
int ret;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
if (s > cpio->entry_bytes_remaining)
|
||||
s = (size_t)cpio->entry_bytes_remaining;
|
||||
|
||||
ret = __archive_write_output(a, buff, s);
|
||||
cpio->entry_bytes_remaining -= s;
|
||||
if (ret >= 0)
|
||||
return (s);
|
||||
else
|
||||
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, (char *)p, digits);
|
||||
ret = 0;
|
||||
} else {
|
||||
format_octal_recursive(max, (char *)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' + ((char)v & 7);
|
||||
return (v >> 3);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_cpio_close(struct archive_write *a)
|
||||
{
|
||||
int er;
|
||||
struct archive_entry *trailer;
|
||||
|
||||
trailer = archive_entry_new2(NULL);
|
||||
/* nlink = 1 here for GNU cpio compat. */
|
||||
archive_entry_set_nlink(trailer, 1);
|
||||
archive_entry_set_size(trailer, 0);
|
||||
archive_entry_set_pathname(trailer, "TRAILER!!!");
|
||||
er = write_header(a, trailer);
|
||||
archive_entry_free(trailer);
|
||||
return (er);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_cpio_free(struct archive_write *a)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
free(cpio->ino_list);
|
||||
free(cpio);
|
||||
a->format_data = NULL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_cpio_finish_entry(struct archive_write *a)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
return (__archive_write_nulls(a,
|
||||
(size_t)cpio->entry_bytes_remaining));
|
||||
return archive_write_set_format_cpio_odc(_a);
|
||||
}
|
||||
@@ -0,0 +1,610 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007 Tim Kientzle
|
||||
* Copyright (c) 2011-2012 Michihiro NAKAJIMA
|
||||
* 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.
|
||||
* 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 "archive_platform.h"
|
||||
__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio.c 201170 2009-12-29 06:34:23Z kientzle $");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_entry_locale.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_write_private.h"
|
||||
#include "archive_write_set_format_private.h"
|
||||
|
||||
static ssize_t archive_write_binary_data(struct archive_write *,
|
||||
const void *buff, size_t s);
|
||||
static int archive_write_binary_close(struct archive_write *);
|
||||
static int archive_write_binary_free(struct archive_write *);
|
||||
static int archive_write_binary_finish_entry(struct archive_write *);
|
||||
static int archive_write_binary_header(struct archive_write *,
|
||||
struct archive_entry *);
|
||||
static int archive_write_binary_options(struct archive_write *,
|
||||
const char *, const char *);
|
||||
static int write_header(struct archive_write *, struct archive_entry *);
|
||||
|
||||
struct cpio {
|
||||
uint64_t entry_bytes_remaining;
|
||||
|
||||
int64_t ino_next;
|
||||
|
||||
struct { int64_t old; int new;} *ino_list;
|
||||
size_t ino_list_size;
|
||||
size_t ino_list_next;
|
||||
|
||||
struct archive_string_conv *opt_sconv;
|
||||
struct archive_string_conv *sconv_default;
|
||||
int init_default_conversion;
|
||||
};
|
||||
|
||||
/* This struct needs to be packed to get the header right */
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define PACKED(x) x __attribute__((packed))
|
||||
#elif defined(_MSC_VER)
|
||||
#define PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))
|
||||
#else
|
||||
#define PACKED(x) x
|
||||
#endif
|
||||
|
||||
#define HSIZE 26
|
||||
|
||||
PACKED(struct cpio_binary_header {
|
||||
uint16_t h_magic;
|
||||
uint16_t h_dev;
|
||||
uint16_t h_ino;
|
||||
uint16_t h_mode;
|
||||
uint16_t h_uid;
|
||||
uint16_t h_gid;
|
||||
uint16_t h_nlink;
|
||||
uint16_t h_majmin;
|
||||
uint32_t h_mtime;
|
||||
uint16_t h_namesize;
|
||||
uint32_t h_filesize;
|
||||
});
|
||||
|
||||
/* Back in the day, the 7th Edition cpio.c had this, to
|
||||
* adapt to, as the comment said, "VAX, Interdata, ...":
|
||||
*
|
||||
* union { long l; short s[2]; char c[4]; } U;
|
||||
* #define MKSHORT(v,lv) {U.l=1L;if(U.c[0]) U.l=lv,v[0]=U.s[1],v[1]=U.s[0]; else U.l=lv,v[0]=U.s[0],v[1]=U.s[1];}
|
||||
* long mklong(v)
|
||||
* short v[];
|
||||
* {
|
||||
* U.l = 1;
|
||||
* if(U.c[0])
|
||||
* U.s[0] = v[1], U.s[1] = v[0];
|
||||
* else
|
||||
* U.s[0] = v[0], U.s[1] = v[1];
|
||||
* return U.l;
|
||||
* }
|
||||
*
|
||||
* Of course, that assumes that all machines have little-endian shorts,
|
||||
* and just adapts the others to the special endianness of the PDP-11.
|
||||
*
|
||||
* Now, we could do this:
|
||||
*
|
||||
* union { uint32_t l; uint16_t s[2]; uint8_t c[4]; } U;
|
||||
* #define PUTI16(v,sv) {U.s[0]=1;if(U.c[0]) v=sv; else U.s[0]=sv,U.c[2]=U.c[1],U.c[3]=U.c[0],v=U.s[1];}
|
||||
* #define PUTI32(v,lv) {char_t Ut;U.l=1;if(U.c[0]) U.l=lv,v[0]=U.s[1],v[1]=U.s[0]; else U.l=lv,Ut=U.c[0],U.c[0]=U.c[1],U.c[1]=Ut,Ut=U.c[2],U.c[2]=U.c[3],U.c[3]=Ut,v[0]=U.s[0],v[1]=U.s[1];}
|
||||
*
|
||||
* ...but it feels a little better to do it like this:
|
||||
*/
|
||||
|
||||
static uint16_t swap16(uint16_t in) {
|
||||
union {
|
||||
uint16_t s[2];
|
||||
uint8_t c[4];
|
||||
} U;
|
||||
U.s[0] = 1;
|
||||
if (U.c[0])
|
||||
return in;
|
||||
else {
|
||||
U.s[0] = in;
|
||||
U.c[2] = U.c[1];
|
||||
U.c[3] = U.c[0];
|
||||
return U.s[1];
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
static uint32_t swap32(uint32_t in) {
|
||||
union {
|
||||
uint32_t l;
|
||||
uint16_t s[2];
|
||||
uint8_t c[4];
|
||||
} U;
|
||||
U.l = 1;
|
||||
if (U.c[0]) { /* Little-endian */
|
||||
uint16_t t;
|
||||
U.l = in;
|
||||
t = U.s[0];
|
||||
U.s[0] = U.s[1];
|
||||
U.s[1] = t;
|
||||
} else if (U.c[3]) { /* Big-endian */
|
||||
U.l = in;
|
||||
U.s[0] = swap16(U.s[0]);
|
||||
U.s[1] = swap16(U.s[1]);
|
||||
} else { /* PDP-endian */
|
||||
U.l = in;
|
||||
}
|
||||
return U.l;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set output format to the selected binary variant
|
||||
*/
|
||||
static int
|
||||
archive_write_set_format_cpio_binary(struct archive *_a, int format)
|
||||
{
|
||||
struct archive_write *a = (struct archive_write *)_a;
|
||||
struct cpio *cpio;
|
||||
|
||||
if (sizeof(struct cpio_binary_header) != HSIZE) {
|
||||
archive_set_error(&a->archive, EINVAL,
|
||||
"Binary cpio format not supported on this platform");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
|
||||
ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_binary");
|
||||
|
||||
/* If someone else was already registered, unregister them. */
|
||||
if (a->format_free != NULL)
|
||||
(a->format_free)(a);
|
||||
|
||||
cpio = (struct cpio *)calloc(1, sizeof(*cpio));
|
||||
if (cpio == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
a->format_data = cpio;
|
||||
a->format_name = "cpio";
|
||||
a->format_options = archive_write_binary_options;
|
||||
a->format_write_header = archive_write_binary_header;
|
||||
a->format_write_data = archive_write_binary_data;
|
||||
a->format_finish_entry = archive_write_binary_finish_entry;
|
||||
a->format_close = archive_write_binary_close;
|
||||
a->format_free = archive_write_binary_free;
|
||||
a->archive.archive_format = format;
|
||||
switch (format) {
|
||||
case ARCHIVE_FORMAT_CPIO_PWB:
|
||||
a->archive.archive_format_name = "PWB cpio";
|
||||
break;
|
||||
case ARCHIVE_FORMAT_CPIO_BIN_LE:
|
||||
a->archive.archive_format_name = "7th Edition cpio";
|
||||
break;
|
||||
default:
|
||||
archive_set_error(&a->archive, EINVAL, "binary format must be 'pwb' or 'bin'");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set output format to PWB (6th Edition) binary format
|
||||
*/
|
||||
int
|
||||
archive_write_set_format_cpio_pwb(struct archive *_a)
|
||||
{
|
||||
return archive_write_set_format_cpio_binary(_a, ARCHIVE_FORMAT_CPIO_PWB);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set output format to 7th Edition binary format
|
||||
*/
|
||||
int
|
||||
archive_write_set_format_cpio_bin(struct archive *_a)
|
||||
{
|
||||
return archive_write_set_format_cpio_binary(_a, ARCHIVE_FORMAT_CPIO_BIN_LE);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_binary_options(struct archive_write *a, const char *key,
|
||||
const char *val)
|
||||
{
|
||||
struct cpio *cpio = (struct cpio *)a->format_data;
|
||||
int ret = ARCHIVE_FAILED;
|
||||
|
||||
if (strcmp(key, "hdrcharset") == 0) {
|
||||
if (val == NULL || val[0] == 0)
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"%s: hdrcharset option needs a character-set name",
|
||||
a->format_name);
|
||||
else {
|
||||
cpio->opt_sconv = archive_string_conversion_to_charset(
|
||||
&a->archive, val, 0);
|
||||
if (cpio->opt_sconv != NULL)
|
||||
ret = ARCHIVE_OK;
|
||||
else
|
||||
ret = ARCHIVE_FATAL;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/* Note: The "warn" return is just to inform the options
|
||||
* supervisor that we didn't handle it. It will generate
|
||||
* a suitable error if no one used this option. */
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ino values are as long as 64 bits on some systems; cpio format
|
||||
* only allows 16 bits and relies on the ino values to identify hardlinked
|
||||
* files. So, we can't merely "hash" the ino numbers since collisions
|
||||
* would corrupt the archive. Instead, we generate synthetic ino values
|
||||
* to store in the archive and maintain a map of original ino values to
|
||||
* synthetic ones so we can preserve hardlink information.
|
||||
*
|
||||
* TODO: Make this more efficient. It's not as bad as it looks (most
|
||||
* files don't have any hardlinks and we don't do any work here for those),
|
||||
* but it wouldn't be hard to do better.
|
||||
*
|
||||
* TODO: Work with dev/ino pairs here instead of just ino values.
|
||||
*/
|
||||
static int
|
||||
synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
|
||||
{
|
||||
int64_t ino = archive_entry_ino64(entry);
|
||||
int ino_new;
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* If no index number was given, don't assign one. In
|
||||
* particular, this handles the end-of-archive marker
|
||||
* correctly by giving it a zero index value. (This is also
|
||||
* why we start our synthetic index numbers with one below.)
|
||||
*/
|
||||
if (ino == 0)
|
||||
return (0);
|
||||
|
||||
/* Don't store a mapping if we don't need to. */
|
||||
if (archive_entry_nlink(entry) < 2) {
|
||||
return (int)(++cpio->ino_next);
|
||||
}
|
||||
|
||||
/* Look up old ino; if we have it, this is a hardlink
|
||||
* and we reuse the same value. */
|
||||
for (i = 0; i < cpio->ino_list_next; ++i) {
|
||||
if (cpio->ino_list[i].old == ino)
|
||||
return (cpio->ino_list[i].new);
|
||||
}
|
||||
|
||||
/* Assign a new index number. */
|
||||
ino_new = (int)(++cpio->ino_next);
|
||||
|
||||
/* Ensure space for the new mapping. */
|
||||
if (cpio->ino_list_size <= cpio->ino_list_next) {
|
||||
size_t newsize = cpio->ino_list_size < 512
|
||||
? 512 : cpio->ino_list_size * 2;
|
||||
void *newlist = realloc(cpio->ino_list,
|
||||
sizeof(cpio->ino_list[0]) * newsize);
|
||||
if (newlist == NULL)
|
||||
return (-1);
|
||||
|
||||
cpio->ino_list_size = newsize;
|
||||
cpio->ino_list = newlist;
|
||||
}
|
||||
|
||||
/* Record and return the new value. */
|
||||
cpio->ino_list[cpio->ino_list_next].old = ino;
|
||||
cpio->ino_list[cpio->ino_list_next].new = ino_new;
|
||||
++cpio->ino_list_next;
|
||||
return (ino_new);
|
||||
}
|
||||
|
||||
|
||||
static struct archive_string_conv *
|
||||
get_sconv(struct archive_write *a)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
struct archive_string_conv *sconv;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
sconv = cpio->opt_sconv;
|
||||
if (sconv == NULL) {
|
||||
if (!cpio->init_default_conversion) {
|
||||
cpio->sconv_default =
|
||||
archive_string_default_conversion_for_write(
|
||||
&(a->archive));
|
||||
cpio->init_default_conversion = 1;
|
||||
}
|
||||
sconv = cpio->sconv_default;
|
||||
}
|
||||
return (sconv);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_binary_header(struct archive_write *a, struct archive_entry *entry)
|
||||
{
|
||||
const char *path;
|
||||
size_t len;
|
||||
|
||||
if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
|
||||
archive_set_error(&a->archive, -1, "Filetype required");
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
|
||||
if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
|
||||
&& errno == ENOMEM) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate memory for Pathname");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
if (len == 0 || path == NULL || path[0] == '\0') {
|
||||
archive_set_error(&a->archive, -1, "Pathname required");
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
|
||||
if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0) {
|
||||
archive_set_error(&a->archive, -1, "Size required");
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
return write_header(a, entry);
|
||||
}
|
||||
|
||||
static int
|
||||
write_header(struct archive_write *a, struct archive_entry *entry)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
const char *p, *path;
|
||||
int pathlength, ret, ret_final;
|
||||
int64_t ino;
|
||||
struct cpio_binary_header h;
|
||||
struct archive_string_conv *sconv;
|
||||
struct archive_entry *entry_main;
|
||||
size_t len;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
ret_final = ARCHIVE_OK;
|
||||
sconv = get_sconv(a);
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
/* Make sure the path separators in pathname, hardlink and symlink
|
||||
* are all slash '/', not the Windows path separator '\'. */
|
||||
entry_main = __la_win_entry_in_posix_pathseparator(entry);
|
||||
if (entry_main == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate ustar data");
|
||||
return(ARCHIVE_FATAL);
|
||||
}
|
||||
if (entry != entry_main)
|
||||
entry = entry_main;
|
||||
else
|
||||
entry_main = NULL;
|
||||
#else
|
||||
entry_main = NULL;
|
||||
#endif
|
||||
|
||||
ret = archive_entry_pathname_l(entry, &path, &len, sconv);
|
||||
if (ret != 0) {
|
||||
if (errno == ENOMEM) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate memory for Pathname");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Can't translate pathname '%s' to %s",
|
||||
archive_entry_pathname(entry),
|
||||
archive_string_conversion_charset_name(sconv));
|
||||
ret_final = ARCHIVE_WARN;
|
||||
}
|
||||
/* Include trailing null */
|
||||
pathlength = (int)len + 1;
|
||||
|
||||
h.h_magic = swap16(070707);
|
||||
h.h_dev = swap16(archive_entry_dev(entry));
|
||||
|
||||
ino = synthesize_ino_value(cpio, entry);
|
||||
if (ino < 0) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"No memory for ino translation table");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
} else if (ino > 077777) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"Too many files for this cpio format");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
h.h_ino = swap16(ino);
|
||||
|
||||
h.h_mode = archive_entry_mode(entry);
|
||||
if (((h.h_mode & AE_IFMT) == AE_IFSOCK) || ((h.h_mode & AE_IFMT) == AE_IFIFO)) {
|
||||
archive_set_error(&a->archive, EINVAL,
|
||||
"sockets and fifos cannot be represented in the binary cpio formats");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) {
|
||||
if ((h.h_mode & AE_IFMT) == AE_IFLNK) {
|
||||
archive_set_error(&a->archive, EINVAL,
|
||||
"symbolic links cannot be represented in the PWB cpio format");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
/* we could turn off AE_IFREG here, but it does no harm, */
|
||||
/* and allows v7 cpio to read the entry without confusion */
|
||||
}
|
||||
h.h_mode = swap16(h.h_mode);
|
||||
|
||||
h.h_uid = swap16(archive_entry_uid(entry));
|
||||
h.h_gid = swap16(archive_entry_gid(entry));
|
||||
h.h_nlink = swap16(archive_entry_nlink(entry));
|
||||
|
||||
if (archive_entry_filetype(entry) == AE_IFBLK
|
||||
|| archive_entry_filetype(entry) == AE_IFCHR)
|
||||
h.h_majmin = swap16(archive_entry_rdev(entry));
|
||||
else
|
||||
h.h_majmin = 0;
|
||||
|
||||
h.h_mtime = swap32(archive_entry_mtime(entry));
|
||||
h.h_namesize = swap16(pathlength);
|
||||
|
||||
/* Non-regular files don't store bodies. */
|
||||
if (archive_entry_filetype(entry) != AE_IFREG)
|
||||
archive_entry_set_size(entry, 0);
|
||||
|
||||
/* Symlinks get the link written as the body of the entry. */
|
||||
ret = archive_entry_symlink_l(entry, &p, &len, sconv);
|
||||
if (ret != 0) {
|
||||
if (errno == ENOMEM) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate memory for Linkname");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Can't translate linkname '%s' to %s",
|
||||
archive_entry_symlink(entry),
|
||||
archive_string_conversion_charset_name(sconv));
|
||||
ret_final = ARCHIVE_WARN;
|
||||
}
|
||||
|
||||
if (len > 0 && p != NULL && *p != '\0') {
|
||||
if (a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) {
|
||||
archive_set_error(&a->archive, EINVAL,
|
||||
"symlinks are not supported by UNIX V6 or by PWB cpio");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
h.h_filesize = swap32(strlen(p)); /* symlink */
|
||||
} else {
|
||||
if ((a->archive.archive_format == ARCHIVE_FORMAT_CPIO_PWB) &&
|
||||
(archive_entry_size(entry) > 256*256*256-1)) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"File is too large for PWB binary cpio format.");
|
||||
ret_final = ARCHIVE_FAILED;
|
||||
goto exit_write_header;
|
||||
} else if (archive_entry_size(entry) > INT32_MAX) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"File is too large for binary cpio format.");
|
||||
ret_final = ARCHIVE_FAILED;
|
||||
goto exit_write_header;
|
||||
}
|
||||
h.h_filesize = swap32(archive_entry_size(entry)); /* file */
|
||||
}
|
||||
|
||||
ret = __archive_write_output(a, &h, HSIZE);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
|
||||
ret = __archive_write_output(a, path, pathlength);
|
||||
if ((ret == ARCHIVE_OK) && ((pathlength % 2) != 0))
|
||||
ret = __archive_write_nulls(a, 1);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
|
||||
cpio->entry_bytes_remaining = archive_entry_size(entry);
|
||||
if ((cpio->entry_bytes_remaining % 2) != 0)
|
||||
cpio->entry_bytes_remaining++;
|
||||
|
||||
/* Write the symlink now. */
|
||||
if (p != NULL && *p != '\0') {
|
||||
ret = __archive_write_output(a, p, strlen(p));
|
||||
if ((ret == ARCHIVE_OK) && ((strlen(p) % 2) != 0))
|
||||
ret = __archive_write_nulls(a, 1);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
}
|
||||
|
||||
exit_write_header:
|
||||
archive_entry_free(entry_main);
|
||||
return (ret_final);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
archive_write_binary_data(struct archive_write *a, const void *buff, size_t s)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
int ret;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
if (s > cpio->entry_bytes_remaining)
|
||||
s = (size_t)cpio->entry_bytes_remaining;
|
||||
|
||||
ret = __archive_write_output(a, buff, s);
|
||||
cpio->entry_bytes_remaining -= s;
|
||||
if (ret >= 0)
|
||||
return (s);
|
||||
else
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_binary_close(struct archive_write *a)
|
||||
{
|
||||
int er;
|
||||
struct archive_entry *trailer;
|
||||
|
||||
trailer = archive_entry_new2(NULL);
|
||||
/* nlink = 1 here for GNU cpio compat. */
|
||||
archive_entry_set_nlink(trailer, 1);
|
||||
archive_entry_set_size(trailer, 0);
|
||||
archive_entry_set_pathname(trailer, "TRAILER!!!");
|
||||
er = write_header(a, trailer);
|
||||
archive_entry_free(trailer);
|
||||
return (er);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_binary_free(struct archive_write *a)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
free(cpio->ino_list);
|
||||
free(cpio);
|
||||
a->format_data = NULL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_binary_finish_entry(struct archive_write *a)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
return (__archive_write_nulls(a,
|
||||
(size_t)cpio->entry_bytes_remaining));
|
||||
}
|
||||
@@ -0,0 +1,500 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007 Tim Kientzle
|
||||
* Copyright (c) 2011-2012 Michihiro NAKAJIMA
|
||||
* 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.
|
||||
* 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 "archive_platform.h"
|
||||
__FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_cpio.c 201170 2009-12-29 06:34:23Z kientzle $");
|
||||
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#ifdef HAVE_STRING_H
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_entry_locale.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_write_private.h"
|
||||
#include "archive_write_set_format_private.h"
|
||||
|
||||
static ssize_t archive_write_odc_data(struct archive_write *,
|
||||
const void *buff, size_t s);
|
||||
static int archive_write_odc_close(struct archive_write *);
|
||||
static int archive_write_odc_free(struct archive_write *);
|
||||
static int archive_write_odc_finish_entry(struct archive_write *);
|
||||
static int archive_write_odc_header(struct archive_write *,
|
||||
struct archive_entry *);
|
||||
static int archive_write_odc_options(struct archive_write *,
|
||||
const char *, const char *);
|
||||
static int format_octal(int64_t, void *, int);
|
||||
static int64_t format_octal_recursive(int64_t, char *, int);
|
||||
static int write_header(struct archive_write *, struct archive_entry *);
|
||||
|
||||
struct cpio {
|
||||
uint64_t entry_bytes_remaining;
|
||||
|
||||
int64_t ino_next;
|
||||
|
||||
struct { int64_t old; int new;} *ino_list;
|
||||
size_t ino_list_size;
|
||||
size_t ino_list_next;
|
||||
|
||||
struct archive_string_conv *opt_sconv;
|
||||
struct archive_string_conv *sconv_default;
|
||||
int init_default_conversion;
|
||||
};
|
||||
|
||||
#define c_magic_offset 0
|
||||
#define c_magic_size 6
|
||||
#define c_dev_offset 6
|
||||
#define c_dev_size 6
|
||||
#define c_ino_offset 12
|
||||
#define c_ino_size 6
|
||||
#define c_mode_offset 18
|
||||
#define c_mode_size 6
|
||||
#define c_uid_offset 24
|
||||
#define c_uid_size 6
|
||||
#define c_gid_offset 30
|
||||
#define c_gid_size 6
|
||||
#define c_nlink_offset 36
|
||||
#define c_nlink_size 6
|
||||
#define c_rdev_offset 42
|
||||
#define c_rdev_size 6
|
||||
#define c_mtime_offset 48
|
||||
#define c_mtime_size 11
|
||||
#define c_namesize_offset 59
|
||||
#define c_namesize_size 6
|
||||
#define c_filesize_offset 65
|
||||
#define c_filesize_size 11
|
||||
|
||||
/*
|
||||
* Set output format to 'cpio' format.
|
||||
*/
|
||||
int
|
||||
archive_write_set_format_cpio_odc(struct archive *_a)
|
||||
{
|
||||
struct archive_write *a = (struct archive_write *)_a;
|
||||
struct cpio *cpio;
|
||||
|
||||
archive_check_magic(_a, ARCHIVE_WRITE_MAGIC,
|
||||
ARCHIVE_STATE_NEW, "archive_write_set_format_cpio_odc");
|
||||
|
||||
/* If someone else was already registered, unregister them. */
|
||||
if (a->format_free != NULL)
|
||||
(a->format_free)(a);
|
||||
|
||||
cpio = (struct cpio *)calloc(1, sizeof(*cpio));
|
||||
if (cpio == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM, "Can't allocate cpio data");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
a->format_data = cpio;
|
||||
a->format_name = "cpio";
|
||||
a->format_options = archive_write_odc_options;
|
||||
a->format_write_header = archive_write_odc_header;
|
||||
a->format_write_data = archive_write_odc_data;
|
||||
a->format_finish_entry = archive_write_odc_finish_entry;
|
||||
a->format_close = archive_write_odc_close;
|
||||
a->format_free = archive_write_odc_free;
|
||||
a->archive.archive_format = ARCHIVE_FORMAT_CPIO_POSIX;
|
||||
a->archive.archive_format_name = "POSIX cpio";
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_odc_options(struct archive_write *a, const char *key,
|
||||
const char *val)
|
||||
{
|
||||
struct cpio *cpio = (struct cpio *)a->format_data;
|
||||
int ret = ARCHIVE_FAILED;
|
||||
|
||||
if (strcmp(key, "hdrcharset") == 0) {
|
||||
if (val == NULL || val[0] == 0)
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"%s: hdrcharset option needs a character-set name",
|
||||
a->format_name);
|
||||
else {
|
||||
cpio->opt_sconv = archive_string_conversion_to_charset(
|
||||
&a->archive, val, 0);
|
||||
if (cpio->opt_sconv != NULL)
|
||||
ret = ARCHIVE_OK;
|
||||
else
|
||||
ret = ARCHIVE_FATAL;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/* Note: The "warn" return is just to inform the options
|
||||
* supervisor that we didn't handle it. It will generate
|
||||
* a suitable error if no one used this option. */
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ino values are as long as 64 bits on some systems; cpio format
|
||||
* only allows 18 bits and relies on the ino values to identify hardlinked
|
||||
* files. So, we can't merely "hash" the ino numbers since collisions
|
||||
* would corrupt the archive. Instead, we generate synthetic ino values
|
||||
* to store in the archive and maintain a map of original ino values to
|
||||
* synthetic ones so we can preserve hardlink information.
|
||||
*
|
||||
* TODO: Make this more efficient. It's not as bad as it looks (most
|
||||
* files don't have any hardlinks and we don't do any work here for those),
|
||||
* but it wouldn't be hard to do better.
|
||||
*
|
||||
* TODO: Work with dev/ino pairs here instead of just ino values.
|
||||
*/
|
||||
static int
|
||||
synthesize_ino_value(struct cpio *cpio, struct archive_entry *entry)
|
||||
{
|
||||
int64_t ino = archive_entry_ino64(entry);
|
||||
int ino_new;
|
||||
size_t i;
|
||||
|
||||
/*
|
||||
* If no index number was given, don't assign one. In
|
||||
* particular, this handles the end-of-archive marker
|
||||
* correctly by giving it a zero index value. (This is also
|
||||
* why we start our synthetic index numbers with one below.)
|
||||
*/
|
||||
if (ino == 0)
|
||||
return (0);
|
||||
|
||||
/* Don't store a mapping if we don't need to. */
|
||||
if (archive_entry_nlink(entry) < 2) {
|
||||
return (int)(++cpio->ino_next);
|
||||
}
|
||||
|
||||
/* Look up old ino; if we have it, this is a hardlink
|
||||
* and we reuse the same value. */
|
||||
for (i = 0; i < cpio->ino_list_next; ++i) {
|
||||
if (cpio->ino_list[i].old == ino)
|
||||
return (cpio->ino_list[i].new);
|
||||
}
|
||||
|
||||
/* Assign a new index number. */
|
||||
ino_new = (int)(++cpio->ino_next);
|
||||
|
||||
/* Ensure space for the new mapping. */
|
||||
if (cpio->ino_list_size <= cpio->ino_list_next) {
|
||||
size_t newsize = cpio->ino_list_size < 512
|
||||
? 512 : cpio->ino_list_size * 2;
|
||||
void *newlist = realloc(cpio->ino_list,
|
||||
sizeof(cpio->ino_list[0]) * newsize);
|
||||
if (newlist == NULL)
|
||||
return (-1);
|
||||
|
||||
cpio->ino_list_size = newsize;
|
||||
cpio->ino_list = newlist;
|
||||
}
|
||||
|
||||
/* Record and return the new value. */
|
||||
cpio->ino_list[cpio->ino_list_next].old = ino;
|
||||
cpio->ino_list[cpio->ino_list_next].new = ino_new;
|
||||
++cpio->ino_list_next;
|
||||
return (ino_new);
|
||||
}
|
||||
|
||||
|
||||
static struct archive_string_conv *
|
||||
get_sconv(struct archive_write *a)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
struct archive_string_conv *sconv;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
sconv = cpio->opt_sconv;
|
||||
if (sconv == NULL) {
|
||||
if (!cpio->init_default_conversion) {
|
||||
cpio->sconv_default =
|
||||
archive_string_default_conversion_for_write(
|
||||
&(a->archive));
|
||||
cpio->init_default_conversion = 1;
|
||||
}
|
||||
sconv = cpio->sconv_default;
|
||||
}
|
||||
return (sconv);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_odc_header(struct archive_write *a, struct archive_entry *entry)
|
||||
{
|
||||
const char *path;
|
||||
size_t len;
|
||||
|
||||
if (archive_entry_filetype(entry) == 0 && archive_entry_hardlink(entry) == NULL) {
|
||||
archive_set_error(&a->archive, -1, "Filetype required");
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
|
||||
if (archive_entry_pathname_l(entry, &path, &len, get_sconv(a)) != 0
|
||||
&& errno == ENOMEM) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate memory for Pathname");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
if (len == 0 || path == NULL || path[0] == '\0') {
|
||||
archive_set_error(&a->archive, -1, "Pathname required");
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
|
||||
if (!archive_entry_size_is_set(entry) || archive_entry_size(entry) < 0) {
|
||||
archive_set_error(&a->archive, -1, "Size required");
|
||||
return (ARCHIVE_FAILED);
|
||||
}
|
||||
return write_header(a, entry);
|
||||
}
|
||||
|
||||
static int
|
||||
write_header(struct archive_write *a, struct archive_entry *entry)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
const char *p, *path;
|
||||
int pathlength, ret, ret_final;
|
||||
int64_t ino;
|
||||
char h[76];
|
||||
struct archive_string_conv *sconv;
|
||||
struct archive_entry *entry_main;
|
||||
size_t len;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
ret_final = ARCHIVE_OK;
|
||||
sconv = get_sconv(a);
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
/* Make sure the path separators in pathname, hardlink and symlink
|
||||
* are all slash '/', not the Windows path separator '\'. */
|
||||
entry_main = __la_win_entry_in_posix_pathseparator(entry);
|
||||
if (entry_main == NULL) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate ustar data");
|
||||
return(ARCHIVE_FATAL);
|
||||
}
|
||||
if (entry != entry_main)
|
||||
entry = entry_main;
|
||||
else
|
||||
entry_main = NULL;
|
||||
#else
|
||||
entry_main = NULL;
|
||||
#endif
|
||||
|
||||
ret = archive_entry_pathname_l(entry, &path, &len, sconv);
|
||||
if (ret != 0) {
|
||||
if (errno == ENOMEM) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate memory for Pathname");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Can't translate pathname '%s' to %s",
|
||||
archive_entry_pathname(entry),
|
||||
archive_string_conversion_charset_name(sconv));
|
||||
ret_final = ARCHIVE_WARN;
|
||||
}
|
||||
/* Include trailing null. */
|
||||
pathlength = (int)len + 1;
|
||||
|
||||
memset(h, 0, sizeof(h));
|
||||
format_octal(070707, h + c_magic_offset, c_magic_size);
|
||||
format_octal(archive_entry_dev(entry), h + c_dev_offset, c_dev_size);
|
||||
|
||||
ino = synthesize_ino_value(cpio, entry);
|
||||
if (ino < 0) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"No memory for ino translation table");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
} else if (ino > 0777777) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"Too many files for this cpio format");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
format_octal(ino & 0777777, h + c_ino_offset, c_ino_size);
|
||||
|
||||
/* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */
|
||||
format_octal(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);
|
||||
format_octal(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);
|
||||
format_octal(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);
|
||||
format_octal(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size);
|
||||
if (archive_entry_filetype(entry) == AE_IFBLK
|
||||
|| archive_entry_filetype(entry) == AE_IFCHR)
|
||||
format_octal(archive_entry_rdev(entry), h + c_rdev_offset, c_rdev_size);
|
||||
else
|
||||
format_octal(0, h + c_rdev_offset, c_rdev_size);
|
||||
format_octal(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size);
|
||||
format_octal(pathlength, h + c_namesize_offset, c_namesize_size);
|
||||
|
||||
/* Non-regular files don't store bodies. */
|
||||
if (archive_entry_filetype(entry) != AE_IFREG)
|
||||
archive_entry_set_size(entry, 0);
|
||||
|
||||
/* Symlinks get the link written as the body of the entry. */
|
||||
ret = archive_entry_symlink_l(entry, &p, &len, sconv);
|
||||
if (ret != 0) {
|
||||
if (errno == ENOMEM) {
|
||||
archive_set_error(&a->archive, ENOMEM,
|
||||
"Can't allocate memory for Linkname");
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
|
||||
"Can't translate linkname '%s' to %s",
|
||||
archive_entry_symlink(entry),
|
||||
archive_string_conversion_charset_name(sconv));
|
||||
ret_final = ARCHIVE_WARN;
|
||||
}
|
||||
if (len > 0 && p != NULL && *p != '\0')
|
||||
ret = format_octal(strlen(p), h + c_filesize_offset,
|
||||
c_filesize_size);
|
||||
else
|
||||
ret = format_octal(archive_entry_size(entry),
|
||||
h + c_filesize_offset, c_filesize_size);
|
||||
if (ret) {
|
||||
archive_set_error(&a->archive, ERANGE,
|
||||
"File is too large for cpio format.");
|
||||
ret_final = ARCHIVE_FAILED;
|
||||
goto exit_write_header;
|
||||
}
|
||||
|
||||
ret = __archive_write_output(a, h, sizeof(h));
|
||||
if (ret != ARCHIVE_OK) {
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
|
||||
ret = __archive_write_output(a, path, pathlength);
|
||||
if (ret != ARCHIVE_OK) {
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
|
||||
cpio->entry_bytes_remaining = archive_entry_size(entry);
|
||||
|
||||
/* Write the symlink now. */
|
||||
if (p != NULL && *p != '\0') {
|
||||
ret = __archive_write_output(a, p, strlen(p));
|
||||
if (ret != ARCHIVE_OK) {
|
||||
ret_final = ARCHIVE_FATAL;
|
||||
goto exit_write_header;
|
||||
}
|
||||
}
|
||||
exit_write_header:
|
||||
archive_entry_free(entry_main);
|
||||
return (ret_final);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
archive_write_odc_data(struct archive_write *a, const void *buff, size_t s)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
int ret;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
if (s > cpio->entry_bytes_remaining)
|
||||
s = (size_t)cpio->entry_bytes_remaining;
|
||||
|
||||
ret = __archive_write_output(a, buff, s);
|
||||
cpio->entry_bytes_remaining -= s;
|
||||
if (ret >= 0)
|
||||
return (s);
|
||||
else
|
||||
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, (char *)p, digits);
|
||||
ret = 0;
|
||||
} else {
|
||||
format_octal_recursive(max, (char *)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' + ((char)v & 7);
|
||||
return (v >> 3);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_odc_close(struct archive_write *a)
|
||||
{
|
||||
int er;
|
||||
struct archive_entry *trailer;
|
||||
|
||||
trailer = archive_entry_new2(NULL);
|
||||
/* nlink = 1 here for GNU cpio compat. */
|
||||
archive_entry_set_nlink(trailer, 1);
|
||||
archive_entry_set_size(trailer, 0);
|
||||
archive_entry_set_pathname(trailer, "TRAILER!!!");
|
||||
er = write_header(a, trailer);
|
||||
archive_entry_free(trailer);
|
||||
return (er);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_odc_free(struct archive_write *a)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
free(cpio->ino_list);
|
||||
free(cpio);
|
||||
a->format_data = NULL;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
archive_write_odc_finish_entry(struct archive_write *a)
|
||||
{
|
||||
struct cpio *cpio;
|
||||
|
||||
cpio = (struct cpio *)a->format_data;
|
||||
return (__archive_write_nulls(a,
|
||||
(size_t)cpio->entry_bytes_remaining));
|
||||
}
|
||||
@@ -279,7 +279,7 @@ Values between 0 and 9 are supported.
|
||||
The interpretation of the compression level depends on the chosen
|
||||
compression method.
|
||||
.El
|
||||
.It Format cpio
|
||||
.It Format bin
|
||||
.Bl -tag -compact -width indent
|
||||
.It Cm hdrcharset
|
||||
The value is used as a character set name that will be
|
||||
@@ -519,6 +519,18 @@ XXX needs explanation XXX
|
||||
The value is used as a character set name that will be
|
||||
used when translating file names.
|
||||
.El
|
||||
.It Format odc
|
||||
.Bl -tag -compact -width indent
|
||||
.It Cm hdrcharset
|
||||
The value is used as a character set name that will be
|
||||
used when translating file names.
|
||||
.El
|
||||
.It Format pwb
|
||||
.Bl -tag -compact -width indent
|
||||
.It Cm hdrcharset
|
||||
The value is used as a character set name that will be
|
||||
used when translating file names.
|
||||
.El
|
||||
.It Format pax
|
||||
.Bl -tag -compact -width indent
|
||||
.It Cm hdrcharset
|
||||
|
||||
+141
-75
@@ -56,40 +56,44 @@ The end of the archive is indicated by a special record with
|
||||
the pathname
|
||||
.Dq TRAILER!!! .
|
||||
.Ss PWB format
|
||||
XXX Any documentation of the original PWB/UNIX 1.0 format? XXX
|
||||
.Ss Old Binary Format
|
||||
The old binary
|
||||
The PWB binary
|
||||
.Nm
|
||||
format stores numbers as 2-byte and 4-byte binary values.
|
||||
format is the original format, when cpio was introduced as part of the
|
||||
Programmer's Work Bench system, a variant of 6th Edition UNIX. It
|
||||
stores numbers as 2-byte and 4-byte binary values.
|
||||
Each entry begins with a header in the following format:
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
struct header_old_cpio {
|
||||
unsigned short c_magic;
|
||||
unsigned short c_dev;
|
||||
unsigned short c_ino;
|
||||
unsigned short c_mode;
|
||||
unsigned short c_uid;
|
||||
unsigned short c_gid;
|
||||
unsigned short c_nlink;
|
||||
unsigned short c_rdev;
|
||||
unsigned short c_mtime[2];
|
||||
unsigned short c_namesize;
|
||||
unsigned short c_filesize[2];
|
||||
struct header_pwb_cpio {
|
||||
short h_magic;
|
||||
short h_dev;
|
||||
short h_ino;
|
||||
short h_mode;
|
||||
short h_uid;
|
||||
short h_gid;
|
||||
short h_nlink;
|
||||
short h_majmin;
|
||||
long h_mtime;
|
||||
short h_namesize;
|
||||
long h_filesize;
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Va unsigned short
|
||||
fields here are 16-bit integer values; the
|
||||
.Va unsigned int
|
||||
fields are 32-bit integer values.
|
||||
The fields are as follows
|
||||
.Va short
|
||||
fields here are 16-bit integer values, while the
|
||||
.Va long
|
||||
fields are 32 bit integers. Since PWB UNIX, like the 6th Edition UNIX
|
||||
it was based on, only ran on PDP-11 computers, they
|
||||
are in PDP-endian format, which has little-endian shorts, and
|
||||
big-endian longs. That is, the long integer whose hexadecimal
|
||||
representation is 0x12345678 would be stored in four successive bytes
|
||||
as 0x34, 0x12, 0x78, 0x56.
|
||||
The fields are as follows:
|
||||
.Bl -tag -width indent
|
||||
.It Va magic
|
||||
.It Va h_magic
|
||||
The integer value octal 070707.
|
||||
This value can be used to determine whether this archive is
|
||||
written with little-endian or big-endian integers.
|
||||
.It Va dev , Va ino
|
||||
.It Va h_dev , Va h_ino
|
||||
The device and inode numbers from the disk.
|
||||
These are used by programs that read
|
||||
.Nm
|
||||
@@ -97,9 +101,94 @@ archives to determine when two entries refer to the same file.
|
||||
Programs that synthesize
|
||||
.Nm
|
||||
archives should be careful to set these to distinct values for each entry.
|
||||
.It Va mode
|
||||
The mode specifies both the regular permissions and the file type.
|
||||
It consists of several bit fields as follows:
|
||||
.It Va h_mode
|
||||
The mode specifies both the regular permissions and the file type, and
|
||||
it also holds a couple of bits that are irrelevant to the cpio format,
|
||||
because the field is actually a raw copy of the mode field in the inode
|
||||
representing the file. These are the IALLOC flag, which shows that
|
||||
the inode entry is in use, and the ILARG flag, which shows that the
|
||||
file it represents is large enough to have indirect blocks pointers in
|
||||
the inode.
|
||||
The mode is decoded as follows:
|
||||
.Pp
|
||||
.Bl -tag -width "MMMMMMM" -compact
|
||||
.It 0100000
|
||||
IALLOC flag - irrelevant to cpio.
|
||||
.It 0060000
|
||||
This masks the file type bits.
|
||||
.It 0040000
|
||||
File type value for directories.
|
||||
.It 0020000
|
||||
File type value for character special devices.
|
||||
.It 0060000
|
||||
File type value for block special devices.
|
||||
.It 0010000
|
||||
ILARG flag - irrelevant to cpio.
|
||||
.It 0004000
|
||||
SUID bit.
|
||||
.It 0002000
|
||||
SGID bit.
|
||||
.It 0001000
|
||||
Sticky bit.
|
||||
.It 0000777
|
||||
The lower 9 bits specify read/write/execute permissions
|
||||
for world, group, and user following standard POSIX conventions.
|
||||
.El
|
||||
.It Va h_uid , Va h_gid
|
||||
The numeric user id and group id of the owner.
|
||||
.It Va h_nlink
|
||||
The number of links to this file.
|
||||
Directories always have a value of at least two here.
|
||||
Note that hardlinked files include file data with every copy in the archive.
|
||||
.It Va h_majmin
|
||||
For block special and character special entries,
|
||||
this field contains the associated device number, with the major
|
||||
number in the high byte, and the minor number in the low byte.
|
||||
For all other entry types, it should be set to zero by writers
|
||||
and ignored by readers.
|
||||
.It Va h_mtime
|
||||
Modification time of the file, indicated as the number
|
||||
of seconds since the start of the epoch,
|
||||
00:00:00 UTC January 1, 1970.
|
||||
.It Va h_namesize
|
||||
The number of bytes in the pathname that follows the header.
|
||||
This count includes the trailing NUL byte.
|
||||
.It Va h_filesize
|
||||
The size of the file. Note that this archive format is limited to 16
|
||||
megabyte file sizes, because PWB UNIX, like 6th Edition, only used
|
||||
an unsigned 24 bit integer for the file size internally.
|
||||
.El
|
||||
.Pp
|
||||
The pathname immediately follows the fixed header.
|
||||
If
|
||||
.Cm h_namesize
|
||||
is odd, an additional NUL byte is added after the pathname.
|
||||
The file data is then appended, again with an additional NUL
|
||||
appended if needed to get the next header at an even offset.
|
||||
.Pp
|
||||
Hardlinked files are not given special treatment;
|
||||
the full file contents are included with each copy of the
|
||||
file.
|
||||
.Ss New Binary Format
|
||||
The new binary
|
||||
.Nm
|
||||
format showed up when cpio was adopted into late 7th Edition UNIX.
|
||||
It is exactly like the PWB binary format, described above, except for
|
||||
three changes:
|
||||
.Pp
|
||||
First, UNIX now ran on more than one hardware type, so the endianness
|
||||
of 16 bit integers must be determined by observing the magic number at
|
||||
the start of the header. The 32 bit integers are still always stored
|
||||
with the most significant word first, though, so each of those two, in
|
||||
the struct shown above, was stored as an array of two 16 bit integers,
|
||||
in the traditional order. Those 16 bit integers, like all the others
|
||||
in the struct, were accessed using a macro that byte swapped them if
|
||||
necessary.
|
||||
.Pp
|
||||
Next, 7th Edition had more file types to store, and the IALLOC and ILARG
|
||||
flag bits were re-purposed to accomodate these. The revised use of the
|
||||
various bits is as follows:
|
||||
.Pp
|
||||
.Bl -tag -width "MMMMMMM" -compact
|
||||
.It 0170000
|
||||
This masks the file type bits.
|
||||
@@ -124,51 +213,26 @@ SUID bit.
|
||||
SGID bit.
|
||||
.It 0001000
|
||||
Sticky bit.
|
||||
On some systems, this modifies the behavior of executables and/or directories.
|
||||
.It 0000777
|
||||
The lower 9 bits specify read/write/execute permissions
|
||||
for world, group, and user following standard POSIX conventions.
|
||||
.El
|
||||
.It Va uid , Va gid
|
||||
The numeric user id and group id of the owner.
|
||||
.It Va nlink
|
||||
The number of links to this file.
|
||||
Directories always have a value of at least two here.
|
||||
Note that hardlinked files include file data with every copy in the archive.
|
||||
.It Va rdev
|
||||
For block special and character special entries,
|
||||
this field contains the associated device number.
|
||||
For all other entry types, it should be set to zero by writers
|
||||
and ignored by readers.
|
||||
.It Va mtime
|
||||
Modification time of the file, indicated as the number
|
||||
of seconds since the start of the epoch,
|
||||
00:00:00 UTC January 1, 1970.
|
||||
The four-byte integer is stored with the most-significant 16 bits first
|
||||
followed by the least-significant 16 bits.
|
||||
Each of the two 16 bit values are stored in machine-native byte order.
|
||||
.It Va namesize
|
||||
The number of bytes in the pathname that follows the header.
|
||||
This count includes the trailing NUL byte.
|
||||
.It Va filesize
|
||||
The size of the file.
|
||||
Note that this archive format is limited to
|
||||
four gigabyte file sizes.
|
||||
See
|
||||
.Va mtime
|
||||
above for a description of the storage of four-byte integers.
|
||||
.El
|
||||
.Pp
|
||||
The pathname immediately follows the fixed header.
|
||||
If the
|
||||
.Cm namesize
|
||||
is odd, an additional NUL byte is added after the pathname.
|
||||
The file data is then appended, padded with NUL
|
||||
bytes to an even length.
|
||||
Finally, the file size field now represents a signed 32 bit integer in
|
||||
the underlying file system, so the maximum file size has increased to
|
||||
2 gigabytes.
|
||||
.Pp
|
||||
Hardlinked files are not given special treatment;
|
||||
the full file contents are included with each copy of the
|
||||
file.
|
||||
Note that there is no obvious way to tell which of the two binary
|
||||
formats an archive uses, other than to see which one makes more
|
||||
sense. The typical error scenario is that a PWB format archive
|
||||
unpacked as if it were in the new format will create named sockets
|
||||
instead of directories, and then fail to unpack files that should
|
||||
go in those directories. Running
|
||||
.Va bsdcpio -itv
|
||||
on an unknown archive will make it obvious which it is: if it's
|
||||
PWB format, directories will be listed with an 's' instead of
|
||||
a 'd' as the first character of the mode string, and the larger
|
||||
files will have a '?' in that position.
|
||||
.Ss Portable ASCII Format
|
||||
.St -susv2
|
||||
standardized an ASCII variant that is portable across all
|
||||
@@ -180,6 +244,7 @@ format or as the
|
||||
format.
|
||||
It stores the same numeric fields as the old binary format, but
|
||||
represents them as 6-character or 11-character octal values.
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
struct cpio_odc_header {
|
||||
char c_magic[6];
|
||||
@@ -196,9 +261,9 @@ struct cpio_odc_header {
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The fields are identical to those in the old binary format.
|
||||
The fields are identical to those in the new binary format.
|
||||
The name and file body follow the fixed header.
|
||||
Unlike the old binary format, there is no additional padding
|
||||
Unlike the binary formats, there is no additional padding
|
||||
after the pathname or file contents.
|
||||
If the files being archived are themselves entirely ASCII, then
|
||||
the resulting archive will be entirely ASCII, except for the
|
||||
@@ -207,6 +272,7 @@ NUL byte that terminates the name field.
|
||||
The "new" ASCII format uses 8-byte hexadecimal fields for
|
||||
all numbers and separates device numbers into separate fields
|
||||
for major and minor numbers.
|
||||
.Pp
|
||||
.Bd -literal -offset indent
|
||||
struct cpio_newc_header {
|
||||
char c_magic[6];
|
||||
@@ -227,7 +293,7 @@ struct cpio_newc_header {
|
||||
.Ed
|
||||
.Pp
|
||||
Except as specified below, the fields here match those specified
|
||||
for the old binary format above.
|
||||
for the new binary format above.
|
||||
.Bl -tag -width indent
|
||||
.It Va magic
|
||||
The string
|
||||
@@ -288,9 +354,9 @@ while working in AT&T's Unix Support Group.
|
||||
It appeared in 1977 as part of PWB/UNIX 1.0, the
|
||||
.Dq Programmer's Work Bench
|
||||
derived from
|
||||
.At v6
|
||||
.At 6th Edition UNIX
|
||||
that was used internally at AT&T.
|
||||
Both the old binary and old character formats were in use
|
||||
Both the new binary and old character formats were in use
|
||||
by 1980, according to the System III source released
|
||||
by SCO under their
|
||||
.Dq Ancient Unix
|
||||
@@ -304,9 +370,9 @@ The
|
||||
format is mis-named, as it uses a simple checksum and
|
||||
not a cyclic redundancy check.
|
||||
.Pp
|
||||
The old binary format is limited to 16 bits for user id,
|
||||
group id, device, and inode numbers.
|
||||
It is limited to 4 gigabyte file sizes.
|
||||
The binary formats are limited to 16 bits for user id, group id,
|
||||
device, and inode numbers. They are limited to 16 megabyte and 2
|
||||
gigabyte file sizes for the older and newer variants, respectively.
|
||||
.Pp
|
||||
The old ASCII format is limited to 18 bits for
|
||||
the user id, group id, device, and inode numbers.
|
||||
|
||||
@@ -201,28 +201,27 @@ POSIX.1-2001 extended the ustar format to create the
|
||||
.Dq pax interchange
|
||||
format.
|
||||
.Ss Cpio Formats
|
||||
The libarchive library can read a number of common cpio variants and can write
|
||||
.Dq odc
|
||||
and
|
||||
.Dq newc
|
||||
format archives.
|
||||
A cpio archive stores each entry as a fixed-size header followed
|
||||
by a variable-length filename and variable-length data.
|
||||
Unlike the tar format, the cpio format does only minimal padding
|
||||
of the header or file data.
|
||||
There are several cpio variants, which differ primarily in
|
||||
how they store the initial header: some store the values as
|
||||
octal or hexadecimal numbers in ASCII, others as binary values of
|
||||
varying byte order and length.
|
||||
The libarchive library can read and write a number of common cpio
|
||||
variants. A cpio archive stores each entry as a fixed-size header
|
||||
followed by a variable-length filename and variable-length data.
|
||||
Unlike the tar format, the cpio format does only minimal padding of
|
||||
the header or file data. There are several cpio variants, which
|
||||
differ primarily in how they store the initial header: some store the
|
||||
values as octal or hexadecimal numbers in ASCII, others as binary
|
||||
values of varying byte order and length.
|
||||
.Bl -tag -width indent
|
||||
.It Cm binary
|
||||
The libarchive library transparently reads both big-endian and little-endian
|
||||
variants of the original binary cpio format.
|
||||
This format used 32-bit binary values for file size and mtime,
|
||||
and 16-bit binary values for the other fields.
|
||||
The libarchive library transparently reads both big-endian and
|
||||
little-endian variants of the the two binary cpio formats; the
|
||||
original one from PWB/UNIX, and the later, more widely used, variant.
|
||||
This format used 32-bit binary values for file size and mtime, and
|
||||
16-bit binary values for the other fields. The formats support only
|
||||
the file types present in UNIX at the time of their creation. File
|
||||
sizes are limited to 24 bits in the PWB format, because of the limits
|
||||
of the file system, and to 31 bits in the newer binary format, where
|
||||
signed 32 bit longs were used.
|
||||
.It Cm odc
|
||||
The libarchive library can both read and write this
|
||||
POSIX-standard format, which is officially known as the
|
||||
This is the POSIX standardized format, which is officially known as the
|
||||
.Dq cpio interchange format
|
||||
or the
|
||||
.Dq octet-oriented cpio archive format
|
||||
|
||||
@@ -105,7 +105,7 @@ POSIX
|
||||
.Dq pax interchange format
|
||||
archives,
|
||||
.It
|
||||
POSIX octet-oriented cpio archives,
|
||||
cpio archives,
|
||||
.It
|
||||
Zip archive,
|
||||
.It
|
||||
|
||||
@@ -273,18 +273,28 @@ test_big_entries(int (*set_format)(struct archive *), int64_t size, int expected
|
||||
|
||||
DEFINE_TEST(test_write_format_cpio)
|
||||
{
|
||||
int64_t size_16m = ((int64_t)1) << 24;
|
||||
int64_t size_2g = ((int64_t)1) << 31;
|
||||
int64_t size_4g = ((int64_t)1) << 32;
|
||||
int64_t size_8g = ((int64_t)1) << 33;
|
||||
|
||||
test_format(archive_write_set_format_cpio);
|
||||
test_format(archive_write_set_format_cpio_odc);
|
||||
test_format(archive_write_set_format_cpio_newc);
|
||||
|
||||
test_big_entries(archive_write_set_format_cpio,
|
||||
test_big_entries(archive_write_set_format_cpio_odc,
|
||||
size_8g - 1, ARCHIVE_OK);
|
||||
test_big_entries(archive_write_set_format_cpio,
|
||||
test_big_entries(archive_write_set_format_cpio_odc,
|
||||
size_8g, ARCHIVE_FAILED);
|
||||
test_big_entries(archive_write_set_format_cpio_newc,
|
||||
size_4g - 1, ARCHIVE_OK);
|
||||
test_big_entries(archive_write_set_format_cpio_newc,
|
||||
size_4g, ARCHIVE_FAILED);
|
||||
test_big_entries(archive_write_set_format_cpio_bin,
|
||||
size_2g - 1, ARCHIVE_OK);
|
||||
test_big_entries(archive_write_set_format_cpio_bin,
|
||||
size_2g, ARCHIVE_FAILED);
|
||||
test_big_entries(archive_write_set_format_cpio_pwb,
|
||||
size_16m - 1, ARCHIVE_OK);
|
||||
test_big_entries(archive_write_set_format_cpio_pwb,
|
||||
size_16m, ARCHIVE_FAILED);
|
||||
}
|
||||
@@ -52,7 +52,7 @@ DEFINE_TEST(test_write_format_cpio_empty)
|
||||
|
||||
/* Create a new archive in memory. */
|
||||
assert((a = archive_write_new()) != NULL);
|
||||
assertA(0 == archive_write_set_format_cpio(a));
|
||||
assertA(0 == archive_write_set_format_cpio_odc(a));
|
||||
assertA(0 == archive_write_add_filter_none(a));
|
||||
/* 1-byte block size ensures we see only the required bytes. */
|
||||
/* We're not testing the padding here. */
|
||||
|
||||
@@ -54,7 +54,7 @@ DEFINE_TEST(test_write_format_cpio_odc)
|
||||
|
||||
/* Create a new archive in memory. */
|
||||
assert((a = archive_write_new()) != NULL);
|
||||
assertEqualIntA(a, 0, archive_write_set_format_cpio(a));
|
||||
assertEqualIntA(a, 0, archive_write_set_format_cpio_odc(a));
|
||||
assertEqualIntA(a, 0, archive_write_add_filter_none(a));
|
||||
assertEqualIntA(a, 0, archive_write_open_memory(a, buff, buffsize, &used));
|
||||
|
||||
|
||||
Reference in new issue
Block a user