#define MTREE_HAS_UNAME 0x0400
#define MTREE_HAS_OPTIONAL 0x0800
+#define MTREE_HAS_NOCHANGE 0x1000 /* FreeBSD specific */
struct mtree_option {
struct mtree_option *next;
"md5", "md5digest", "mode", NULL
};
static const char *keys_no[] = {
- "nlink", "optional", NULL
+ "nlink", "nochange", "optional", NULL
};
static const char *keys_r[] = {
"rmd160", "rmd160digest", NULL
* if it wasn't already parsed from the specification.
*/
if (st != NULL) {
- if ((parsed_kws & MTREE_HAS_DEVICE) == 0 &&
+ if (((parsed_kws & MTREE_HAS_DEVICE) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0) &&
(archive_entry_filetype(entry) == AE_IFCHR ||
archive_entry_filetype(entry) == AE_IFBLK))
archive_entry_set_rdev(entry, st->st_rdev);
- if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0)
+ if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
archive_entry_set_gid(entry, st->st_gid);
- if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0)
+ if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
archive_entry_set_uid(entry, st->st_uid);
- if ((parsed_kws & MTREE_HAS_MTIME) == 0) {
+ if ((parsed_kws & MTREE_HAS_MTIME) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0) {
#if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
archive_entry_set_mtime(entry, st->st_mtime,
st->st_mtimespec.tv_nsec);
archive_entry_set_mtime(entry, st->st_mtime, 0);
#endif
}
- if ((parsed_kws & MTREE_HAS_NLINK) == 0)
+ if ((parsed_kws & MTREE_HAS_NLINK) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
archive_entry_set_nlink(entry, st->st_nlink);
- if ((parsed_kws & MTREE_HAS_PERM) == 0)
+ if ((parsed_kws & MTREE_HAS_PERM) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
archive_entry_set_perm(entry, st->st_mode);
- if ((parsed_kws & MTREE_HAS_SIZE) == 0)
+ if ((parsed_kws & MTREE_HAS_SIZE) == 0 ||
+ (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
archive_entry_set_size(entry, st->st_size);
archive_entry_set_ino(entry, st->st_ino);
archive_entry_set_dev(entry, st->st_dev);
if (*key == '\0')
return (ARCHIVE_OK);
+ if (strcmp(key, "nochange") == 0) {
+ *parsed_kws |= MTREE_HAS_NOCHANGE;
+ return (ARCHIVE_OK);
+ }
if (strcmp(key, "optional") == 0) {
*parsed_kws |= MTREE_HAS_OPTIONAL;
return (ARCHIVE_OK);
assertEqualInt(ARCHIVE_OK, archive_read_free(a));
}
+DEFINE_TEST(test_read_format_mtree_nochange)
+{
+ static char archive[] =
+ "#mtree\n"
+ "./a type=file mode=0644 time=123\n"
+ "./b type=file mode=0644 time=234\n"
+ "./c type=file mode=0644 time=345\n";
+ static char archive2[] =
+ "#mtree\n"
+ "./a type=file mode=0644 time=123 nochange\n"
+ "./b type=file mode=0644 time=234\n"
+ "./c type=file mode=0644 time=345 nochange\n";
+ struct archive_entry *ae;
+ struct archive *a;
+
+ assertMakeFile("a", 0640, "12345");
+ assertMakeFile("b", 0664, "123456");
+ assertMakeFile("c", 0755, "1234567");
+
+ /*
+ * Test 1. Read a mtree archive without `nochange' keyword.
+ */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_memory(a, archive, sizeof(archive)));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./a");
+ assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+ assertEqualInt(archive_entry_mtime(ae), 123);
+ assertEqualInt(archive_entry_size(ae), 5);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./b");
+ assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+ assertEqualInt(archive_entry_mtime(ae), 234);
+ assertEqualInt(archive_entry_size(ae), 6);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./c");
+ assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+ assertEqualInt(archive_entry_mtime(ae), 345);
+ assertEqualInt(archive_entry_size(ae), 7);
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(3, archive_file_count(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+ /*
+ * Test 2. Read a mtree archive with `nochange' keyword.
+ */
+ assert((a = archive_read_new()) != NULL);
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_filter_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_support_format_all(a));
+ assertEqualIntA(a, ARCHIVE_OK,
+ archive_read_open_memory(a, archive2, sizeof(archive2)));
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./a");
+ assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0640);
+ assert(archive_entry_mtime(ae) != 123);
+ assertEqualInt(archive_entry_size(ae), 5);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./b");
+ assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+ assertEqualInt(archive_entry_mtime(ae), 234);
+ assertEqualInt(archive_entry_size(ae), 6);
+ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+ assertEqualString(archive_entry_pathname(ae), "./c");
+ assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0755);
+ assert(archive_entry_mtime(ae) != 345);
+ assertEqualInt(archive_entry_size(ae), 7);
+
+ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+ assertEqualInt(3, archive_file_count(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+ assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
+
DEFINE_TEST(test_read_format_mtree_nomagic_v1_form)
{
const char reffile[] = "test_read_format_mtree_nomagic.mtree";