json-util: add a generic parser for parsing access mode/umask
Let's move this from user-record.c into generic code and let's beef it up a bit: allow parsing octal strings. This is particular relevant given json_variant_new_stat() generates the mode in that format, and we probably should be able to parse our own fields (even though we currently don't do that for the data from json_variant_new_stat()).
This commit is contained in:
@@ -701,3 +701,45 @@ int json_variant_new_fd_info(sd_json_variant **ret, int fd) {
|
||||
JSON_BUILD_PAIR_INTEGER_NON_NEGATIVE("mountId", mntid),
|
||||
SD_JSON_BUILD_PAIR_VARIANT("fileHandle", w));
|
||||
}
|
||||
|
||||
int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
mode_t *m = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
if (sd_json_variant_is_null(variant)) {
|
||||
*m = MODE_INVALID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Let the SD_JSON_STRICT determine if we allow suid/sgid/sticky or not */
|
||||
mode_t limit = FLAGS_SET(flags, SD_JSON_STRICT) ? 0777 : 07777;
|
||||
|
||||
if (sd_json_variant_is_string(variant)) {
|
||||
/* NB: we parse the mode in the usual octal if a string is specified. */
|
||||
|
||||
mode_t mode;
|
||||
r = parse_mode(sd_json_variant_string(variant), &mode);
|
||||
if (r < 0)
|
||||
return json_log(variant, flags, r, "JSON field '%s' is not a valid access mode string.", strna(name));
|
||||
|
||||
if (mode > limit)
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
|
||||
"JSON field '%s' outside of valid range 0%s0%o.",
|
||||
strna(name), glyph(GLYPH_ELLIPSIS), limit);
|
||||
|
||||
*m = mode;
|
||||
|
||||
} else if (sd_json_variant_is_unsigned(variant)) {
|
||||
|
||||
uint64_t k = sd_json_variant_unsigned(variant);
|
||||
if (k > (uint64_t) limit)
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
|
||||
"JSON field '%s' outside of valid range 0%s0%o.",
|
||||
strna(name), glyph(GLYPH_ELLIPSIS), limit);
|
||||
|
||||
*m = (mode_t) k;
|
||||
} else
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is neither a number nor a string.", strna(name));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -127,6 +127,7 @@ int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dis
|
||||
int json_dispatch_ifindex(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_log_level(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_strv_environment(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
|
||||
static inline int json_variant_unbase64_iovec(sd_json_variant *v, struct iovec *ret) {
|
||||
return sd_json_variant_unbase64(v, ret ? &ret->iov_base : NULL, ret ? &ret->iov_len : NULL);
|
||||
|
||||
@@ -462,50 +462,6 @@ static int json_dispatch_image_path(const char *name, sd_json_variant *variant,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_dispatch_umask(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
mode_t *m = userdata;
|
||||
uint64_t k;
|
||||
|
||||
if (sd_json_variant_is_null(variant)) {
|
||||
*m = MODE_INVALID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!sd_json_variant_is_unsigned(variant))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name));
|
||||
|
||||
k = sd_json_variant_unsigned(variant);
|
||||
if (k > 0777)
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
|
||||
"JSON field '%s' outside of valid range 0%s0777.",
|
||||
strna(name), glyph(GLYPH_ELLIPSIS));
|
||||
|
||||
*m = (mode_t) k;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
mode_t *m = userdata;
|
||||
uint64_t k;
|
||||
|
||||
if (sd_json_variant_is_null(variant)) {
|
||||
*m = MODE_INVALID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!sd_json_variant_is_unsigned(variant))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a number.", strna(name));
|
||||
|
||||
k = sd_json_variant_unsigned(variant);
|
||||
if (k > 07777)
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
|
||||
"JSON field '%s' outside of valid range 0%s07777.",
|
||||
strna(name), glyph(GLYPH_ELLIPSIS));
|
||||
|
||||
*m = (mode_t) k;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_dispatch_locale(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
char **s = userdata;
|
||||
const char *n;
|
||||
@@ -1273,7 +1229,7 @@ static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_j
|
||||
{ "iconName", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, icon_name), SD_JSON_STRICT },
|
||||
{ "location", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, location), 0 },
|
||||
{ "shell", SD_JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 },
|
||||
{ "umask", SD_JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 },
|
||||
{ "umask", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, umask), SD_JSON_STRICT },
|
||||
{ "environment", SD_JSON_VARIANT_ARRAY, json_dispatch_strv_environment, offsetof(UserRecord, environment), 0 },
|
||||
{ "timeZone", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, time_zone), SD_JSON_STRICT },
|
||||
{ "preferredLanguage", SD_JSON_VARIANT_STRING, json_dispatch_locale, offsetof(UserRecord, preferred_language), 0 },
|
||||
@@ -1287,7 +1243,7 @@ static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_j
|
||||
{ "diskSize", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, disk_size), 0 },
|
||||
{ "diskSizeRelative", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 },
|
||||
{ "skeletonDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), SD_JSON_STRICT },
|
||||
{ "accessMode", SD_JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
|
||||
{ "accessMode", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
|
||||
{ "tasksMax", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 },
|
||||
{ "memoryHigh", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 },
|
||||
{ "memoryMax", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 },
|
||||
@@ -1397,7 +1353,7 @@ static int dispatch_status(const char *name, sd_json_variant *variant, sd_json_d
|
||||
{ "rateLimitBeginUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, ratelimit_begin_usec), 0 },
|
||||
{ "rateLimitCount", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, ratelimit_count), 0 },
|
||||
{ "removable", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(UserRecord, removable), 0 },
|
||||
{ "accessMode", SD_JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
|
||||
{ "accessMode", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
|
||||
{ "fileSystemType", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, file_system_type), SD_JSON_STRICT },
|
||||
{ "fallbackShell", SD_JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, fallback_shell), 0 },
|
||||
{ "fallbackHomeDirectory", SD_JSON_VARIANT_STRING, json_dispatch_home_directory, offsetof(UserRecord, fallback_home_directory), 0 },
|
||||
@@ -1633,7 +1589,7 @@ int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load
|
||||
{ "lastChangeUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, last_change_usec), 0 },
|
||||
{ "lastPasswordChangeUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, last_password_change_usec), 0 },
|
||||
{ "shell", SD_JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 },
|
||||
{ "umask", SD_JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 },
|
||||
{ "umask", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, umask), SD_JSON_STRICT },
|
||||
{ "environment", SD_JSON_VARIANT_ARRAY, json_dispatch_strv_environment, offsetof(UserRecord, environment), 0 },
|
||||
{ "timeZone", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, time_zone), SD_JSON_STRICT },
|
||||
{ "preferredLanguage", SD_JSON_VARIANT_STRING, json_dispatch_locale, offsetof(UserRecord, preferred_language), 0 },
|
||||
@@ -1647,7 +1603,7 @@ int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load
|
||||
{ "diskSize", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, disk_size), 0 },
|
||||
{ "diskSizeRelative", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, disk_size_relative), 0 },
|
||||
{ "skeletonDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(UserRecord, skeleton_directory), SD_JSON_STRICT },
|
||||
{ "accessMode", SD_JSON_VARIANT_UNSIGNED, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
|
||||
{ "accessMode", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, offsetof(UserRecord, access_mode), 0 },
|
||||
{ "tasksMax", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, tasks_max), 0 },
|
||||
{ "memoryHigh", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_high), 0 },
|
||||
{ "memoryMax", SD_JSON_VARIANT_UNSIGNED, json_dispatch_tasks_or_memory_max, offsetof(UserRecord, memory_max), 0 },
|
||||
|
||||
@@ -1477,4 +1477,50 @@ TEST(unit_name) {
|
||||
&data), EINVAL);
|
||||
}
|
||||
|
||||
TEST(access_mode) {
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
|
||||
ASSERT_OK(sd_json_parse("{"
|
||||
" \"a\" : \"0755\", "
|
||||
" \"b\" : 448, "
|
||||
" \"c\" : null, "
|
||||
" \"d\" : \"01755\" "
|
||||
"}",
|
||||
/* flags= */ 0,
|
||||
&v,
|
||||
/* reterr_line= */ NULL,
|
||||
/* reterr_column= */ NULL));
|
||||
|
||||
struct {
|
||||
mode_t a, b, c, d;
|
||||
} mm = { 1, 2, 3, 4 };
|
||||
|
||||
ASSERT_OK(sd_json_dispatch(
|
||||
v,
|
||||
(const sd_json_dispatch_field[]) {
|
||||
{ "a", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, a), 0 },
|
||||
{ "b", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, b), 0 },
|
||||
{ "c", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, c), 0 },
|
||||
{ "d", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, d), 0 },
|
||||
{},
|
||||
},
|
||||
/* flags= */ 0,
|
||||
&mm));
|
||||
|
||||
ASSERT_EQ(mm.a, (mode_t) 0755);
|
||||
ASSERT_EQ(mm.b, (mode_t) 0700);
|
||||
ASSERT_EQ(mm.c, MODE_INVALID);
|
||||
ASSERT_EQ(mm.d, (mode_t) 01755);
|
||||
|
||||
/* retry with SD_JSON_STRICT, where 'd' should not parse anymore */
|
||||
ASSERT_ERROR(sd_json_dispatch(
|
||||
v,
|
||||
(const sd_json_dispatch_field[]) {
|
||||
{ "d", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_access_mode, voffsetof(mm, d), SD_JSON_STRICT },
|
||||
{},
|
||||
},
|
||||
/* flags= */ SD_JSON_ALLOW_EXTENSIONS,
|
||||
&mm), ERANGE);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
||||
Reference in New Issue
Block a user