disk: add try_clone method to DiskFile trait

Introduce `try_clone` method to `DiskFile` trait. This allows callers to
create DiskFile instances which share the same underlying file
descriptors, which enables you to manipulate them in multiple threads in
parallel. virtio-blk requires this change to run multiple worker threads
in parallel.

Note `try_clone` is introduced as a method of `DiskFile`, not as a
standalone `TryClone`, since such `TryClone` trait is not an object-safe
and thus cannot be defined actually.

BUG=b:267716786
TEST=build passes

Change-Id: I8aae4ee08992de2649b7157b8dfe7328751208b5
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4281742
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-by: Keiichi Watanabe <keiichiw@chromium.org>
Commit-Queue: Takaya Saeki <takayas@chromium.org>
diff --git a/disk/src/android_sparse.rs b/disk/src/android_sparse.rs
index 5511b97..9e00612 100644
--- a/disk/src/android_sparse.rs
+++ b/disk/src/android_sparse.rs
@@ -308,6 +308,9 @@
     }
 }
 
+// TODO(b/271381851): implement `try_clone`. It allows virtio-blk to run multiple workers.
+impl DiskFile for AndroidSparse {}
+
 /// An Android Sparse disk that implements `AsyncDisk` for access.
 pub struct AsyncAndroidSparse {
     inner: Box<dyn IoSourceExt<File>>,
diff --git a/disk/src/composite.rs b/disk/src/composite.rs
index a70cafc..704d558 100644
--- a/disk/src/composite.rs
+++ b/disk/src/composite.rs
@@ -142,6 +142,9 @@
     component_disks: Vec<ComponentDiskPart>,
 }
 
+// TODO(b/271381851): implement `try_clone`. It allows virtio-blk to run multiple workers.
+impl DiskFile for CompositeDiskFile {}
+
 fn ranges_overlap(a: &Range<u64>, b: &Range<u64>) -> bool {
     range_intersection(a, b).is_some()
 }
diff --git a/disk/src/disk.rs b/disk/src/disk.rs
index 481ddfe..249a55c 100644
--- a/disk/src/disk.rs
+++ b/disk/src/disk.rs
@@ -163,17 +163,18 @@
 pub trait DiskFile:
     FileSetLen + DiskGetLen + FileReadWriteAtVolatile + ToAsyncDisk + Send + AsRawDescriptors + Debug
 {
-}
-impl<
-        D: FileSetLen
-            + DiskGetLen
-            + FileReadWriteAtVolatile
-            + ToAsyncDisk
-            + Send
-            + AsRawDescriptors
-            + Debug,
-    > DiskFile for D
-{
+    /// Creates a new DiskFile instance that shares the same underlying disk file image. IO
+    /// operations to a DiskFile should affect all DiskFile instances with the same underlying disk
+    /// file image.
+    ///
+    /// `try_clone()` returns [`io::ErrorKind::Unsupported`] Error if a DiskFile does not support
+    /// creating an instance with the same underlying disk file image.
+    fn try_clone(&self) -> io::Result<Box<dyn DiskFile>> {
+        Err(io::Error::new(
+            io::ErrorKind::Unsupported,
+            "unsupported operation",
+        ))
+    }
 }
 
 /// A `DiskFile` that can be converted for asychronous access.
@@ -260,6 +261,12 @@
     Ok(ImageType::Raw)
 }
 
+impl DiskFile for File {
+    fn try_clone(&self) -> io::Result<Box<dyn DiskFile>> {
+        Ok(Box::new(self.try_clone()?))
+    }
+}
+
 /// Inspect the image file type and create an appropriate disk file to match it.
 pub fn create_disk_file(
     raw_image: File,
diff --git a/disk/src/qcow/mod.rs b/disk/src/qcow/mod.rs
index 7b8d763..db09f30 100644
--- a/disk/src/qcow/mod.rs
+++ b/disk/src/qcow/mod.rs
@@ -426,6 +426,8 @@
     backing_file: Option<Box<dyn DiskFile>>,
 }
 
+impl DiskFile for QcowFile {}
+
 impl QcowFile {
     /// Creates a QcowFile from `file`. File must be a valid qcow2 image.
     pub fn from(mut file: File, max_nesting_depth: u32) -> Result<QcowFile> {