Merge pull request #3321 from carrerasdarren-cell/fix/cpio-newc-inode-synthesis
cpio: synthesize newc inode values
This commit is contained in:
3 files changed
+163
-44
No files matched your search
@@ -113,42 +113,6 @@ DEFINE_TEST(test_format_newc)
|
|||||||
|
|
||||||
/* Setup result message. */
|
/* Setup result message. */
|
||||||
memset(result, 0, sizeof(result));
|
memset(result, 0, sizeof(result));
|
||||||
if (is_LargeInode("file1")) {
|
|
||||||
strncat(result,
|
|
||||||
"bsdcpio: file1: large inode number truncated: ",
|
|
||||||
sizeof(result) - strlen(result) -1);
|
|
||||||
strncat(result, strerror(ERANGE),
|
|
||||||
sizeof(result) - strlen(result) -1);
|
|
||||||
strncat(result, "\n",
|
|
||||||
sizeof(result) - strlen(result) -1);
|
|
||||||
}
|
|
||||||
if (canSymlink() && is_LargeInode("symlink")) {
|
|
||||||
strncat(result,
|
|
||||||
"bsdcpio: symlink: large inode number truncated: ",
|
|
||||||
sizeof(result) - strlen(result) -1);
|
|
||||||
strncat(result, strerror(ERANGE),
|
|
||||||
sizeof(result) - strlen(result) -1);
|
|
||||||
strncat(result, "\n",
|
|
||||||
sizeof(result) - strlen(result) -1);
|
|
||||||
}
|
|
||||||
if (is_LargeInode("dir")) {
|
|
||||||
strncat(result,
|
|
||||||
"bsdcpio: dir: large inode number truncated: ",
|
|
||||||
sizeof(result) - strlen(result) -1);
|
|
||||||
strncat(result, strerror(ERANGE),
|
|
||||||
sizeof(result) - strlen(result) -1);
|
|
||||||
strncat(result, "\n",
|
|
||||||
sizeof(result) - strlen(result) -1);
|
|
||||||
}
|
|
||||||
if (is_LargeInode("hardlink")) {
|
|
||||||
strncat(result,
|
|
||||||
"bsdcpio: hardlink: large inode number truncated: ",
|
|
||||||
sizeof(result) - strlen(result) -1);
|
|
||||||
strncat(result, strerror(ERANGE),
|
|
||||||
sizeof(result) - strlen(result) -1);
|
|
||||||
strncat(result, "\n",
|
|
||||||
sizeof(result) - strlen(result) -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Record some facts about what we just created: */
|
/* Record some facts about what we just created: */
|
||||||
now = time(NULL); /* They were all created w/in last two seconds. */
|
now = time(NULL); /* They were all created w/in last two seconds. */
|
||||||
@@ -225,7 +189,7 @@ DEFINE_TEST(test_format_newc)
|
|||||||
/* "symlink" pointing to "file1" */
|
/* "symlink" pointing to "file1" */
|
||||||
assert(is_hex(e, 110));
|
assert(is_hex(e, 110));
|
||||||
assertEqualMem(e + 0, "070701", 6); /* Magic */
|
assertEqualMem(e + 0, "070701", 6); /* Magic */
|
||||||
assert(is_hex(e + 6, 8)); /* ino */
|
assert(ino != from_hex(e + 6, 8)); /* ino */
|
||||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||||
/* Mode: Group members bits and others bits do not work. */
|
/* Mode: Group members bits and others bits do not work. */
|
||||||
assertEqualInt(0xa180, from_hex(e + 14, 8) & 0xffc0);
|
assertEqualInt(0xa180, from_hex(e + 14, 8) & 0xffc0);
|
||||||
@@ -258,7 +222,7 @@ DEFINE_TEST(test_format_newc)
|
|||||||
/* "dir" */
|
/* "dir" */
|
||||||
assert(is_hex(e, 110));
|
assert(is_hex(e, 110));
|
||||||
assertEqualMem(e + 0, "070701", 6); /* Magic */
|
assertEqualMem(e + 0, "070701", 6); /* Magic */
|
||||||
assert(is_hex(e + 6, 8)); /* ino */
|
assert(ino != from_hex(e + 6, 8)); /* ino */
|
||||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||||
/* Group members bits and others bits do not work. */
|
/* Group members bits and others bits do not work. */
|
||||||
assertEqualInt(0x41c0, from_hex(e + 14, 8) & 0xffc0); /* Mode */
|
assertEqualInt(0x41c0, from_hex(e + 14, 8) & 0xffc0); /* Mode */
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
#include "archive.h"
|
#include "archive.h"
|
||||||
#include "archive_entry.h"
|
#include "archive_entry.h"
|
||||||
#include "archive_entry_locale.h"
|
#include "archive_entry_locale.h"
|
||||||
|
#include "archive_integer.h"
|
||||||
#include "archive_private.h"
|
#include "archive_private.h"
|
||||||
#include "archive_write_private.h"
|
#include "archive_write_private.h"
|
||||||
#include "archive_write_set_format_private.h"
|
#include "archive_write_set_format_private.h"
|
||||||
@@ -62,6 +63,17 @@ struct cpio {
|
|||||||
uint64_t entry_bytes_remaining;
|
uint64_t entry_bytes_remaining;
|
||||||
int padding;
|
int padding;
|
||||||
|
|
||||||
|
int64_t ino_next;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int64_t old_devmajor;
|
||||||
|
int64_t old_devminor;
|
||||||
|
int64_t old_ino;
|
||||||
|
int64_t new_ino;
|
||||||
|
} *ino_list;
|
||||||
|
size_t ino_list_size;
|
||||||
|
size_t ino_list_next;
|
||||||
|
|
||||||
struct archive_string_conv *opt_sconv;
|
struct archive_string_conv *opt_sconv;
|
||||||
struct archive_string_conv *sconv_default;
|
struct archive_string_conv *sconv_default;
|
||||||
int init_default_conversion;
|
int init_default_conversion;
|
||||||
@@ -142,6 +154,86 @@ archive_write_newc_options(struct archive_write *a, const char *key,
|
|||||||
&cpio->opt_sconv));
|
&cpio->opt_sconv));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The newc format stores 32-bit inode numbers and relies on them to identify
|
||||||
|
* hardlinked files. Generate unique in-range values so distinct 64-bit inode
|
||||||
|
* numbers cannot collide when written to the archive.
|
||||||
|
*/
|
||||||
|
static int64_t
|
||||||
|
synthesize_ino_value(struct archive_write *a, struct archive_entry *entry)
|
||||||
|
{
|
||||||
|
struct cpio *cpio = a->format_data;
|
||||||
|
int64_t devmajor = archive_entry_devmajor(entry);
|
||||||
|
int64_t devminor = archive_entry_devminor(entry);
|
||||||
|
int64_t ino = archive_entry_ino64(entry);
|
||||||
|
int64_t ino_new;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (ino == 0)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/* Directory link counts do not represent separate archive entries. */
|
||||||
|
if (archive_entry_nlink(entry) < 2 ||
|
||||||
|
archive_entry_filetype(entry) == AE_IFDIR) {
|
||||||
|
if (cpio->ino_next == UINT32_MAX) {
|
||||||
|
archive_set_error(&a->archive, ERANGE,
|
||||||
|
"No available inode values for cpio format");
|
||||||
|
return (ARCHIVE_FATAL);
|
||||||
|
}
|
||||||
|
return (++cpio->ino_next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Revisit the flat list if large hardlink sets become slow. */
|
||||||
|
for (i = 0; i < cpio->ino_list_next; ++i) {
|
||||||
|
if (cpio->ino_list[i].old_devmajor == devmajor &&
|
||||||
|
cpio->ino_list[i].old_devminor == devminor &&
|
||||||
|
cpio->ino_list[i].old_ino == ino)
|
||||||
|
return (cpio->ino_list[i].new_ino);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpio->ino_next == UINT32_MAX) {
|
||||||
|
archive_set_error(&a->archive, ERANGE,
|
||||||
|
"No available inode values for cpio format");
|
||||||
|
return (ARCHIVE_FATAL);
|
||||||
|
}
|
||||||
|
ino_new = ++cpio->ino_next;
|
||||||
|
|
||||||
|
if (cpio->ino_list_size <= cpio->ino_list_next) {
|
||||||
|
size_t newsize, size;
|
||||||
|
|
||||||
|
if (cpio->ino_list_size < 512)
|
||||||
|
newsize = 512;
|
||||||
|
else if (archive_ckd_mul_size(&newsize,
|
||||||
|
cpio->ino_list_size, 2)) {
|
||||||
|
archive_set_error(&a->archive, ENOMEM,
|
||||||
|
"No memory for inode translation table");
|
||||||
|
return (ARCHIVE_FATAL);
|
||||||
|
}
|
||||||
|
if (archive_ckd_mul_size(&size,
|
||||||
|
newsize, sizeof(cpio->ino_list[0]))) {
|
||||||
|
archive_set_error(&a->archive, ENOMEM,
|
||||||
|
"No memory for inode translation table");
|
||||||
|
return (ARCHIVE_FATAL);
|
||||||
|
}
|
||||||
|
void *newlist = realloc(cpio->ino_list, size);
|
||||||
|
if (newlist == NULL) {
|
||||||
|
archive_set_error(&a->archive, ENOMEM,
|
||||||
|
"No memory for inode translation table");
|
||||||
|
return (ARCHIVE_FATAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
cpio->ino_list_size = newsize;
|
||||||
|
cpio->ino_list = newlist;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpio->ino_list[cpio->ino_list_next].old_devmajor = devmajor;
|
||||||
|
cpio->ino_list[cpio->ino_list_next].old_devminor = devminor;
|
||||||
|
cpio->ino_list[cpio->ino_list_next].old_ino = ino;
|
||||||
|
cpio->ino_list[cpio->ino_list_next].new_ino = ino_new;
|
||||||
|
++cpio->ino_list_next;
|
||||||
|
return (ino_new);
|
||||||
|
}
|
||||||
|
|
||||||
static struct archive_string_conv *
|
static struct archive_string_conv *
|
||||||
get_sconv(struct archive_write *a)
|
get_sconv(struct archive_write *a)
|
||||||
{
|
{
|
||||||
@@ -247,15 +339,14 @@ write_header(struct archive_write *a, struct archive_entry *entry)
|
|||||||
format_hex(archive_entry_devminor(entry), h + c_devminor_offset,
|
format_hex(archive_entry_devminor(entry), h + c_devminor_offset,
|
||||||
c_devminor_size);
|
c_devminor_size);
|
||||||
|
|
||||||
ino = archive_entry_ino64(entry);
|
ino = synthesize_ino_value(a, entry);
|
||||||
if (ino > 0xffffffff) {
|
if (ino < 0) {
|
||||||
archive_set_error(&a->archive, ERANGE,
|
ret_final = (int)ino;
|
||||||
"large inode number truncated");
|
goto exit_write_header;
|
||||||
ret_final = ARCHIVE_WARN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */
|
/* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */
|
||||||
format_hex(ino & 0xffffffff, h + c_ino_offset, c_ino_size);
|
format_hex(ino, h + c_ino_offset, c_ino_size);
|
||||||
format_hex(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);
|
format_hex(archive_entry_mode(entry), h + c_mode_offset, c_mode_size);
|
||||||
format_hex(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);
|
format_hex(archive_entry_uid(entry), h + c_uid_offset, c_uid_size);
|
||||||
format_hex(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);
|
format_hex(archive_entry_gid(entry), h + c_gid_offset, c_gid_size);
|
||||||
@@ -422,6 +513,7 @@ archive_write_newc_free(struct archive_write *a)
|
|||||||
{
|
{
|
||||||
struct cpio *cpio = a->format_data;
|
struct cpio *cpio = a->format_data;
|
||||||
|
|
||||||
|
free(cpio->ino_list);
|
||||||
free(cpio);
|
free(cpio);
|
||||||
a->format_data = NULL;
|
a->format_data = NULL;
|
||||||
return (ARCHIVE_OK);
|
return (ARCHIVE_OK);
|
||||||
|
|||||||
@@ -42,6 +42,24 @@ is_hex(const char *p, size_t l)
|
|||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_newc_entry(struct archive *a, const char *path, int64_t dev, int64_t ino,
|
||||||
|
int nlink)
|
||||||
|
{
|
||||||
|
struct archive_entry *entry;
|
||||||
|
|
||||||
|
assert((entry = archive_entry_new()) != NULL);
|
||||||
|
archive_entry_set_pathname(entry, path);
|
||||||
|
archive_entry_set_mode(entry, AE_IFREG | 0644);
|
||||||
|
archive_entry_set_size(entry, 0);
|
||||||
|
archive_entry_set_dev(entry, dev);
|
||||||
|
archive_entry_set_ino64(entry, ino);
|
||||||
|
archive_entry_set_nlink(entry, nlink);
|
||||||
|
assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, entry));
|
||||||
|
assertEqualIntA(a, ARCHIVE_OK, archive_write_finish_entry(a));
|
||||||
|
archive_entry_free(entry);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Detailed verification that cpio 'newc' archives are written with
|
* Detailed verification that cpio 'newc' archives are written with
|
||||||
* the correct format.
|
* the correct format.
|
||||||
@@ -205,3 +223,48 @@ DEFINE_TEST(test_write_format_cpio_newc)
|
|||||||
|
|
||||||
free(buff);
|
free(buff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFINE_TEST(test_write_format_cpio_newc_large_inode)
|
||||||
|
{
|
||||||
|
struct archive *a;
|
||||||
|
char *buff;
|
||||||
|
const char *first, *again, *other, *small_inode, *different_device;
|
||||||
|
size_t buffsize = 10000;
|
||||||
|
size_t used;
|
||||||
|
|
||||||
|
buff = malloc(buffsize);
|
||||||
|
assert(buff != NULL);
|
||||||
|
assert((a = archive_write_new()) != NULL);
|
||||||
|
assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_cpio_newc(a));
|
||||||
|
assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
|
||||||
|
assertEqualIntA(a, ARCHIVE_OK,
|
||||||
|
archive_write_open_memory(a, buff, buffsize, &used));
|
||||||
|
|
||||||
|
write_newc_entry(a, "first", 12, INT64_C(0x100000001), 2);
|
||||||
|
write_newc_entry(a, "again", 12, INT64_C(0x100000001), 2);
|
||||||
|
write_newc_entry(a, "other", 12, INT64_C(0x200000001), 2);
|
||||||
|
write_newc_entry(a, "small", 12, 1, 1);
|
||||||
|
write_newc_entry(a, "dev13", 13, INT64_C(0x100000001), 2);
|
||||||
|
assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a));
|
||||||
|
assertEqualInt(ARCHIVE_OK, archive_write_free(a));
|
||||||
|
|
||||||
|
/* Each entry is a 110-byte header plus a padded six-byte name. */
|
||||||
|
first = buff + 6;
|
||||||
|
again = buff + 116 + 6;
|
||||||
|
other = buff + 232 + 6;
|
||||||
|
small_inode = buff + 348 + 6;
|
||||||
|
different_device = buff + 464 + 6;
|
||||||
|
assertEqualMem(first, again, 8);
|
||||||
|
assert(memcmp(first, other, 8) != 0);
|
||||||
|
assert(memcmp(first, small_inode, 8) != 0);
|
||||||
|
assert(memcmp(first, different_device, 8) != 0);
|
||||||
|
assert(memcmp(other, small_inode, 8) != 0);
|
||||||
|
assert(memcmp(other, different_device, 8) != 0);
|
||||||
|
assert(memcmp(small_inode, different_device, 8) != 0);
|
||||||
|
assert(memcmp(first, "00000000", 8) != 0);
|
||||||
|
assert(memcmp(other, "00000000", 8) != 0);
|
||||||
|
assert(memcmp(small_inode, "00000000", 8) != 0);
|
||||||
|
assert(memcmp(different_device, "00000000", 8) != 0);
|
||||||
|
|
||||||
|
free(buff);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user