[libdm_rust] Enable writable loopdevice
Add option in loopdevice module to create a block device in writable
mode as well. This is required for unit-testing dm-crypt device.
Also, add test: attach_loop_device_with_dio_writable
Test: atest libdm_rust.test
Test: atest apkdmverity.test
Bug: 250880499
Change-Id: I793ca247ec3cb3258d16a6d9bb25aae31c127ede
diff --git a/apkdmverity/src/main.rs b/apkdmverity/src/main.rs
index 23457c4..a69b583 100644
--- a/apkdmverity/src/main.rs
+++ b/apkdmverity/src/main.rs
@@ -93,7 +93,7 @@
bail!("The size of {:?} is not multiple of {}.", &apk, BLOCK_SIZE)
}
(
- loopdevice::attach(&apk, 0, apk_size, /*direct_io*/ true)
+ loopdevice::attach(&apk, 0, apk_size, /*direct_io*/ true, /*writable*/ false)
.context("Failed to attach APK to a loop device")?,
apk_size,
)
@@ -108,8 +108,9 @@
// Due to unknown reason(b/191344832), we can't enable "direct IO" for the IDSIG file (backing
// the hash). For now we don't use "direct IO" but it seems OK since the IDSIG file is very
// small and the benefit of direct-IO would be negliable.
- let hash_device = loopdevice::attach(&idsig, offset, size, /*direct_io*/ false)
- .context("Failed to attach idsig to a loop device")?;
+ let hash_device =
+ loopdevice::attach(&idsig, offset, size, /*direct_io*/ false, /*writable*/ false)
+ .context("Failed to attach idsig to a loop device")?;
// Build a dm-verity target spec from the information from the idsig file. The apk and the
// idsig files are used as the data device and the hash device, respectively.
@@ -324,9 +325,19 @@
// already a block device, `enable_verity` uses the block device as it is. The detatching
// of the data device is done in the scopeguard for the return value of `enable_verity`
// below. Only the idsig_loop_device needs detatching.
- let apk_loop_device = loopdevice::attach(&apk_path, 0, apk_size, true).unwrap();
+ let apk_loop_device = loopdevice::attach(
+ &apk_path, 0, apk_size, /*direct_io*/ true, /*writable*/ false,
+ )
+ .unwrap();
let idsig_loop_device = scopeguard::guard(
- loopdevice::attach(&idsig_path, 0, idsig_size, false).unwrap(),
+ loopdevice::attach(
+ &idsig_path,
+ 0,
+ idsig_size,
+ /*direct_io*/ false,
+ /*writable*/ false,
+ )
+ .unwrap(),
|dev| loopdevice::detach(dev).unwrap(),
);
diff --git a/libs/devicemapper/src/loopdevice.rs b/libs/devicemapper/src/loopdevice.rs
index bdbc0f6..5533e17 100644
--- a/libs/devicemapper/src/loopdevice.rs
+++ b/libs/devicemapper/src/loopdevice.rs
@@ -66,6 +66,7 @@
offset: u64,
size_limit: u64,
direct_io: bool,
+ writable: bool,
) -> Result<PathBuf> {
// Attaching a file to a loop device can make a race condition; a loop device number obtained
// from LOOP_CTL_GET_FREE might have been used by another thread or process. In that case the
@@ -80,7 +81,7 @@
let begin = Instant::now();
loop {
- match try_attach(&path, offset, size_limit, direct_io) {
+ match try_attach(&path, offset, size_limit, direct_io, writable) {
Ok(loop_dev) => return Ok(loop_dev),
Err(e) => {
if begin.elapsed() > TIMEOUT {
@@ -103,6 +104,7 @@
offset: u64,
size_limit: u64,
direct_io: bool,
+ writable: bool,
) -> Result<PathBuf> {
// Get a free loop device
wait_for_path(LOOP_CONTROL)?;
@@ -116,6 +118,7 @@
// Construct the loop_info64 struct
let backing_file = OpenOptions::new()
.read(true)
+ .write(writable)
.custom_flags(if direct_io { O_DIRECT } else { 0 })
.open(&path)
.context(format!("failed to open {:?}", path.as_ref()))?;
@@ -126,7 +129,11 @@
config.block_size = 4096;
config.info.lo_offset = offset;
config.info.lo_sizelimit = size_limit;
- config.info.lo_flags = Flag::LO_FLAGS_READ_ONLY;
+
+ if !writable {
+ config.info.lo_flags = Flag::LO_FLAGS_READ_ONLY;
+ }
+
if direct_io {
config.info.lo_flags.insert(Flag::LO_FLAGS_DIRECT_IO);
}
@@ -168,13 +175,19 @@
"1" == fs::read_to_string(&dio).unwrap().trim()
}
+ // kernel exposes /sys/block/loop*/ro which gives the read-only value
+ fn is_direct_io_writable(dev: &Path) -> bool {
+ let ro = Path::new("/sys/block").join(dev.file_name().unwrap()).join("ro");
+ "0" == fs::read_to_string(&ro).unwrap().trim()
+ }
+
#[test]
fn attach_loop_device_with_dio() {
let a_dir = tempfile::TempDir::new().unwrap();
let a_file = a_dir.path().join("test");
let a_size = 4096u64;
create_empty_file(&a_file, a_size);
- let dev = attach(a_file, 0, a_size, /*direct_io*/ true).unwrap();
+ let dev = attach(a_file, 0, a_size, /*direct_io*/ true, /*writable*/ false).unwrap();
scopeguard::defer! {
detach(&dev).unwrap();
}
@@ -187,10 +200,24 @@
let a_file = a_dir.path().join("test");
let a_size = 4096u64;
create_empty_file(&a_file, a_size);
- let dev = attach(a_file, 0, a_size, /*direct_io*/ false).unwrap();
+ let dev = attach(a_file, 0, a_size, /*direct_io*/ false, /*writable*/ false).unwrap();
scopeguard::defer! {
detach(&dev).unwrap();
}
assert!(!is_direct_io(&dev));
}
+
+ #[test]
+ fn attach_loop_device_with_dio_writable() {
+ let a_dir = tempfile::TempDir::new().unwrap();
+ let a_file = a_dir.path().join("test");
+ let a_size = 4096u64;
+ create_empty_file(&a_file, a_size);
+ let dev = attach(a_file, 0, a_size, /*direct_io*/ true, /*writable*/ true).unwrap();
+ scopeguard::defer! {
+ detach(&dev).unwrap();
+ }
+ assert!(is_direct_io(&dev));
+ assert!(is_direct_io_writable(&dev));
+ }
}