summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-06-28 20:57:15 +0200
committerLennart Poettering <lennart@poettering.net>2018-11-29 20:02:39 +0100
commitfb2430c6e56e31c1b4220719640fc544e289214b (patch)
treeeaa692de8a44fa29e65378575595d7c043f373be
parent192b89368e58e2b9eabdeee61e415aadcbbc84ff (diff)
downloadsystemd-fb2430c6e56e31c1b4220719640fc544e289214b.tar.gz
stat-util: add macros for checking whether major and minor values are in range
As it turns out glibc and the Linux kernel have different ideas about the size of dev_t and how many bits exist for the major and the minor. When validating major/minor numbers we should check against the kernel's actual sizes, hence add macros for this.
-rw-r--r--src/basic/stat-util.h19
-rw-r--r--src/test/test-stat-util.c36
2 files changed, 55 insertions, 0 deletions
diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h
index 84400a6083..fe4a4bb717 100644
--- a/src/basic/stat-util.h
+++ b/src/basic/stat-util.h
@@ -62,3 +62,22 @@ int fd_verify_regular(int fd);
int stat_verify_directory(const struct stat *st);
int fd_verify_directory(int fd);
+
+/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
+ * specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
+ * major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
+ * comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
+ * such a test would be pointless in such a case.) */
+
+#define DEVICE_MAJOR_VALID(x) \
+ ({ \
+ typeof(x) _x = (x), _y = 0; \
+ _x >= _y && _x < (UINT32_C(1) << 12); \
+ \
+ })
+
+#define DEVICE_MINOR_VALID(x) \
+ ({ \
+ typeof(x) _x = (x), _y = 0; \
+ _x >= _y && _x < (UINT32_C(1) << 20); \
+ })
diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c
index 2b0564d8a0..713fbc9a08 100644
--- a/src/test/test-stat-util.c
+++ b/src/test/test-stat-util.c
@@ -81,12 +81,48 @@ static void test_fd_is_network_ns(void) {
assert_se(IN_SET(fd_is_network_ns(fd), 1, -EUCLEAN));
}
+static void test_device_major_minor_valid(void) {
+ /* on glibc dev_t is 64bit, even though in the kernel it is only 32bit */
+ assert_cc(sizeof(dev_t) == sizeof(uint64_t));
+
+ assert_se(DEVICE_MAJOR_VALID(0U));
+ assert_se(DEVICE_MINOR_VALID(0U));
+
+ assert_se(DEVICE_MAJOR_VALID(1U));
+ assert_se(DEVICE_MINOR_VALID(1U));
+
+ assert_se(!DEVICE_MAJOR_VALID(-1U));
+ assert_se(!DEVICE_MINOR_VALID(-1U));
+
+ assert_se(DEVICE_MAJOR_VALID(1U << 10));
+ assert_se(DEVICE_MINOR_VALID(1U << 10));
+
+ assert_se(DEVICE_MAJOR_VALID((1U << 12) - 1));
+ assert_se(DEVICE_MINOR_VALID((1U << 20) - 1));
+
+ assert_se(!DEVICE_MAJOR_VALID((1U << 12)));
+ assert_se(!DEVICE_MINOR_VALID((1U << 20)));
+
+ assert_se(!DEVICE_MAJOR_VALID(1U << 25));
+ assert_se(!DEVICE_MINOR_VALID(1U << 25));
+
+ assert_se(!DEVICE_MAJOR_VALID(UINT32_MAX));
+ assert_se(!DEVICE_MINOR_VALID(UINT32_MAX));
+
+ assert_se(!DEVICE_MAJOR_VALID(UINT64_MAX));
+ assert_se(!DEVICE_MINOR_VALID(UINT64_MAX));
+
+ assert_se(DEVICE_MAJOR_VALID(major(0)));
+ assert_se(DEVICE_MINOR_VALID(minor(0)));
+}
+
int main(int argc, char *argv[]) {
test_files_same();
test_is_symlink();
test_path_is_fs_type();
test_path_is_temporary_fs();
test_fd_is_network_ns();
+ test_device_major_minor_valid();
return 0;
}