From 946efa2fb5e39669a7fcfc4faf33ed01e92af1ee Mon Sep 17 00:00:00 2001
From: Jeremy Soller <jeremy@system76.com>
Date: Wed, 8 May 2019 21:07:21 -0600
Subject: [PATCH] Add sparse disk file to not require image to be sized
 initially.

---
 src/bin/ar.rs      |  9 +++++----
 src/disk/mod.rs    |  2 ++
 src/disk/sparse.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++
 src/lib.rs         |  2 +-
 4 files changed, 56 insertions(+), 5 deletions(-)
 create mode 100644 src/disk/sparse.rs

diff --git a/src/bin/ar.rs b/src/bin/ar.rs
index 037958a..6332e91 100644
--- a/src/bin/ar.rs
+++ b/src/bin/ar.rs
@@ -7,7 +7,7 @@ use std::io::{self, Read};
 use std::path::Path;
 use std::time::{Duration, SystemTime, UNIX_EPOCH};
 
-use redoxfs::{BLOCK_SIZE, Disk, DiskFile, Extent, FileSystem, Node};
+use redoxfs::{BLOCK_SIZE, Disk, DiskSparse, Extent, FileSystem, Node};
 use uuid::Uuid;
 
 fn syscall_err(err: syscall::Error) -> io::Error {
@@ -72,14 +72,15 @@ fn archive<D: Disk, P: AsRef<Path>>(fs: &mut FileSystem<D>, parent_path: P, ctim
     let free_block = fs.header.1.free;
     let mut free = fs.node(free_block).map_err(syscall_err)?;
     let end_block = free.1.extents[0].block;
+    let end_size = end_block * BLOCK_SIZE;
     free.1.extents[0] = Extent::default();
     fs.write_at(free.0, &free.1).map_err(syscall_err)?;
 
-    fs.header.1.size = end_block;
+    fs.header.1.size = end_size;
     let header = fs.header;
     fs.write_at(header.0, &header.1).map_err(syscall_err)?;
 
-    Ok(end_block * BLOCK_SIZE)
+    Ok(end_size)
 }
 
 fn main() {
@@ -103,7 +104,7 @@ fn main() {
 
     let bootloader_path_opt = args.next();
 
-    let disk = match DiskFile::open(&disk_path) {
+    let disk = match DiskSparse::create(&disk_path) {
         Ok(disk) => disk,
         Err(err) => {
             println!("redoxfs-ar: failed to open image {}: {}", disk_path, err);
diff --git a/src/disk/mod.rs b/src/disk/mod.rs
index 941fa6f..73b5db3 100644
--- a/src/disk/mod.rs
+++ b/src/disk/mod.rs
@@ -2,9 +2,11 @@ use syscall::error::Result;
 
 pub use self::cache::DiskCache;
 pub use self::file::DiskFile;
+pub use self::sparse::DiskSparse;
 
 mod cache;
 mod file;
+mod sparse;
 
 /// A disk
 pub trait Disk {
diff --git a/src/disk/sparse.rs b/src/disk/sparse.rs
new file mode 100644
index 0000000..dd7d799
--- /dev/null
+++ b/src/disk/sparse.rs
@@ -0,0 +1,48 @@
+use std::fs::{File, OpenOptions};
+use std::io::{Read, Write, Seek, SeekFrom};
+use std::u64;
+use syscall::error::{Error, Result, EIO};
+
+use BLOCK_SIZE;
+use disk::Disk;
+
+macro_rules! try_disk {
+    ($expr:expr) => (match $expr {
+        Ok(val) => val,
+        Err(err) => {
+            eprintln!("Disk I/O Error: {}", err);
+            return Err(Error::new(EIO));
+        }
+    })
+}
+
+pub struct DiskSparse {
+    pub file: File,
+}
+
+impl DiskSparse {
+    pub fn create(path: &str) -> Result<DiskSparse> {
+        let file = try_disk!(OpenOptions::new().read(true).write(true).create(true).open(path));
+        Ok(DiskSparse {
+            file
+        })
+    }
+}
+
+impl Disk for DiskSparse {
+    fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
+        try_disk!(self.file.seek(SeekFrom::Start(block * BLOCK_SIZE)));
+        let count = try_disk!(self.file.read(buffer));
+        Ok(count)
+    }
+
+    fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
+        try_disk!(self.file.seek(SeekFrom::Start(block * BLOCK_SIZE)));
+        let count = try_disk!(self.file.write(buffer));
+        Ok(count)
+    }
+
+    fn size(&mut self) -> Result<u64> {
+        Ok(u64::MAX)
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 2e51ab1..a6bc814 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,7 +11,7 @@ pub const SIGNATURE: &'static [u8; 8] = b"RedoxFS\0";
 pub const VERSION: u64 = 3;
 pub static IS_UMT: AtomicUsize = AtomicUsize::new(0);
 
-pub use self::disk::{Disk, DiskCache, DiskFile};
+pub use self::disk::{Disk, DiskCache, DiskFile, DiskSparse};
 pub use self::ex_node::ExNode;
 pub use self::extent::Extent;
 pub use self::filesystem::FileSystem;
-- 
GitLab