]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
devnum-util: add macros to safely convert dev_t to pointers and back
authorLennart Poettering <lennart@poettering.net>
Thu, 23 Jan 2025 10:40:36 +0000 (11:40 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 23 Jan 2025 21:16:24 +0000 (22:16 +0100)
Sometimes it's nice being able to store dev_t as pointer values in
hashmaps/tables, instead of having to allocate memory for them and using
devt_hash_ops. After all dev_t is weird on Linux/glibc: glibc defines it
as 64bit entity (which hence appears as something we cannot encode in a
pointer value for compat with 32bit archs) but it actually is 32bit in
the kernel apis. Hence we can safely cut off the upper 32bit, and still
retain compat with all archs.

But let's hide this in new macros, and validate this is all correct via
a test.

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

index e109de991360b28676b55a060d5718873c1328cc..0efca567809a5f632b219d066a52682d316acf14 100644 (file)
@@ -9,6 +9,9 @@
 
 int parse_devnum(const char *s, dev_t *ret);
 
+#define DEVNUM_MAJOR_MAX ((UINT32_C(1) << 12) - 1U)
+#define DEVNUM_MINOR_MAX ((UINT32_C(1) << 20) - 1U)
+
 /* 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
@@ -18,14 +21,14 @@ int parse_devnum(const char *s, dev_t *ret);
 #define DEVICE_MAJOR_VALID(x)                                           \
         ({                                                              \
                 typeof(x) _x = (x), _y = 0;                             \
-                _x >= _y && _x < (UINT32_C(1) << 12);                   \
+                _x >= _y && _x <= DEVNUM_MAJOR_MAX;                     \
                                                                         \
         })
 
 #define DEVICE_MINOR_VALID(x)                                           \
         ({                                                              \
                 typeof(x) _x = (x), _y = 0;                             \
-                _x >= _y && _x < (UINT32_C(1) << 20);                   \
+                _x >= _y && _x <= DEVNUM_MINOR_MAX;                     \
         })
 
 int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret);
@@ -54,3 +57,6 @@ static inline char *format_devnum(dev_t d, char buf[static DEVNUM_STR_MAX]) {
 static inline bool devnum_is_zero(dev_t d) {
         return major(d) == 0 && minor(d) == 0;
 }
+
+#define DEVNUM_TO_PTR(u) ((void*) (uintptr_t) (u))
+#define PTR_TO_DEVNUM(p) ((dev_t) ((uintptr_t) (p)))
index ebef794001b82e01062175a390b0c10e18971cc5..782f15d86ffcabd85f6b3b7892eadbbadd38a353 100644 (file)
@@ -121,4 +121,21 @@ TEST(devnum_format_str) {
         test_devnum_format_str_one(makedev(4095, 1048575), "4095:1048575");
 }
 
+TEST(devnum_to_ptr) {
+        dev_t m = makedev(0, 0);
+        ASSERT_EQ(major(m), 0U);
+        ASSERT_EQ(minor(m), 0U);
+        ASSERT_EQ(m, PTR_TO_DEVNUM(DEVNUM_TO_PTR(m)));
+
+        m = makedev(DEVNUM_MAJOR_MAX, DEVNUM_MINOR_MAX);
+        ASSERT_EQ(major(m), DEVNUM_MAJOR_MAX);
+        ASSERT_EQ(minor(m), DEVNUM_MINOR_MAX);
+        ASSERT_EQ(m, PTR_TO_DEVNUM(DEVNUM_TO_PTR(m)));
+
+        m = makedev(5, 8);
+        ASSERT_EQ(major(m), 5U);
+        ASSERT_EQ(minor(m), 8U);
+        ASSERT_EQ(m, PTR_TO_DEVNUM(DEVNUM_TO_PTR(m)));
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);