Skip to content
Snippets Groups Projects
Commit 763cd60e authored by Ludovico de Nittis's avatar Ludovico de Nittis
Browse files

Merge branch 'wip/smcv/mtree-enhancements' into 'master'

Improve compatibility with other mtree implementations

See merge request !320
parents a5c4b4be 16e20ba0
No related branches found
No related tags found
1 merge request!320Improve compatibility with other mtree implementations
Pipeline #14032 passed
...@@ -40,12 +40,51 @@ ...@@ -40,12 +40,51 @@
#define trace(...) do { } while (0) #define trace(...) do { } while (0)
#endif #endif
gboolean static gboolean
pv_mtree_entry_parse (const char *line, is_token (const char *token,
PvMtreeEntry *entry, const char *equals,
const char *filename, const char *expected)
guint line_number, {
GError **error) if (equals == NULL)
{
return strcmp (token, expected) == 0;
}
else
{
g_assert (equals >= token);
g_assert (*equals == '=');
return strncmp (token, expected, equals - token) == 0;
}
}
static gboolean
require_value (const char *token,
const char *equals,
GError **error)
{
if (equals == NULL)
return glnx_throw (error, "%s requires a value", token);
return TRUE;
}
static gboolean
forbid_value (const char *token,
const char *equals,
GError **error)
{
if (equals != NULL)
return glnx_throw (error, "%s does not take a value", token);
return TRUE;
}
static gboolean
pv_mtree_entry_parse_internal (const char *line,
PvMtreeEntry *entry,
const char *filename,
guint line_number,
GError **error)
{ {
PvMtreeEntry blank = PV_MTREE_ENTRY_BLANK; PvMtreeEntry blank = PV_MTREE_ENTRY_BLANK;
g_auto(GStrv) tokens = NULL; g_auto(GStrv) tokens = NULL;
...@@ -57,19 +96,14 @@ pv_mtree_entry_parse (const char *line, ...@@ -57,19 +96,14 @@ pv_mtree_entry_parse (const char *line,
return TRUE; return TRUE;
if (line[0] == '/') if (line[0] == '/')
return glnx_throw (error, return glnx_throw (error, "Special commands not supported");
"%s:%u: Special commands not supported",
filename, line_number);
if (line[0] != '.' || (line[1] != ' ' && line[1] != '/' && line[1] != '\0')) if (line[0] != '.' || (line[1] != ' ' && line[1] != '/' && line[1] != '\0'))
return glnx_throw (error, return glnx_throw (error,
"%s:%u: Filenames not relative to top level not supported", "Filenames not relative to top level not supported");
filename, line_number);
if (g_str_has_suffix (line, "\\")) if (g_str_has_suffix (line, "\\"))
return glnx_throw (error, return glnx_throw (error, "Continuation lines not supported");
"%s:%u: Continuation lines not supported",
filename, line_number);
for (i = 0; line[i] != '\0'; i++) for (i = 0; line[i] != '\0'; i++)
{ {
...@@ -96,8 +130,8 @@ pv_mtree_entry_parse (const char *line, ...@@ -96,8 +130,8 @@ pv_mtree_entry_parse (const char *line,
* newline are not supported here */ * newline are not supported here */
default: default:
return glnx_throw (error, return glnx_throw (error,
"%s:%u: Unsupported backslash escape: \"\\%c\"", "Unsupported backslash escape: \"\\%c\"",
filename, line_number, line[i + 1]); line[i + 1]);
} }
} }
} }
...@@ -105,116 +139,175 @@ pv_mtree_entry_parse (const char *line, ...@@ -105,116 +139,175 @@ pv_mtree_entry_parse (const char *line,
tokens = g_strsplit_set (line, " \t", -1); tokens = g_strsplit_set (line, " \t", -1);
if (tokens == NULL) if (tokens == NULL)
return glnx_throw (error, "%s:%u: Line is empty", filename, line_number); return glnx_throw (error, "Line is empty");
entry->name = g_strcompress (tokens[0]); entry->name = g_strcompress (tokens[0]);
for (i = 1; tokens[i] != NULL; i++) for (i = 1; tokens[i] != NULL; i++)
{ {
static const char * const ignored[] =
{
"cksum",
"device",
"flags",
"gid",
"gname",
"inode",
"md5",
"md5digest",
"nlink",
"resdevice",
"ripemd160digest",
"rmd160",
"rmd160digest",
"sha1",
"sha1digest",
"sha384",
"sha384digest",
"sha512",
"sha512digest",
"uid",
"uname",
};
const char *equals; const char *equals;
char *endptr; char *endptr;
gsize j;
equals = strchr (tokens[i], '='); equals = strchr (tokens[i], '=');
if (equals == NULL) for (j = 0; j < G_N_ELEMENTS (ignored); j++)
return glnx_throw (error, {
"%s:%u: No value for keyword '%s'", if (is_token (tokens[i], equals, ignored[j]))
filename, line_number, tokens[i]); break;
}
if (g_str_has_prefix (tokens[i], "cksum=")
|| g_str_has_prefix (tokens[i], "device=") if (j < G_N_ELEMENTS (ignored))
|| g_str_has_prefix (tokens[i], "flags=")
|| g_str_has_prefix (tokens[i], "gid=")
|| g_str_has_prefix (tokens[i], "gname=")
|| g_str_has_prefix (tokens[i], "ignore=")
|| g_str_has_prefix (tokens[i], "inode=")
|| g_str_has_prefix (tokens[i], "md5=")
|| g_str_has_prefix (tokens[i], "md5digest=")
|| g_str_has_prefix (tokens[i], "nlink=")
|| g_str_has_prefix (tokens[i], "nochange=")
|| g_str_has_prefix (tokens[i], "optional=")
|| g_str_has_prefix (tokens[i], "resdevice=")
|| g_str_has_prefix (tokens[i], "ripemd160digest=")
|| g_str_has_prefix (tokens[i], "rmd160=")
|| g_str_has_prefix (tokens[i], "rmd160digest=")
|| g_str_has_prefix (tokens[i], "sha1=")
|| g_str_has_prefix (tokens[i], "sha1digest=")
|| g_str_has_prefix (tokens[i], "sha384=")
|| g_str_has_prefix (tokens[i], "sha384digest=")
|| g_str_has_prefix (tokens[i], "sha512=")
|| g_str_has_prefix (tokens[i], "sha512digest=")
|| g_str_has_prefix (tokens[i], "uid=")
|| g_str_has_prefix (tokens[i], "uname="))
continue; continue;
if (g_str_has_prefix (tokens[i], "link=")) #define REQUIRE_VALUE \
G_STMT_START \
{ \
if (!require_value (tokens[i], equals, error)) \
return FALSE; \
} \
G_STMT_END
#define FORBID_VALUE(token) \
G_STMT_START \
{ \
if (!forbid_value ((token), equals, error)) \
return FALSE; \
} \
G_STMT_END
if (is_token (tokens[i], equals, "link"))
{ {
REQUIRE_VALUE;
entry->link = g_strcompress (equals + 1); entry->link = g_strcompress (equals + 1);
continue; continue;
} }
if (g_str_has_prefix (tokens[i], "contents=")) if (is_token (tokens[i], equals, "contents")
|| is_token (tokens[i], equals, "content"))
{ {
REQUIRE_VALUE;
entry->contents = g_strcompress (equals + 1); entry->contents = g_strcompress (equals + 1);
continue; continue;
} }
if (g_str_has_prefix (tokens[i], "sha256=") if (is_token (tokens[i], equals, "sha256")
|| g_str_has_prefix (tokens[i], "sha256digest=")) || is_token (tokens[i], equals, "sha256digest"))
{ {
REQUIRE_VALUE;
if (entry->sha256 == NULL) if (entry->sha256 == NULL)
entry->sha256 = g_strdup (equals + 1); entry->sha256 = g_strdup (equals + 1);
else if (strcmp (entry->sha256, equals + 1) != 0) else if (strcmp (entry->sha256, equals + 1) != 0)
return glnx_throw (error, return glnx_throw (error,
"%s:%u: sha256 and sha256digest not consistent", "sha256 and sha256digest not consistent");
filename, line_number);
continue; continue;
} }
if (g_str_has_prefix (tokens[i], "mode=")) if (is_token (tokens[i], equals, "mode"))
{ {
gint64 value = g_ascii_strtoll (equals + 1, &endptr, 8); gint64 value;
REQUIRE_VALUE;
value = g_ascii_strtoll (equals + 1, &endptr, 8);
if (equals[1] == '\0' || *endptr != '\0') if (equals[1] == '\0' || *endptr != '\0')
return glnx_throw (error, return glnx_throw (error, "Invalid mode %s", equals + 1);
"%s:%u: Invalid mode %s",
filename, line_number, equals + 1);
entry->mode = value & 07777; entry->mode = value & 07777;
continue; continue;
} }
if (g_str_has_prefix (tokens[i], "size=")) if (is_token (tokens[i], equals, "size"))
{ {
gint64 value = g_ascii_strtoll (equals + 1, &endptr, 10); gint64 value;
REQUIRE_VALUE;
value = g_ascii_strtoll (equals + 1, &endptr, 10);
if (equals[1] == '\0' || *endptr != '\0') if (equals[1] == '\0' || *endptr != '\0')
return glnx_throw (error, return glnx_throw (error, "Invalid size %s", equals + 1);
"%s:%u: Invalid size %s",
filename, line_number, equals + 1);
entry->size = value; entry->size = value;
continue; continue;
} }
if (g_str_has_prefix (tokens[i], "time=")) if (is_token (tokens[i], equals, "time"))
{ {
gdouble value = g_ascii_strtod (equals + 1, &endptr); guint64 value;
guint64 ns = 0;
REQUIRE_VALUE;
value = g_ascii_strtoull (equals + 1, &endptr, 10);
if (equals[1] == '\0' || (*endptr != '\0' && *endptr != '.'))
return glnx_throw (error, "Invalid time %s", equals + 1);
/* This is silly, but time=1.234 has historically meant
* 1 second + 234 nanoseconds, or what normal people would
* write as 1.000000234, so parsing it as a float is incorrect
* (for example mtree-netbsd in Debian still prints it
* like that).
*
* time=1.0 is unambiguous, and so is time=1.123456789
* with exactly 9 digits. */
if (*endptr == '.' && strcmp (endptr, ".0") != 0)
{
const char *dot = endptr;
if (equals[1] == '\0' || *endptr != '\0') ns = g_ascii_strtoull (dot + 1, &endptr, 10);
return glnx_throw (error,
"%s:%u: Invalid time %s", if (dot[1] == '\0' || *endptr != '\0' || ns > 999999999)
filename, line_number, equals + 1); return glnx_throw (error, "Invalid nanoseconds count %s",
dot + 1);
entry->mtime_usec = (value * G_TIME_SPAN_SECOND); /* If necessary this could become just a warning, but for
* now require it to be unambiguous - libarchive and
* FreeBSD mtree show this unambiguous format. */
if (endptr != dot + 10)
return glnx_throw (error,
"Ambiguous nanoseconds count %s, "
"should have exactly 9 digits",
dot + 1);
}
/* We store it as a GTimeSpan which is "only" microsecond
* precision. */
entry->mtime_usec = (value * G_TIME_SPAN_SECOND) + (ns / 1000);
continue; continue;
} }
if (g_str_has_prefix (tokens[i], "type=")) if (is_token (tokens[i], equals, "type"))
{ {
int value; int value;
REQUIRE_VALUE;
if (srt_enum_from_nick (PV_TYPE_MTREE_ENTRY_KIND, equals + 1, if (srt_enum_from_nick (PV_TYPE_MTREE_ENTRY_KIND, equals + 1,
&value, NULL)) &value, NULL))
entry->kind = value; entry->kind = value;
...@@ -224,24 +317,56 @@ pv_mtree_entry_parse (const char *line, ...@@ -224,24 +317,56 @@ pv_mtree_entry_parse (const char *line,
continue; continue;
} }
if (is_token (tokens[i], equals, "ignore"))
{
FORBID_VALUE ("ignore");
entry->entry_flags |= PV_MTREE_ENTRY_FLAGS_IGNORE_BELOW;
continue;
}
if (is_token (tokens[i], equals, "nochange"))
{
FORBID_VALUE ("nochange");
entry->entry_flags |= PV_MTREE_ENTRY_FLAGS_NO_CHANGE;
continue;
}
if (is_token (tokens[i], equals, "optional"))
{
FORBID_VALUE ("optional");
entry->entry_flags |= PV_MTREE_ENTRY_FLAGS_OPTIONAL;
continue;
}
g_warning ("%s:%u: Unknown mtree keyword %s", g_warning ("%s:%u: Unknown mtree keyword %s",
filename, line_number, tokens[i]); filename, line_number, tokens[i]);
} }
if (entry->kind == PV_MTREE_ENTRY_KIND_UNKNOWN) if (entry->kind == PV_MTREE_ENTRY_KIND_UNKNOWN)
return glnx_throw (error, return glnx_throw (error, "Unknown mtree entry type");
"%s:%u: Unknown mtree entry type",
filename, line_number);
if (entry->link != NULL && entry->kind != PV_MTREE_ENTRY_KIND_LINK) if (entry->link != NULL && entry->kind != PV_MTREE_ENTRY_KIND_LINK)
return glnx_throw (error, return glnx_throw (error, "Non-symlink cannot have a symlink target");
"%s:%u: Non-symlink cannot have a symlink target",
filename, line_number);
if (entry->link == NULL && entry->kind == PV_MTREE_ENTRY_KIND_LINK) if (entry->link == NULL && entry->kind == PV_MTREE_ENTRY_KIND_LINK)
return glnx_throw (error, return glnx_throw (error, "Symlink must have a symlink target");
"%s:%u: Symlink must have a symlink target",
filename, line_number); return TRUE;
}
gboolean
pv_mtree_entry_parse (const char *line,
PvMtreeEntry *entry,
const char *filename,
guint line_number,
GError **error)
{
if (!pv_mtree_entry_parse_internal (line, entry,
filename, line_number, error))
{
g_prefix_error (error, "%s: %u: ", filename, line_number);
return FALSE;
}
return TRUE; return TRUE;
} }
...@@ -465,6 +590,7 @@ pv_mtree_apply (const char *mtree, ...@@ -465,6 +590,7 @@ pv_mtree_apply (const char *mtree,
/* For other regular files we just assert that it already exists /* For other regular files we just assert that it already exists
* (and is not a symlink). */ * (and is not a symlink). */
if (fd < 0 if (fd < 0
&& !(entry.entry_flags & PV_MTREE_ENTRY_FLAGS_OPTIONAL)
&& !glnx_openat_rdonly (parent_fd, base, FALSE, &fd, error)) && !glnx_openat_rdonly (parent_fd, base, FALSE, &fd, error))
return glnx_prefix_error (error, return glnx_prefix_error (error,
"Unable to open \"%s\" in \"%s\"", "Unable to open \"%s\" in \"%s\"",
...@@ -519,7 +645,9 @@ pv_mtree_apply (const char *mtree, ...@@ -519,7 +645,9 @@ pv_mtree_apply (const char *mtree,
else else
adjusted_mode = 0644; adjusted_mode = 0644;
if (fd >= 0 && !glnx_fchmod (fd, adjusted_mode, error)) if (fd >= 0
&& !(entry.entry_flags & PV_MTREE_ENTRY_FLAGS_NO_CHANGE)
&& !glnx_fchmod (fd, adjusted_mode, error))
{ {
g_prefix_error (error, g_prefix_error (error,
"Unable to set mode of \"%s\" in \"%s\": ", "Unable to set mode of \"%s\" in \"%s\": ",
...@@ -527,7 +655,10 @@ pv_mtree_apply (const char *mtree, ...@@ -527,7 +655,10 @@ pv_mtree_apply (const char *mtree,
return FALSE; return FALSE;
} }
if (entry.mtime_usec >= 0 && fd >= 0 && entry.kind == PV_MTREE_ENTRY_KIND_FILE) if (entry.mtime_usec >= 0
&& fd >= 0
&& !(entry.entry_flags & PV_MTREE_ENTRY_FLAGS_NO_CHANGE)
&& entry.kind == PV_MTREE_ENTRY_KIND_FILE)
{ {
struct timespec times[2] = struct timespec times[2] =
{ {
......
...@@ -47,6 +47,23 @@ typedef enum ...@@ -47,6 +47,23 @@ typedef enum
PV_MTREE_ENTRY_KIND_SOCKET = 's', PV_MTREE_ENTRY_KIND_SOCKET = 's',
} PvMtreeEntryKind; } PvMtreeEntryKind;
/*
* PvMtreeEntryFlags:
* @PV_MTREE_ENTRY_FLAGS_IGNORE_BELOW: Anything below this directory (but
* not the directory itself!) is to be ignored
* @PV_MTREE_ENTRY_FLAGS_NO_CHANGE: When applying a manifest to a directory
* on disk, don't modify this file or directory
* @PV_MTREE_ENTRY_FLAGS_OPTIONAL: When applying or verifying a manifest,
* it's OK if this item doesn't exist
*/
typedef enum
{
PV_MTREE_ENTRY_FLAGS_IGNORE_BELOW = (1 << 0),
PV_MTREE_ENTRY_FLAGS_NO_CHANGE = (1 << 1),
PV_MTREE_ENTRY_FLAGS_OPTIONAL = (1 << 2),
PV_MTREE_ENTRY_FLAGS_NONE = 0
} PvMtreeEntryFlags;
typedef struct _PvMtreeEntry PvMtreeEntry; typedef struct _PvMtreeEntry PvMtreeEntry;
struct _PvMtreeEntry struct _PvMtreeEntry
...@@ -59,6 +76,7 @@ struct _PvMtreeEntry ...@@ -59,6 +76,7 @@ struct _PvMtreeEntry
GTimeSpan mtime_usec; GTimeSpan mtime_usec;
int mode; int mode;
PvMtreeEntryKind kind; PvMtreeEntryKind kind;
PvMtreeEntryFlags entry_flags;
}; };
#define PV_MTREE_ENTRY_BLANK \ #define PV_MTREE_ENTRY_BLANK \
......
...@@ -146,6 +146,8 @@ class TestMtreeApply(BaseTest): ...@@ -146,6 +146,8 @@ class TestMtreeApply(BaseTest):
./create type=file size=0 ./create type=file size=0
./make-executable type=file mode=755 ./make-executable type=file mode=755
./make-non-executable type=file mode=644 time=1597415889 ./make-non-executable type=file mode=644 time=1597415889
./do-not-assert type=file optional
./do-not-chmod type=file mode=755 nochange
''' '''
with tempfile.NamedTemporaryFile( with tempfile.NamedTemporaryFile(
...@@ -163,7 +165,11 @@ class TestMtreeApply(BaseTest): ...@@ -163,7 +165,11 @@ class TestMtreeApply(BaseTest):
(Path(expected) / 'sym').mkdir() (Path(expected) / 'sym').mkdir()
(Path(expected) / 'sym' / 'link').symlink_to('/dev/null') (Path(expected) / 'sym' / 'link').symlink_to('/dev/null')
for filename in 'make-executable', 'make-non-executable': for filename in (
'make-executable',
'make-non-executable',
'do-not-chmod',
):
for d in Path(expected), Path(dest): for d in Path(expected), Path(dest):
with open(str(d / filename), 'w') as writer: with open(str(d / filename), 'w') as writer:
writer.write('#!/bin/sh\n') writer.write('#!/bin/sh\n')
...@@ -178,6 +184,8 @@ class TestMtreeApply(BaseTest): ...@@ -178,6 +184,8 @@ class TestMtreeApply(BaseTest):
(Path(expected) / 'create').chmod(0o644) (Path(expected) / 'create').chmod(0o644)
(Path(expected) / 'make-executable').chmod(0o755) (Path(expected) / 'make-executable').chmod(0o755)
(Path(expected) / 'make-non-executable').chmod(0o644) (Path(expected) / 'make-non-executable').chmod(0o644)
(Path(dest) / 'do-not-chmod').chmod(0o644)
(Path(expected) / 'do-not-chmod').chmod(0o644)
os.utime( os.utime(
str(Path(expected) / 'make-non-executable'), str(Path(expected) / 'make-non-executable'),
times=(1597415889, 1597415889), times=(1597415889, 1597415889),
......
...@@ -330,12 +330,13 @@ test_mtree_entry_parse (Fixture *f, ...@@ -330,12 +330,13 @@ test_mtree_entry_parse (Fixture *f,
{ .size = -1, .mtime_usec = -1, .mode = -1, { .size = -1, .mtime_usec = -1, .mode = -1,
.kind = PV_MTREE_ENTRY_KIND_UNKNOWN }, .kind = PV_MTREE_ENTRY_KIND_UNKNOWN },
}, },
{ ". type=dir", { ". type=dir ignore",
".", ".",
{ .size = -1, .mtime_usec = -1, .mode = -1, { .size = -1, .mtime_usec = -1, .mode = -1,
.kind = PV_MTREE_ENTRY_KIND_DIR }, .kind = PV_MTREE_ENTRY_KIND_DIR,
.entry_flags = PV_MTREE_ENTRY_FLAGS_IGNORE_BELOW },
}, },
{ "./foo type=file sha256=ffff mode=0640 size=42 time=1597415889.5", { "./foo type=file sha256=ffff mode=0640 size=42 time=1597415889.500000000",
"./foo", "./foo",
{ .size = 42, { .size = 42,
.mtime_usec = 1597415889 * G_TIME_SPAN_SECOND + (G_TIME_SPAN_SECOND / 2), .mtime_usec = 1597415889 * G_TIME_SPAN_SECOND + (G_TIME_SPAN_SECOND / 2),
...@@ -343,22 +344,24 @@ test_mtree_entry_parse (Fixture *f, ...@@ -343,22 +344,24 @@ test_mtree_entry_parse (Fixture *f,
.kind = PV_MTREE_ENTRY_KIND_FILE }, .kind = PV_MTREE_ENTRY_KIND_FILE },
.sha256 = "ffff", .sha256 = "ffff",
}, },
{ "./foo type=file sha256digest=ffff mode=4755", { "./foo type=file sha256digest=ffff mode=4755 time=1234567890 optional",
"./foo", "./foo",
{ .size = -1, .mtime_usec = -1, .mode = 04755, { .size = -1, .mtime_usec = (1234567890 * G_TIME_SPAN_SECOND),
.kind = PV_MTREE_ENTRY_KIND_FILE }, .mode = 04755, .kind = PV_MTREE_ENTRY_KIND_FILE,
.entry_flags = PV_MTREE_ENTRY_FLAGS_OPTIONAL },
.sha256 = "ffff", .sha256 = "ffff",
}, },
{ "./foo type=file sha256=ffff sha256digest=ffff", { "./foo type=file sha256=ffff sha256digest=ffff time=1234567890.0",
"./foo", "./foo",
{ .size = -1, .mtime_usec = -1, .mode = -1, { .size = -1, .mtime_usec = (1234567890 * G_TIME_SPAN_SECOND), .mode = -1,
.kind = PV_MTREE_ENTRY_KIND_FILE }, .kind = PV_MTREE_ENTRY_KIND_FILE },
.sha256 = "ffff", .sha256 = "ffff",
}, },
{ "./symlink type=link link=/dev/null", { "./symlink type=link link=/dev/null nochange",
"./symlink", "./symlink",
{ .size = -1, .mtime_usec = -1, .mode = -1, { .size = -1, .mtime_usec = -1, .mode = -1,
.kind = PV_MTREE_ENTRY_KIND_LINK }, .kind = PV_MTREE_ENTRY_KIND_LINK,
.entry_flags = PV_MTREE_ENTRY_FLAGS_NO_CHANGE },
.link = "/dev/null", .link = "/dev/null",
}, },
{ "./silly-name/\\001\\123\\n\\r type=link link=\\\"\\\\\\b", { "./silly-name/\\001\\123\\n\\r type=link link=\\\"\\\\\\b",
...@@ -368,8 +371,8 @@ test_mtree_entry_parse (Fixture *f, ...@@ -368,8 +371,8 @@ test_mtree_entry_parse (Fixture *f,
.link = "\"\\\b", .link = "\"\\\b",
}, },
{ ("./ignore cksum=123 device=456 contents=./ignore flags=123 gid=123 " { ("./ignore cksum=123 device=456 contents=./ignore flags=123 gid=123 "
"gname=users ignore=1 inode=123 md5=ffff md5digest=ffff nlink=1 " "gname=users ignore inode=123 md5=ffff md5digest=ffff nlink=1 "
"nochange=1 optional=1 resdevice=123 " "nochange optional resdevice=123 "
"ripemd160digest=ffff rmd160=ffff rmd160digest=ffff " "ripemd160digest=ffff rmd160=ffff rmd160digest=ffff "
"sha1=ffff sha1digest=ffff " "sha1=ffff sha1digest=ffff "
"sha384=ffff sha384digest=ffff " "sha384=ffff sha384digest=ffff "
...@@ -377,7 +380,10 @@ test_mtree_entry_parse (Fixture *f, ...@@ -377,7 +380,10 @@ test_mtree_entry_parse (Fixture *f,
"uid=0 uname=root type=dir"), "uid=0 uname=root type=dir"),
"./ignore", "./ignore",
{ .size = -1, .mtime_usec = -1, .mode = -1, { .size = -1, .mtime_usec = -1, .mode = -1,
.kind = PV_MTREE_ENTRY_KIND_DIR }, .kind = PV_MTREE_ENTRY_KIND_DIR,
.entry_flags = (PV_MTREE_ENTRY_FLAGS_IGNORE_BELOW
| PV_MTREE_ENTRY_FLAGS_NO_CHANGE
| PV_MTREE_ENTRY_FLAGS_OPTIONAL) },
}, },
{ "./foo type=file sha256=ffff sha256digest=eeee", .error = TRUE }, { "./foo type=file sha256=ffff sha256digest=eeee", .error = TRUE },
{ "./foo type=file mode=1a", .error = TRUE }, { "./foo type=file mode=1a", .error = TRUE },
...@@ -392,6 +398,9 @@ test_mtree_entry_parse (Fixture *f, ...@@ -392,6 +398,9 @@ test_mtree_entry_parse (Fixture *f,
{ "./symlink type=file link=/dev/null", .error = TRUE }, { "./symlink type=file link=/dev/null", .error = TRUE },
{ "./symlink type=link", .error = TRUE }, { "./symlink type=link", .error = TRUE },
{ " ", .error = TRUE }, { " ", .error = TRUE },
{ "./not-time type=file type=1a", .error = TRUE },
{ "./not-time type=file type=1.2a", .error = TRUE },
{ "./ambiguous-time type=file type=1.234", .error = TRUE },
}; };
gsize i; gsize i;
...@@ -421,6 +430,7 @@ test_mtree_entry_parse (Fixture *f, ...@@ -421,6 +430,7 @@ test_mtree_entry_parse (Fixture *f,
g_assert_cmpint (got.mtime_usec, ==, expected.mtime_usec); g_assert_cmpint (got.mtime_usec, ==, expected.mtime_usec);
g_assert_cmpint (got.mode, ==, expected.mode); g_assert_cmpint (got.mode, ==, expected.mode);
g_assert_cmpint (got.kind, ==, expected.kind); g_assert_cmpint (got.kind, ==, expected.kind);
g_assert_cmphex (got.entry_flags, ==, expected.entry_flags);
} }
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment