diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/tar/src/write_header.c | 167 | ||||
-rw-r--r-- | lib/tar/test/tar_write_simple.c | 192 |
2 files changed, 226 insertions, 133 deletions
diff --git a/lib/tar/src/write_header.c b/lib/tar/src/write_header.c index dda98b7..d27cef0 100644 --- a/lib/tar/src/write_header.c +++ b/lib/tar/src/write_header.c @@ -41,6 +41,9 @@ static void write_number(char *dst, sqfs_u64 value, int digits) for (i = 0; i < (digits - 1); ++i) mask = (mask << 3) | 7; + if (digits < 0 || (size_t)digits >= sizeof(buffer)) + digits = sizeof(buffer) - 1; + if (value <= mask) { sprintf(buffer, "%0*lo ", digits - 1, (unsigned long)value); memcpy(dst, buffer, digits); @@ -64,36 +67,36 @@ static void write_number_signed(char *dst, sqfs_s64 value, int digits) } } -static int write_header(sqfs_ostream_t *fp, const struct stat *sb, +static int write_header(sqfs_ostream_t *fp, const sqfs_dir_entry_t *ent, const char *name, const char *slink_target, int type) { int maj = 0, min = 0; sqfs_u64 size = 0; tar_header_t hdr; - if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode)) { - maj = major(sb->st_rdev); - min = minor(sb->st_rdev); + if (S_ISCHR(ent->mode) || S_ISBLK(ent->mode)) { + maj = major(ent->rdev); + min = minor(ent->rdev); } - if (S_ISREG(sb->st_mode)) - size = sb->st_size; + if (S_ISREG(ent->mode)) + size = ent->size; memset(&hdr, 0, sizeof(hdr)); strncpy(hdr.name, name, sizeof(hdr.name) - 1); - write_number(hdr.mode, sb->st_mode & ~S_IFMT, sizeof(hdr.mode)); - write_number(hdr.uid, sb->st_uid, sizeof(hdr.uid)); - write_number(hdr.gid, sb->st_gid, sizeof(hdr.gid)); + write_number(hdr.mode, ent->mode & ~S_IFMT, sizeof(hdr.mode)); + write_number(hdr.uid, ent->uid, sizeof(hdr.uid)); + write_number(hdr.gid, ent->gid, sizeof(hdr.gid)); write_number(hdr.size, size, sizeof(hdr.size)); - write_number_signed(hdr.mtime, sb->st_mtime, sizeof(hdr.mtime)); + write_number_signed(hdr.mtime, ent->mtime, sizeof(hdr.mtime)); hdr.typeflag = type; if (slink_target != NULL) - memcpy(hdr.linkname, slink_target, sb->st_size); + memcpy(hdr.linkname, slink_target, ent->size); memcpy(hdr.magic, TAR_MAGIC_OLD, sizeof(hdr.magic)); memcpy(hdr.version, TAR_VERSION_OLD, sizeof(hdr.version)); - sprintf(hdr.uname, "%u", sb->st_uid); - sprintf(hdr.gname, "%u", sb->st_gid); + sprintf(hdr.uname, "%lu", (unsigned long)ent->uid); + sprintf(hdr.gname, "%lu", (unsigned long)ent->gid); write_number(hdr.devmajor, maj, sizeof(hdr.devmajor)); write_number(hdr.devminor, min, sizeof(hdr.devminor)); @@ -102,18 +105,18 @@ static int write_header(sqfs_ostream_t *fp, const struct stat *sb, return fp->append(fp, &hdr, sizeof(hdr)); } -static int write_ext_header(sqfs_ostream_t *fp, const struct stat *orig, +static int write_ext_header(sqfs_ostream_t *fp, const sqfs_dir_entry_t *orig, const char *payload, size_t payload_len, int type, const char *name) { - struct stat sb; + sqfs_dir_entry_t ent; int ret; - sb = *orig; - sb.st_mode = S_IFREG | 0644; - sb.st_size = payload_len; + ent = *orig; + ent.mode = S_IFREG | 0644; + ent.size = payload_len; - ret = write_header(fp, &sb, name, NULL, type); + ret = write_header(fp, &ent, name, NULL, type); if (ret) return ret; @@ -148,7 +151,7 @@ static size_t prefix_digit_len(size_t len) return ndigit; } -static int write_schily_xattr(sqfs_ostream_t *fp, const struct stat *orig, +static int write_schily_xattr(sqfs_ostream_t *fp, const sqfs_dir_entry_t *orig, const char *name, const sqfs_xattr_t *xattr) { static const char *prefix = "SCHILY.xattr."; @@ -184,28 +187,83 @@ static int write_schily_xattr(sqfs_ostream_t *fp, const struct stat *orig, return ret; } -int write_tar_header(sqfs_ostream_t *fp, - const struct stat *sb, const char *name, +static int write_hard_link(sqfs_ostream_t *fp, const sqfs_dir_entry_t *ent, + const char *target, unsigned int counter) +{ + const char *name = ent->name; + tar_header_t hdr; + char buffer[64]; + size_t len; + int ret; + + memset(&hdr, 0, sizeof(hdr)); + + len = strlen(target); + if (len >= 100) { + sprintf(buffer, "gnu/target%u", counter); + ret = write_ext_header(fp, ent, target, len, + TAR_TYPE_GNU_SLINK, buffer); + if (ret) + return ret; + sprintf(hdr.linkname, "hardlink_%u", counter); + } else { + memcpy(hdr.linkname, target, len); + } + + len = strlen(name); + if (len >= 100) { + sprintf(buffer, "gnu/name%u", counter); + ret = write_ext_header(fp, ent, name, len, + TAR_TYPE_GNU_PATH, buffer); + if (ret) + return ret; + sprintf(hdr.name, "gnu/data%u", counter); + } else { + memcpy(hdr.name, name, len); + } + + write_number(hdr.mode, ent->mode & ~S_IFMT, sizeof(hdr.mode)); + write_number(hdr.uid, ent->uid, sizeof(hdr.uid)); + write_number(hdr.gid, ent->gid, sizeof(hdr.gid)); + write_number(hdr.size, 0, sizeof(hdr.size)); + write_number_signed(hdr.mtime, ent->mtime, sizeof(hdr.mtime)); + hdr.typeflag = TAR_TYPE_LINK; + memcpy(hdr.magic, TAR_MAGIC_OLD, sizeof(hdr.magic)); + memcpy(hdr.version, TAR_VERSION_OLD, sizeof(hdr.version)); + sprintf(hdr.uname, "%lu", (unsigned long)ent->uid); + sprintf(hdr.gname, "%lu", (unsigned long)ent->gid); + write_number(hdr.devmajor, 0, sizeof(hdr.devmajor)); + write_number(hdr.devminor, 0, sizeof(hdr.devminor)); + + update_checksum(&hdr); + return fp->append(fp, &hdr, sizeof(hdr)); +} + +int write_tar_header(sqfs_ostream_t *fp, const sqfs_dir_entry_t *ent, const char *slink_target, const sqfs_xattr_t *xattr, unsigned int counter) { + const char *name = ent->name; char buffer[64]; int type, ret; + if (ent->flags & SQFS_DIR_ENTRY_FLAG_HARD_LINK) + return write_hard_link(fp, ent, slink_target, counter); + if (xattr != NULL) { sprintf(buffer, "pax/xattr%u", counter); - ret = write_schily_xattr(fp, sb, buffer, xattr); + ret = write_schily_xattr(fp, ent, buffer, xattr); if (ret) return ret; } - if (!S_ISLNK(sb->st_mode)) + if (!S_ISLNK(ent->mode)) slink_target = NULL; - if (S_ISLNK(sb->st_mode) && sb->st_size >= 100) { + if (S_ISLNK(ent->mode) && ent->size >= 100) { sprintf(buffer, "gnu/target%u", counter); - ret = write_ext_header(fp, sb, slink_target, sb->st_size, + ret = write_ext_header(fp, ent, slink_target, ent->size, TAR_TYPE_GNU_SLINK, buffer); if (ret) return ret; @@ -215,7 +273,7 @@ int write_tar_header(sqfs_ostream_t *fp, if (strlen(name) >= 100) { sprintf(buffer, "gnu/name%u", counter); - ret = write_ext_header(fp, sb, name, strlen(name), + ret = write_ext_header(fp, ent, name, strlen(name), TAR_TYPE_GNU_PATH, buffer); if (ret) return ret; @@ -224,7 +282,7 @@ int write_tar_header(sqfs_ostream_t *fp, name = buffer; } - switch (sb->st_mode & S_IFMT) { + switch (ent->mode & S_IFMT) { case S_IFCHR: type = TAR_TYPE_CHARDEV; break; case S_IFBLK: type = TAR_TYPE_BLOCKDEV; break; case S_IFLNK: type = TAR_TYPE_SLINK; break; @@ -235,56 +293,5 @@ int write_tar_header(sqfs_ostream_t *fp, return SQFS_ERROR_UNSUPPORTED; } - return write_header(fp, sb, name, slink_target, type); -} - -int write_hard_link(sqfs_ostream_t *fp, const struct stat *sb, const char *name, - const char *target, unsigned int counter) -{ - tar_header_t hdr; - char buffer[64]; - size_t len; - int ret; - - memset(&hdr, 0, sizeof(hdr)); - - len = strlen(target); - if (len >= 100) { - sprintf(buffer, "gnu/target%u", counter); - ret = write_ext_header(fp, sb, target, len, - TAR_TYPE_GNU_SLINK, buffer); - if (ret) - return ret; - sprintf(hdr.linkname, "hardlink_%u", counter); - } else { - memcpy(hdr.linkname, target, len); - } - - len = strlen(name); - if (len >= 100) { - sprintf(buffer, "gnu/name%u", counter); - ret = write_ext_header(fp, sb, name, len, - TAR_TYPE_GNU_PATH, buffer); - if (ret) - return ret; - sprintf(hdr.name, "gnu/data%u", counter); - } else { - memcpy(hdr.name, name, len); - } - - write_number(hdr.mode, sb->st_mode & ~S_IFMT, sizeof(hdr.mode)); - write_number(hdr.uid, sb->st_uid, sizeof(hdr.uid)); - write_number(hdr.gid, sb->st_gid, sizeof(hdr.gid)); - write_number(hdr.size, 0, sizeof(hdr.size)); - write_number_signed(hdr.mtime, sb->st_mtime, sizeof(hdr.mtime)); - hdr.typeflag = TAR_TYPE_LINK; - memcpy(hdr.magic, TAR_MAGIC_OLD, sizeof(hdr.magic)); - memcpy(hdr.version, TAR_VERSION_OLD, sizeof(hdr.version)); - sprintf(hdr.uname, "%u", sb->st_uid); - sprintf(hdr.gname, "%u", sb->st_gid); - write_number(hdr.devmajor, 0, sizeof(hdr.devmajor)); - write_number(hdr.devminor, 0, sizeof(hdr.devminor)); - - update_checksum(&hdr); - return fp->append(fp, &hdr, sizeof(hdr)); + return write_header(fp, ent, name, slink_target, type); } diff --git a/lib/tar/test/tar_write_simple.c b/lib/tar/test/tar_write_simple.c index 438ea37..c82fe08 100644 --- a/lib/tar/test/tar_write_simple.c +++ b/lib/tar/test/tar_write_simple.c @@ -11,6 +11,35 @@ #include "sqfs/xattr.h" #include "compat.h" +static void hex_dump(const sqfs_u8 *data, size_t size) +{ + for (size_t i = 0; i < size; ++i) { + int hi = (data[i] >> 4) & 0x0F; + int lo = data[i] & 0x0F; + + hi = (hi >= 0x0a) ? ('a' + (hi - 0x0a)) : ('0' + hi); + lo = (lo >= 0x0a) ? ('a' + (lo - 0x0a)) : ('0' + lo); + + fprintf(stderr, "%c%c", hi, lo); + + if ((i % 16) == 15) { + fputs(" | ", stderr); + + for (size_t j = i - 15; j <= i; ++j) { + if (data[j] >= 0x20 && data[j] <= 0x7f) { + fputc(data[j], stderr); + } else { + fputc('.', stderr); + } + } + + fputc('\n', stderr); + } else { + fputc(' ', stderr); + } + } +} + /*****************************************************************************/ static int buffer_append(sqfs_ostream_t *strm, const void *data, size_t size); @@ -77,67 +106,95 @@ static sqfs_xattr_t *mkxattr_chain(void) int main(int argc, char **argv) { + sqfs_dir_entry_t *ent; sqfs_xattr_t *xattr; sqfs_istream_t *fp; - struct stat sb; int ret; (void)argc; (void)argv; /* genereate some archive contents */ - memset(&sb, 0, sizeof(sb)); - sb.st_mode = S_IFDIR | 0755; - sb.st_mtime = TIME_STAMP; - ret = write_tar_header(&mem_stream, &sb, "dev/", NULL, NULL, 0); + ent = sqfs_dir_entry_create("dev/", S_IFDIR | 0755, 0); + TEST_NOT_NULL(ent); + ent->mtime = TIME_STAMP; + ret = write_tar_header(&mem_stream, ent, NULL, NULL, 0); + sqfs_free(ent); TEST_EQUAL_I(ret, 0); /* device files */ - sb.st_mode = S_IFCHR | 0620; - sb.st_gid = 5; - sb.st_rdev = makedev(4, 0); - ret = write_tar_header(&mem_stream, &sb, "dev/tty0", NULL, NULL, 1); + ent = sqfs_dir_entry_create("dev/tty0", S_IFCHR | 0620, 0); + TEST_NOT_NULL(ent); + ent->mtime = TIME_STAMP; + ent->gid = 5; + ent->rdev = makedev(4, 0); + ret = write_tar_header(&mem_stream, ent, NULL, NULL, 1); + sqfs_free(ent); TEST_EQUAL_I(ret, 0); - sb.st_rdev = makedev(4, 1); - ret = write_tar_header(&mem_stream, &sb, "dev/tty1", NULL, NULL, 2); + ent = sqfs_dir_entry_create("dev/tty1", S_IFCHR | 0620, 0); + TEST_NOT_NULL(ent); + ent->mtime = TIME_STAMP; + ent->gid = 5; + ent->rdev = makedev(4, 1); + ret = write_tar_header(&mem_stream, ent, NULL, NULL, 2); + sqfs_free(ent); TEST_EQUAL_I(ret, 0); - sb.st_rdev = makedev(4, 2); - ret = write_tar_header(&mem_stream, &sb, "dev/tty2", NULL, NULL, 3); + ent = sqfs_dir_entry_create("dev/tty2", S_IFCHR | 0620, 0); + TEST_NOT_NULL(ent); + ent->mtime = TIME_STAMP; + ent->gid = 5; + ent->rdev = makedev(4, 2); + ret = write_tar_header(&mem_stream, ent, NULL, NULL, 3); + sqfs_free(ent); TEST_EQUAL_I(ret, 0); - memset(&sb, 0, sizeof(sb)); - sb.st_mode = S_IFDIR | 0755; - sb.st_mtime = TIME_STAMP; - ret = write_tar_header(&mem_stream, &sb, "usr/", NULL, NULL, 4); + ent = sqfs_dir_entry_create("usr/", S_IFDIR | 0755, 0); + TEST_NOT_NULL(ent); + ent->mtime = TIME_STAMP; + ret = write_tar_header(&mem_stream, ent, NULL, NULL, 4); + sqfs_free(ent); TEST_EQUAL_I(ret, 0); - ret = write_tar_header(&mem_stream, &sb, "usr/bin/", NULL, NULL, 5); + ent = sqfs_dir_entry_create("usr/bin/", S_IFDIR | 0755, 0); + TEST_NOT_NULL(ent); + ent->mtime = TIME_STAMP; + ret = write_tar_header(&mem_stream, ent, NULL, NULL, 5); + sqfs_free(ent); TEST_EQUAL_I(ret, 0); /* sym link */ - sb.st_mode = S_IFLNK | 0777; - sb.st_size = 7; - ret = write_tar_header(&mem_stream, &sb, "bin", "usr/bin", NULL, 6); + ent = sqfs_dir_entry_create("bin", S_IFLNK | 0777, 0); + TEST_NOT_NULL(ent); + ent->mtime = TIME_STAMP; + ent->size = 7; + ret = write_tar_header(&mem_stream, ent, "usr/bin", NULL, 6); + sqfs_free(ent); TEST_EQUAL_I(ret, 0); - memset(&sb, 0, sizeof(sb)); - sb.st_mode = S_IFDIR | 0755; - sb.st_mtime = TIME_STAMP; - ret = write_tar_header(&mem_stream, &sb, "home/", NULL, NULL, 7); + ent = sqfs_dir_entry_create("home/", S_IFDIR | 0755, 0); + TEST_NOT_NULL(ent); + ent->mtime = TIME_STAMP; + ret = write_tar_header(&mem_stream, ent, NULL, NULL, 7); + sqfs_free(ent); TEST_EQUAL_I(ret, 0); - sb.st_mode = S_IFDIR | 0750; - sb.st_uid = 1000; - sb.st_gid = 1000; - ret = write_tar_header(&mem_stream, &sb, "home/goliath/", - NULL, NULL, 8); + ent = sqfs_dir_entry_create("home/goliath/", S_IFDIR | 0750, 0); + TEST_NOT_NULL(ent); + ent->uid = 1000; + ent->gid = 1000; + ent->mtime = TIME_STAMP; + ret = write_tar_header(&mem_stream, ent, NULL, NULL, 8); + sqfs_free(ent); TEST_EQUAL_I(ret, 0); /* regular file with actual content */ - sb.st_mode = S_IFREG | 0644; - sb.st_size = 14; - ret = write_tar_header(&mem_stream, &sb, "home/goliath/hello.txt", - NULL, NULL, 9); + ent = sqfs_dir_entry_create("home/goliath/hello.txt", S_IFREG | 0644, 0); + TEST_NOT_NULL(ent); + ent->uid = 1000; + ent->gid = 1000; + ent->mtime = TIME_STAMP; + ent->size = 14; + ret = write_tar_header(&mem_stream, ent, NULL, NULL, 9); TEST_EQUAL_I(ret, 0); ret = mem_stream.append(&mem_stream, "Hello, World!\n", 14); @@ -146,17 +203,24 @@ int main(int argc, char **argv) TEST_EQUAL_I(ret, 0); /* hard link */ - ret = write_hard_link(&mem_stream, &sb, "home/goliath/world.txt", - "home/goliath/hello.txt", 10); + strcpy(ent->name, "home/goliath/world.txt"); + ent->size = 22; + ent->flags = SQFS_DIR_ENTRY_FLAG_HARD_LINK; + ret = write_tar_header(&mem_stream, ent, "home/goliath/hello.txt", + NULL, 10); TEST_EQUAL_I(ret, 0); /* something with xattrs */ + strcpy(ent->name, "home/goliath/test.exe"); + ent->flags = 0; + ent->mode = S_IFREG | 0750; + ent->size = 4; + xattr = mkxattr_chain(); - sb.st_mode = S_IFREG | 0750; - sb.st_size = 4; - ret = write_tar_header(&mem_stream, &sb, "home/goliath/test.exe", - NULL, xattr, 11); + + ret = write_tar_header(&mem_stream, ent, NULL, xattr, 11); TEST_EQUAL_I(ret, 0); + sqfs_free(ent); sqfs_xattr_list_free(xattr); ret = mem_stream.append(&mem_stream, ":-)\n", 4); @@ -165,15 +229,16 @@ int main(int argc, char **argv) TEST_EQUAL_I(ret, 0); /* now try something with a long name */ - memset(&sb, 0, sizeof(sb)); - sb.st_mode = S_IFREG | 0755; - sb.st_mtime = TIME_STAMP; - sb.st_size = 42; - ret = write_tar_header(&mem_stream, &sb, - "mnt/windows_drive/C/Documents and Settings/" - "Joe Random User/My Documents/My Evil Plans/" - "file format nonsense/really long name.doc", - NULL, NULL, 12); + ent = sqfs_dir_entry_create("mnt/windows_drive/C/Documents and Settings/" + "Joe Random User/My Documents/My Evil Plans/" + "file format nonsense/really long name.doc", + S_IFREG | 0755, 0); + TEST_NOT_NULL(ent); + ent->mtime = TIME_STAMP; + ent->size = 42; + + ret = write_tar_header(&mem_stream, ent, NULL, NULL, 12); + sqfs_free(ent); TEST_EQUAL_I(ret, 0); ret = mem_stream.append(&mem_stream, @@ -184,7 +249,6 @@ int main(int argc, char **argv) TEST_EQUAL_I(ret, 0); /* compare with reference */ - TEST_EQUAL_UI(wr_offset, sizeof(wr_buffer)); TEST_EQUAL_UI(sizeof(rd_buffer), sizeof(wr_buffer)); ret = sqfs_istream_open_file(&fp, @@ -202,7 +266,29 @@ int main(int argc, char **argv) sqfs_drop(fp); - ret = memcmp(wr_buffer, rd_buffer, sizeof(rd_buffer)); - TEST_EQUAL_I(ret, 0); - return EXIT_SUCCESS; + if (wr_offset != sizeof(wr_buffer)) { + fprintf(stderr, "Result data size should be: %u, " + "but actually is: %u\n", (unsigned int)wr_offset, + (unsigned int)sizeof(wr_buffer)); + ret = -1; + } + + for (size_t i = 0; i < wr_offset; i += 512) { + size_t diff = (wr_offset - i) > 512 ? 512 : (wr_offset - i); + + if (memcmp(wr_buffer + i, rd_buffer + i, diff) != 0) { + fprintf(stderr, "Difference at offset %u:\n", + (unsigned int)i); + + fputs("Reference:\n", stderr); + hex_dump(rd_buffer + i, diff); + + fputs("Result:\n", stderr); + hex_dump(wr_buffer + i, diff); + ret = -1; + break; + } + } + + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } |