authfs: expose fsverity digest via xattr

Due to the FUSE uapi limitation in the kernel (b/196264590), we can't
simply implement the standard fs-verity ioctls with FUSE. Until it's
possible, implement getxattr as a non-standard API.

Currently, only allow retrieving fsverity digest. In the future, we may
need to get signature, but xattr won't be big enough for the Merkle tree.

Bug: 161471326
Bug: 196635431
Test: retrieve xattr in compsvc
Change-Id: I152284015f1860f19ed60ca632cba141c51b44c4
diff --git a/authfs/src/fsverity/editor.rs b/authfs/src/fsverity/editor.rs
index 86ff4d6..f1e7529 100644
--- a/authfs/src/fsverity/editor.rs
+++ b/authfs/src/fsverity/editor.rs
@@ -88,8 +88,12 @@
         Self { file, merkle_tree: Arc::new(RwLock::new(MerkleLeaves::new())) }
     }
 
+    /// Returns the fs-verity digest size in bytes.
+    pub fn get_fsverity_digest_size(&self) -> usize {
+        Sha256Hasher::HASH_SIZE
+    }
+
     /// Calculates the fs-verity digest of the current file.
-    #[allow(dead_code)]
     pub fn calculate_fsverity_digest(&self) -> io::Result<Sha256Hash> {
         let merkle_tree = self.merkle_tree.read().unwrap();
         merkle_tree.calculate_fsverity_digest().map_err(|e| io::Error::new(io::ErrorKind::Other, e))
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index 1b0e935..6bdb498 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -28,8 +28,8 @@
 use std::time::Duration;
 
 use fuse::filesystem::{
-    Context, DirEntry, DirectoryIterator, Entry, FileSystem, FsOptions, SetattrValid,
-    ZeroCopyReader, ZeroCopyWriter,
+    Context, DirEntry, DirectoryIterator, Entry, FileSystem, FsOptions, GetxattrReply,
+    SetattrValid, ZeroCopyReader, ZeroCopyWriter,
 };
 use fuse::mount::MountOption;
 
@@ -374,6 +374,38 @@
             _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
         }
     }
+
+    fn getxattr(
+        &self,
+        _ctx: Context,
+        inode: Self::Inode,
+        name: &CStr,
+        size: u32,
+    ) -> io::Result<GetxattrReply> {
+        match self.get_file_config(&inode)? {
+            FileConfig::RemoteVerifiedNew { editor } => {
+                // FUSE ioctl is limited, thus we can't implement fs-verity ioctls without a kernel
+                // change (see b/196635431). Until it's possible, use xattr to expose what we need
+                // as an authfs specific API.
+                if name != CStr::from_bytes_with_nul(b"authfs.fsverity.digest\0").unwrap() {
+                    return Err(io::Error::from_raw_os_error(libc::ENODATA));
+                }
+
+                if size == 0 {
+                    // Per protocol, when size is 0, return the value size.
+                    Ok(GetxattrReply::Count(editor.get_fsverity_digest_size() as u32))
+                } else {
+                    let digest = editor.calculate_fsverity_digest()?;
+                    if digest.len() > size as usize {
+                        Err(io::Error::from_raw_os_error(libc::ERANGE))
+                    } else {
+                        Ok(GetxattrReply::Value(digest.to_vec()))
+                    }
+                }
+            }
+            _ => Err(io::Error::from_raw_os_error(libc::ENODATA)),
+        }
+    }
 }
 
 /// Mount and start the FUSE instance. This requires CAP_SYS_ADMIN.