Repository: wcampbell0x2a/backhand (language: rust)

Issue:
Support uid and gid as non-root
The ID table isn't added correctly for non-root uid's and gid's.


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index c4500431..7333a3d3 100644
--- a/README.md
+++ b/README.md
@@ -83,9 +83,13 @@ Arguments:
   <FILE_PATH_IN_IMAGE>  Path of file once inserted into squashfs
 
 Options:
-  -o, --out <OUT>  Squashfs output image [default: added.squashfs]
-  -h, --help       Print help
-  -V, --version    Print version
+  -o, --out <OUT>      Squashfs output image [default: added.squashfs]
+      --mode <MODE>    Overide mode read from `file`
+      --uid <UID>      Overide uid read from `file`
+      --gid <GID>      Overide gid read from `file`
+      --mtime <MTIME>  Overide mtime read from `file`
+  -h, --help           Print help
+  -V, --version        Print version
 ```
 
 ### replace-backhand
diff --git a/src/bin/add.rs b/src/bin/add.rs
index 23615466..9944324c 100644
--- a/src/bin/add.rs
+++ b/src/bin/add.rs
@@ -1,4 +1,5 @@
 use std::fs::File;
+use std::os::unix::fs::MetadataExt;
 use std::path::PathBuf;
 
 use backhand::{FilesystemReader, FilesystemWriter, NodeHeader};
@@ -21,6 +22,22 @@ struct Args {
     /// Squashfs output image
     #[clap(short, long, default_value = "added.squashfs")]
     out: PathBuf,
+
+    /// Overide mode read from `file`
+    #[clap(long)]
+    mode: Option<u16>,
+
+    /// Overide uid read from `file`
+    #[clap(long)]
+    uid: Option<u16>,
+
+    /// Overide gid read from `file`
+    #[clap(long)]
+    gid: Option<u16>,
+
+    /// Overide mtime read from `file`
+    #[clap(long)]
+    mtime: Option<u32>,
 }
 
 fn main() {
@@ -30,12 +47,20 @@ fn main() {
 
     // read of squashfs
     let file = File::open(args.image).unwrap();
+    let meta = file.metadata().unwrap();
+
+    let mode = args.mode.unwrap_or(meta.mode() as u16) & 0xfff;
+    let uid = args.uid.unwrap_or(meta.uid() as u16);
+    let gid = args.gid.unwrap_or(meta.gid() as u16);
+    let mtime = args.mtime.unwrap_or(meta.mtime() as u32);
+    let node = NodeHeader::new(mode, uid, gid, mtime);
+
     let filesystem = FilesystemReader::from_reader(file).unwrap();
     let mut filesystem = FilesystemWriter::from_fs_reader(&filesystem).unwrap();
 
     // create new file
     let new_file = File::open(&args.file).unwrap();
-    filesystem.push_file(new_file, args.file_path, NodeHeader::default());
+    filesystem.push_file(new_file, args.file_path, node);
 
     // write new file
     let mut output = File::create(&args.out).unwrap();
diff --git a/src/filesystem/node.rs b/src/filesystem/node.rs
index c131d0a4..c0be2499 100644
--- a/src/filesystem/node.rs
+++ b/src/filesystem/node.rs
@@ -16,6 +16,17 @@ pub struct NodeHeader {
     pub mtime: u32,
 }
 
+impl NodeHeader {
+    pub fn new(permissions: u16, uid: u16, gid: u16, mtime: u32) -> Self {
+        Self {
+            permissions,
+            uid,
+            gid,
+            mtime,
+        }
+    }
+}
+
 impl From<InodeHeader> for NodeHeader {
     fn from(inode_header: InodeHeader) -> Self {
         Self {
diff --git a/src/filesystem/writer.rs b/src/filesystem/writer.rs
index 59178d67..68f59509 100644
--- a/src/filesystem/writer.rs
+++ b/src/filesystem/writer.rs
@@ -1,6 +1,6 @@
 use std::cell::RefCell;
 use std::io::{Read, Seek, SeekFrom, Write};
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 
 use deku::bitvec::BitVec;
 use deku::DekuWrite;
@@ -90,32 +90,40 @@ impl<'a, R: ReadSeek> FilesystemWriter<'a, R> {
         &mut self,
         reader: impl Read + 'a,
         path: P,
-        header: NodeHeader,
+        mut header: NodeHeader,
     ) {
+        // create uid and replace gid with index
+        header.gid = self.lookup_add_id(header.gid as u32);
+        // create uid and replace uid with index
+        header.uid = self.lookup_add_id(header.uid as u32);
         let path = path.into();
 
         if path.parent().is_some() {
             let mut full_path = PathBuf::new();
             let components: Vec<_> = path.components().map(|comp| comp.as_os_str()).collect();
             'component: for dir in components.iter().take(components.len() - 1) {
+                let dir = Path::new(dir);
                 // add to path
                 full_path.push(dir);
 
-                // check if exists
-                for node in &mut self.nodes {
-                    if let InnerNode::Dir(_) = &node.inner {
-                        let left = &node.path;
-                        let right = &full_path;
-                        if left == right {
-                            continue 'component;
+                // skip root directory
+                if dir != Path::new("/") {
+                    // check if exists
+                    for node in &mut self.nodes {
+                        if let InnerNode::Dir(_) = &node.inner {
+                            let left = &node.path;
+                            let right = &full_path;
+                            if left == right {
+                                continue 'component;
+                            }
                         }
                     }
-                }
 
-                // not found, add to dir
-                let new_dir = InnerNode::Dir(SquashfsDir { header });
-                let node = Node::new(full_path.clone(), new_dir);
-                self.nodes.push(node);
+                    // not found, add to dir
+                    let new_dir = InnerNode::Dir(SquashfsDir { header });
+                    let node = Node::new(full_path.clone(), new_dir);
+                    self.nodes.push(node);
+                }
             }
         }
 
@@ -164,8 +172,12 @@ impl<'a, R: ReadSeek> FilesystemWriter<'a, R> {
         &mut self,
         link: S,
         path: P,
-        header: NodeHeader,
+        mut header: NodeHeader,
     ) {
+        // create uid and replace uid with index
+        header.uid = self.lookup_add_id(header.uid as u32);
+        // create uid and replace gid with index
+        header.gid = self.lookup_add_id(header.gid as u32);
         let path = path.into();
 
         let new_symlink = InnerNode::Symlink(SquashfsSymlink {
@@ -177,7 +189,11 @@ impl<'a, R: ReadSeek> FilesystemWriter<'a, R> {
     }
 
     /// Insert empty `dir` at `path`
-    pub fn push_dir<P: Into<PathBuf>>(&mut self, path: P, header: NodeHeader) {
+    pub fn push_dir<P: Into<PathBuf>>(&mut self, path: P, mut header: NodeHeader) {
+        // create uid and replace uid with index
+        header.uid = self.lookup_add_id(header.uid as u32);
+        // create uid and replace gid with index
+        header.gid = self.lookup_add_id(header.gid as u32);
         let path = path.into();
 
         let new_dir = InnerNode::Dir(SquashfsDir { header });
@@ -190,8 +206,12 @@ impl<'a, R: ReadSeek> FilesystemWriter<'a, R> {
         &mut self,
         device_number: u32,
         path: P,
-        header: NodeHeader,
+        mut header: NodeHeader,
     ) {
+        // create uid and replace uid with index
+        header.uid = self.lookup_add_id(header.uid as u32);
+        // create uid and replace gid with index
+        header.gid = self.lookup_add_id(header.gid as u32);
         let path = path.into();
 
         let new_device = InnerNode::CharacterDevice(SquashfsCharacterDevice {
@@ -207,8 +227,12 @@ impl<'a, R: ReadSeek> FilesystemWriter<'a, R> {
         &mut self,
         device_number: u32,
         path: P,
-        header: NodeHeader,
+        mut header: NodeHeader,
     ) {
+        // create uid and replace uid with index
+        header.uid = self.lookup_add_id(header.uid as u32);
+        // create uid and replace gid with index
+        header.gid = self.lookup_add_id(header.gid as u32);
         let path = path.into();
 
         let new_device = InnerNode::BlockDevice(SquashfsBlockDevice {
@@ -374,6 +398,24 @@ impl<'a, R: ReadSeek> FilesystemWriter<'a, R> {
 
         Ok(())
     }
+
+    /// Return index of id, adding if required
+    fn lookup_add_id(&mut self, id: u32) -> u16 {
+        let found = self
+            .id_table
+            .as_ref()
+            .unwrap()
+            .iter()
+            .position(|a| a.0 == id);
+
+        match found {
+            Some(found) => found as u16,
+            None => {
+                self.id_table.as_mut().unwrap().push(Id(id));
+                self.id_table.as_ref().unwrap().len() as u16 - 1
+            },
+        }
+    }
 }
 
 struct WriterWithOffset<W: WriteSeek> {


Test patch: