From c8b7972593d74dfb63cfda7513d19ba570c8de8b Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 11 Sep 2024 13:39:26 -0400 Subject: [PATCH] builder: Add a set_rdev() convenience Commonly one may be building tar archives from the filesystem in a custom way, or operating on a translation of a non-tar format which uses rdev, not split major/minor. Today one needs to replicate the nontrivial bit manipulations to split the device; but since we have that code anyways in our own "build from filesystem" code, let's just add it as a public API. Signed-off-by: Colin Walters --- src/builder.rs | 6 +----- src/header.rs | 11 +++++++++++ tests/header/mod.rs | 12 ++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 9b799329..0e919546 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -535,11 +535,7 @@ fn append_special( prepare_header_path(dst, &mut header, path)?; header.set_entry_type(entry_type); - let dev_id = stat.rdev(); - let dev_major = ((dev_id >> 32) & 0xffff_f000) | ((dev_id >> 8) & 0x0000_0fff); - let dev_minor = ((dev_id >> 12) & 0xffff_ff00) | ((dev_id) & 0x0000_00ff); - header.set_device_major(dev_major as u32)?; - header.set_device_minor(dev_minor as u32)?; + header.set_rdev(stat.rdev())?; header.set_cksum(); dst.write_all(header.as_bytes())?; diff --git a/src/header.rs b/src/header.rs index 36fb52ae..0fce3211 100644 --- a/src/header.rs +++ b/src/header.rs @@ -645,6 +645,17 @@ impl Header { } } + /// Encodes the device number into the major and minor fields of this header. + /// + /// This function will return an error if this header format cannot encode a + /// a device number. + pub fn set_rdev(&mut self, rdev: libc::dev_t) -> io::Result<()> { + let dev_major = ((rdev >> 32) & 0xffff_f000) | ((rdev >> 8) & 0x0000_0fff); + self.set_device_major(dev_major as u32)?; + let dev_minor = ((rdev >> 12) & 0xffff_ff00) | ((rdev) & 0x0000_00ff); + self.set_device_minor(dev_minor as u32) + } + /// Returns the device minor number, if present. /// /// This field may not be present in all archives, and it may not be diff --git a/tests/header/mod.rs b/tests/header/mod.rs index 9008cb55..3bdaacb4 100644 --- a/tests/header/mod.rs +++ b/tests/header/mod.rs @@ -100,6 +100,18 @@ fn dev_major_minor() { assert_eq!(t!(h.device_major()), Some(1)); assert_eq!(t!(h.device_minor()), Some(2)); + // Verify this is idempotent + let onetwo_rdev = libc::makedev(1, 2); + t!(h.set_rdev(onetwo_rdev)); + assert_eq!(t!(h.device_major()), Some(1)); + assert_eq!(t!(h.device_minor()), Some(2)); + + // And verify that changing the device works + let nvme_rdev = libc::makedev(0x88, 0x6); + t!(h.set_rdev(nvme_rdev)); + assert_eq!(t!(h.device_major()), Some(0x88)); + assert_eq!(t!(h.device_minor()), Some(0x6)); + h = Header::new_ustar(); t!(h.set_device_major(1)); t!(h.set_device_minor(2));