]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
stat-util: add macros for checking whether major and minor values are in range
authorLennart Poettering <lennart@poettering.net>
Thu, 28 Jun 2018 18:57:15 +0000 (20:57 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 29 Nov 2018 19:02:39 +0000 (20:02 +0100)
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.

src/basic/stat-util.h
src/test/test-stat-util.c

index 84400a6083bd6e0e1532899671a1e569fa957d4f..fe4a4bb717beb88f94aac14677dc4fd854e43e50 100644 (file)
@@ -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);                   \
+        })
index 2b0564d8a05b0d81a7adf4a4c5c898d81fa52b1e..713fbc9a084fbc520fa27f275a65d776f9da5068 100644 (file)
@@ -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;
 }