Merge "Create SDK of CompOS module"
diff --git a/authfs/fd_server/src/aidl.rs b/authfs/fd_server/src/aidl.rs
new file mode 100644
index 0000000..ed3a0ea
--- /dev/null
+++ b/authfs/fd_server/src/aidl.rs
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use anyhow::Result;
+use log::error;
+use std::cmp::min;
+use std::collections::BTreeMap;
+use std::convert::TryInto;
+use std::fs::File;
+use std::io;
+use std::os::unix::fs::FileExt;
+use std::os::unix::io::AsRawFd;
+
+use crate::fsverity;
+use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::{
+ BnVirtFdService, IVirtFdService, ERROR_FILE_TOO_LARGE, ERROR_IO, ERROR_UNKNOWN_FD,
+ MAX_REQUESTING_DATA,
+};
+use authfs_aidl_interface::binder::{
+ BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, StatusCode, Strong,
+};
+use binder_common::new_binder_exception;
+
+fn validate_and_cast_offset(offset: i64) -> Result<u64, Status> {
+ offset.try_into().map_err(|_| {
+ new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, format!("Invalid offset: {}", offset))
+ })
+}
+
+fn validate_and_cast_size(size: i32) -> Result<usize, Status> {
+ if size > MAX_REQUESTING_DATA {
+ Err(new_binder_exception(
+ ExceptionCode::ILLEGAL_ARGUMENT,
+ format!("Unexpectedly large size: {}", size),
+ ))
+ } else {
+ size.try_into().map_err(|_| {
+ new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, format!("Invalid size: {}", size))
+ })
+ }
+}
+
+/// Configuration of a file descriptor to be served/exposed/shared.
+pub enum FdConfig {
+ /// A read-only file to serve by this server. The file is supposed to be verifiable with the
+ /// associated fs-verity metadata.
+ Readonly {
+ /// The file to read from. fs-verity metadata can be retrieved from this file's FD.
+ file: File,
+
+ /// Alternative Merkle tree stored in another file.
+ alt_merkle_tree: Option<File>,
+
+ /// Alternative signature stored in another file.
+ alt_signature: Option<File>,
+ },
+
+ /// A readable/writable file to serve by this server. This backing file should just be a
+ /// regular file and does not have any specific property.
+ ReadWrite(File),
+}
+
+pub struct FdService {
+ /// A pool of opened files, may be readonly or read-writable.
+ fd_pool: BTreeMap<i32, FdConfig>,
+}
+
+impl FdService {
+ pub fn new_binder(fd_pool: BTreeMap<i32, FdConfig>) -> Strong<dyn IVirtFdService> {
+ BnVirtFdService::new_binder(FdService { fd_pool }, BinderFeatures::default())
+ }
+
+ fn get_file_config(&self, id: i32) -> BinderResult<&FdConfig> {
+ self.fd_pool.get(&id).ok_or_else(|| Status::from(ERROR_UNKNOWN_FD))
+ }
+}
+
+impl Interface for FdService {}
+
+impl IVirtFdService for FdService {
+ fn readFile(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
+ let size: usize = validate_and_cast_size(size)?;
+ let offset: u64 = validate_and_cast_offset(offset)?;
+
+ match self.get_file_config(id)? {
+ FdConfig::Readonly { file, .. } | FdConfig::ReadWrite(file) => {
+ read_into_buf(file, size, offset).map_err(|e| {
+ error!("readFile: read error: {}", e);
+ Status::from(ERROR_IO)
+ })
+ }
+ }
+ }
+
+ fn readFsverityMerkleTree(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
+ let size: usize = validate_and_cast_size(size)?;
+ let offset: u64 = validate_and_cast_offset(offset)?;
+
+ match &self.get_file_config(id)? {
+ FdConfig::Readonly { file, alt_merkle_tree, .. } => {
+ if let Some(tree_file) = &alt_merkle_tree {
+ read_into_buf(tree_file, size, offset).map_err(|e| {
+ error!("readFsverityMerkleTree: read error: {}", e);
+ Status::from(ERROR_IO)
+ })
+ } else {
+ let mut buf = vec![0; size];
+ let s = fsverity::read_merkle_tree(file.as_raw_fd(), offset, &mut buf)
+ .map_err(|e| {
+ error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
+ Status::from(e.raw_os_error().unwrap_or(ERROR_IO))
+ })?;
+ debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
+ buf.truncate(s);
+ Ok(buf)
+ }
+ }
+ FdConfig::ReadWrite(_file) => {
+ // For a writable file, Merkle tree is not expected to be served since Auth FS
+ // doesn't trust it anyway. Auth FS may keep the Merkle tree privately for its own
+ // use.
+ Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
+ }
+ }
+ }
+
+ fn readFsveritySignature(&self, id: i32) -> BinderResult<Vec<u8>> {
+ match &self.get_file_config(id)? {
+ FdConfig::Readonly { file, alt_signature, .. } => {
+ if let Some(sig_file) = &alt_signature {
+ // Supposedly big enough buffer size to store signature.
+ let size = MAX_REQUESTING_DATA as usize;
+ let offset = 0;
+ read_into_buf(sig_file, size, offset).map_err(|e| {
+ error!("readFsveritySignature: read error: {}", e);
+ Status::from(ERROR_IO)
+ })
+ } else {
+ let mut buf = vec![0; MAX_REQUESTING_DATA as usize];
+ let s = fsverity::read_signature(file.as_raw_fd(), &mut buf).map_err(|e| {
+ error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
+ Status::from(e.raw_os_error().unwrap_or(ERROR_IO))
+ })?;
+ debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
+ buf.truncate(s);
+ Ok(buf)
+ }
+ }
+ FdConfig::ReadWrite(_file) => {
+ // There is no signature for a writable file.
+ Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
+ }
+ }
+ }
+
+ fn writeFile(&self, id: i32, buf: &[u8], offset: i64) -> BinderResult<i32> {
+ match &self.get_file_config(id)? {
+ FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
+ FdConfig::ReadWrite(file) => {
+ let offset: u64 = offset.try_into().map_err(|_| {
+ new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, "Invalid offset")
+ })?;
+ // Check buffer size just to make `as i32` safe below.
+ if buf.len() > i32::MAX as usize {
+ return Err(new_binder_exception(
+ ExceptionCode::ILLEGAL_ARGUMENT,
+ "Buffer size is too big",
+ ));
+ }
+ Ok(file.write_at(buf, offset).map_err(|e| {
+ error!("writeFile: write error: {}", e);
+ Status::from(ERROR_IO)
+ })? as i32)
+ }
+ }
+ }
+
+ fn resize(&self, id: i32, size: i64) -> BinderResult<()> {
+ match &self.get_file_config(id)? {
+ FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
+ FdConfig::ReadWrite(file) => {
+ if size < 0 {
+ return Err(new_binder_exception(
+ ExceptionCode::ILLEGAL_ARGUMENT,
+ "Invalid size to resize to",
+ ));
+ }
+ file.set_len(size as u64).map_err(|e| {
+ error!("resize: set_len error: {}", e);
+ Status::from(ERROR_IO)
+ })
+ }
+ }
+ }
+
+ fn getFileSize(&self, id: i32) -> BinderResult<i64> {
+ match &self.get_file_config(id)? {
+ FdConfig::Readonly { file, .. } => {
+ let size = file
+ .metadata()
+ .map_err(|e| {
+ error!("getFileSize error: {}", e);
+ Status::from(ERROR_IO)
+ })?
+ .len();
+ Ok(size.try_into().map_err(|e| {
+ error!("getFileSize: File too large: {}", e);
+ Status::from(ERROR_FILE_TOO_LARGE)
+ })?)
+ }
+ FdConfig::ReadWrite(_file) => {
+ // Content and metadata of a writable file needs to be tracked by authfs, since
+ // fd_server isn't considered trusted. So there is no point to support getFileSize
+ // for a writable file.
+ Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
+ }
+ }
+ }
+}
+
+fn read_into_buf(file: &File, max_size: usize, offset: u64) -> io::Result<Vec<u8>> {
+ let remaining = file.metadata()?.len().saturating_sub(offset);
+ let buf_size = min(remaining, max_size as u64) as usize;
+ let mut buf = vec![0; buf_size];
+ file.read_exact_at(&mut buf, offset)?;
+ Ok(buf)
+}
diff --git a/authfs/fd_server/src/main.rs b/authfs/fd_server/src/main.rs
index 395e2e9..3413ce6 100644
--- a/authfs/fd_server/src/main.rs
+++ b/authfs/fd_server/src/main.rs
@@ -22,235 +22,20 @@
//! For example, `exec 9</path/to/file fd_server --ro-fds 9` starts the binder service. A client
//! client can then request the content of file 9 by offset and size.
+mod aidl;
mod fsverity;
use anyhow::{bail, Result};
use binder_common::rpc_server::run_rpc_server;
-use log::{debug, error};
-use std::cmp::min;
+use log::debug;
use std::collections::BTreeMap;
-use std::convert::TryInto;
use std::fs::File;
-use std::io;
-use std::os::unix::fs::FileExt;
-use std::os::unix::io::{AsRawFd, FromRawFd};
+use std::os::unix::io::FromRawFd;
-use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::{
- BnVirtFdService, IVirtFdService, ERROR_FILE_TOO_LARGE, ERROR_IO, ERROR_UNKNOWN_FD,
- MAX_REQUESTING_DATA,
-};
-use authfs_aidl_interface::binder::{
- BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, StatusCode, Strong,
-};
-use binder_common::new_binder_exception;
+use aidl::{FdConfig, FdService};
const RPC_SERVICE_PORT: u32 = 3264; // TODO: support dynamic port for multiple fd_server instances
-fn validate_and_cast_offset(offset: i64) -> Result<u64, Status> {
- offset.try_into().map_err(|_| {
- new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, format!("Invalid offset: {}", offset))
- })
-}
-
-fn validate_and_cast_size(size: i32) -> Result<usize, Status> {
- if size > MAX_REQUESTING_DATA {
- Err(new_binder_exception(
- ExceptionCode::ILLEGAL_ARGUMENT,
- format!("Unexpectedly large size: {}", size),
- ))
- } else {
- size.try_into().map_err(|_| {
- new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, format!("Invalid size: {}", size))
- })
- }
-}
-
-/// Configuration of a file descriptor to be served/exposed/shared.
-enum FdConfig {
- /// A read-only file to serve by this server. The file is supposed to be verifiable with the
- /// associated fs-verity metadata.
- Readonly {
- /// The file to read from. fs-verity metadata can be retrieved from this file's FD.
- file: File,
-
- /// Alternative Merkle tree stored in another file.
- alt_merkle_tree: Option<File>,
-
- /// Alternative signature stored in another file.
- alt_signature: Option<File>,
- },
-
- /// A readable/writable file to serve by this server. This backing file should just be a
- /// regular file and does not have any specific property.
- ReadWrite(File),
-}
-
-struct FdService {
- /// A pool of opened files, may be readonly or read-writable.
- fd_pool: BTreeMap<i32, FdConfig>,
-}
-
-impl FdService {
- pub fn new_binder(fd_pool: BTreeMap<i32, FdConfig>) -> Strong<dyn IVirtFdService> {
- BnVirtFdService::new_binder(FdService { fd_pool }, BinderFeatures::default())
- }
-
- fn get_file_config(&self, id: i32) -> BinderResult<&FdConfig> {
- self.fd_pool.get(&id).ok_or_else(|| Status::from(ERROR_UNKNOWN_FD))
- }
-}
-
-impl Interface for FdService {}
-
-impl IVirtFdService for FdService {
- fn readFile(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
- let size: usize = validate_and_cast_size(size)?;
- let offset: u64 = validate_and_cast_offset(offset)?;
-
- match self.get_file_config(id)? {
- FdConfig::Readonly { file, .. } | FdConfig::ReadWrite(file) => {
- read_into_buf(file, size, offset).map_err(|e| {
- error!("readFile: read error: {}", e);
- Status::from(ERROR_IO)
- })
- }
- }
- }
-
- fn readFsverityMerkleTree(&self, id: i32, offset: i64, size: i32) -> BinderResult<Vec<u8>> {
- let size: usize = validate_and_cast_size(size)?;
- let offset: u64 = validate_and_cast_offset(offset)?;
-
- match &self.get_file_config(id)? {
- FdConfig::Readonly { file, alt_merkle_tree, .. } => {
- if let Some(tree_file) = &alt_merkle_tree {
- read_into_buf(tree_file, size, offset).map_err(|e| {
- error!("readFsverityMerkleTree: read error: {}", e);
- Status::from(ERROR_IO)
- })
- } else {
- let mut buf = vec![0; size];
- let s = fsverity::read_merkle_tree(file.as_raw_fd(), offset, &mut buf)
- .map_err(|e| {
- error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
- Status::from(e.raw_os_error().unwrap_or(ERROR_IO))
- })?;
- debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
- buf.truncate(s);
- Ok(buf)
- }
- }
- FdConfig::ReadWrite(_file) => {
- // For a writable file, Merkle tree is not expected to be served since Auth FS
- // doesn't trust it anyway. Auth FS may keep the Merkle tree privately for its own
- // use.
- Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
- }
- }
- }
-
- fn readFsveritySignature(&self, id: i32) -> BinderResult<Vec<u8>> {
- match &self.get_file_config(id)? {
- FdConfig::Readonly { file, alt_signature, .. } => {
- if let Some(sig_file) = &alt_signature {
- // Supposedly big enough buffer size to store signature.
- let size = MAX_REQUESTING_DATA as usize;
- let offset = 0;
- read_into_buf(sig_file, size, offset).map_err(|e| {
- error!("readFsveritySignature: read error: {}", e);
- Status::from(ERROR_IO)
- })
- } else {
- let mut buf = vec![0; MAX_REQUESTING_DATA as usize];
- let s = fsverity::read_signature(file.as_raw_fd(), &mut buf).map_err(|e| {
- error!("readFsverityMerkleTree: failed to retrieve merkle tree: {}", e);
- Status::from(e.raw_os_error().unwrap_or(ERROR_IO))
- })?;
- debug_assert!(s <= buf.len(), "Shouldn't return more bytes than asked");
- buf.truncate(s);
- Ok(buf)
- }
- }
- FdConfig::ReadWrite(_file) => {
- // There is no signature for a writable file.
- Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
- }
- }
- }
-
- fn writeFile(&self, id: i32, buf: &[u8], offset: i64) -> BinderResult<i32> {
- match &self.get_file_config(id)? {
- FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
- FdConfig::ReadWrite(file) => {
- let offset: u64 = offset.try_into().map_err(|_| {
- new_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT, "Invalid offset")
- })?;
- // Check buffer size just to make `as i32` safe below.
- if buf.len() > i32::MAX as usize {
- return Err(new_binder_exception(
- ExceptionCode::ILLEGAL_ARGUMENT,
- "Buffer size is too big",
- ));
- }
- Ok(file.write_at(buf, offset).map_err(|e| {
- error!("writeFile: write error: {}", e);
- Status::from(ERROR_IO)
- })? as i32)
- }
- }
- }
-
- fn resize(&self, id: i32, size: i64) -> BinderResult<()> {
- match &self.get_file_config(id)? {
- FdConfig::Readonly { .. } => Err(StatusCode::INVALID_OPERATION.into()),
- FdConfig::ReadWrite(file) => {
- if size < 0 {
- return Err(new_binder_exception(
- ExceptionCode::ILLEGAL_ARGUMENT,
- "Invalid size to resize to",
- ));
- }
- file.set_len(size as u64).map_err(|e| {
- error!("resize: set_len error: {}", e);
- Status::from(ERROR_IO)
- })
- }
- }
- }
-
- fn getFileSize(&self, id: i32) -> BinderResult<i64> {
- match &self.get_file_config(id)? {
- FdConfig::Readonly { file, .. } => {
- let size = file
- .metadata()
- .map_err(|e| {
- error!("getFileSize error: {}", e);
- Status::from(ERROR_IO)
- })?
- .len();
- Ok(size.try_into().map_err(|e| {
- error!("getFileSize: File too large: {}", e);
- Status::from(ERROR_FILE_TOO_LARGE)
- })?)
- }
- FdConfig::ReadWrite(_file) => {
- // Content and metadata of a writable file needs to be tracked by authfs, since
- // fd_server isn't considered trusted. So there is no point to support getFileSize
- // for a writable file.
- Err(new_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION, "Unsupported"))
- }
- }
- }
-}
-
-fn read_into_buf(file: &File, max_size: usize, offset: u64) -> io::Result<Vec<u8>> {
- let remaining = file.metadata()?.len().saturating_sub(offset);
- let buf_size = min(remaining, max_size as u64) as usize;
- let mut buf = vec![0; buf_size];
- file.read_exact_at(&mut buf, offset)?;
- Ok(buf)
-}
-
fn is_fd_valid(fd: i32) -> bool {
// SAFETY: a query-only syscall
let retval = unsafe { libc::fcntl(fd, libc::F_GETFD) };
diff --git a/authfs/src/file.rs b/authfs/src/file.rs
index 947b59f..404e3a5 100644
--- a/authfs/src/file.rs
+++ b/authfs/src/file.rs
@@ -1,7 +1,5 @@
-mod local_file;
mod remote_file;
-pub use local_file::LocalFileReader;
pub use remote_file::{RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader};
use binder::unstable_api::{new_spibinder, AIBinder};
diff --git a/authfs/src/file/local_file.rs b/authfs/src/file/local_file.rs
deleted file mode 100644
index 13c954f..0000000
--- a/authfs/src/file/local_file.rs
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-use std::cmp::min;
-use std::fs::File;
-use std::io;
-use std::os::unix::fs::FileExt;
-
-use super::{ChunkBuffer, ReadByChunk};
-use crate::common::CHUNK_SIZE;
-
-/// A read-only file that can be read by chunks.
-pub struct LocalFileReader {
- file: File,
- size: u64,
-}
-
-impl LocalFileReader {
- /// Creates a `LocalFileReader` to read from for the specified `path`.
- pub fn new(file: File) -> io::Result<LocalFileReader> {
- let size = file.metadata()?.len();
- Ok(LocalFileReader { file, size })
- }
-
- pub fn len(&self) -> u64 {
- self.size
- }
-}
-
-impl ReadByChunk for LocalFileReader {
- fn read_chunk(&self, chunk_index: u64, buf: &mut ChunkBuffer) -> io::Result<usize> {
- let start = chunk_index * CHUNK_SIZE;
- if start >= self.size {
- return Ok(0);
- }
- let end = min(self.size, start + CHUNK_SIZE);
- let read_size = (end - start) as usize;
- debug_assert!(read_size <= buf.len());
- self.file.read_exact_at(&mut buf[..read_size], start)?;
- Ok(read_size)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::env::temp_dir;
-
- #[test]
- fn test_read_4k_file() -> io::Result<()> {
- let file_reader = LocalFileReader::new(File::open("testdata/input.4k")?)?;
- let mut buf = [0u8; 4096];
- let size = file_reader.read_chunk(0, &mut buf)?;
- assert_eq!(size, buf.len());
- Ok(())
- }
-
- #[test]
- fn test_read_4k1_file() -> io::Result<()> {
- let file_reader = LocalFileReader::new(File::open("testdata/input.4k1")?)?;
- let mut buf = [0u8; 4096];
- let size = file_reader.read_chunk(0, &mut buf)?;
- assert_eq!(size, buf.len());
- let size = file_reader.read_chunk(1, &mut buf)?;
- assert_eq!(size, 1);
- Ok(())
- }
-
- #[test]
- fn test_read_4m_file() -> io::Result<()> {
- let file_reader = LocalFileReader::new(File::open("testdata/input.4m")?)?;
- for index in 0..file_reader.len() / 4096 {
- let mut buf = [0u8; 4096];
- let size = file_reader.read_chunk(index, &mut buf)?;
- assert_eq!(size, buf.len());
- }
- Ok(())
- }
-
- #[test]
- fn test_read_beyond_file_size() -> io::Result<()> {
- let file_reader = LocalFileReader::new(File::open("testdata/input.4k").unwrap()).unwrap();
- let mut buf = [0u8; 4096];
- let size = file_reader.read_chunk(1u64, &mut buf)?;
- assert_eq!(size, 0);
- Ok(())
- }
-
- #[test]
- fn test_read_empty_file() -> io::Result<()> {
- let mut temp_file = temp_dir();
- temp_file.push("authfs_test_empty_file");
- let file_reader = LocalFileReader::new(File::create(temp_file).unwrap()).unwrap();
- let mut buf = [0u8; 4096];
- let size = file_reader.read_chunk(0, &mut buf)?;
- assert_eq!(size, 0);
- Ok(())
- }
-}
diff --git a/authfs/src/fsverity/verifier.rs b/authfs/src/fsverity/verifier.rs
index 1f21b13..4a18c6a 100644
--- a/authfs/src/fsverity/verifier.rs
+++ b/authfs/src/fsverity/verifier.rs
@@ -173,10 +173,42 @@
mod tests {
use super::*;
use crate::auth::FakeAuthenticator;
- use crate::file::{LocalFileReader, ReadByChunk};
+ use crate::file::ReadByChunk;
use anyhow::Result;
+ use std::cmp::min;
use std::fs::{self, File};
use std::io::Read;
+ use std::os::unix::fs::FileExt;
+
+ struct LocalFileReader {
+ file: File,
+ size: u64,
+ }
+
+ impl LocalFileReader {
+ fn new(file: File) -> io::Result<LocalFileReader> {
+ let size = file.metadata()?.len();
+ Ok(LocalFileReader { file, size })
+ }
+
+ fn len(&self) -> u64 {
+ self.size
+ }
+ }
+
+ impl ReadByChunk for LocalFileReader {
+ fn read_chunk(&self, chunk_index: u64, buf: &mut ChunkBuffer) -> io::Result<usize> {
+ let start = chunk_index * CHUNK_SIZE;
+ if start >= self.size {
+ return Ok(0);
+ }
+ let end = min(self.size, start + CHUNK_SIZE);
+ let read_size = (end - start) as usize;
+ debug_assert!(read_size <= buf.len());
+ self.file.read_exact_at(&mut buf[..read_size], start)?;
+ Ok(read_size)
+ }
+ }
type LocalVerifiedFileReader = VerifiedFileReader<LocalFileReader, LocalFileReader>;
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index 6bdb498..d54b5be 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -35,8 +35,7 @@
use crate::common::{divide_roundup, ChunkedSizeIter, CHUNK_SIZE};
use crate::file::{
- LocalFileReader, RandomWrite, ReadByChunk, RemoteFileEditor, RemoteFileReader,
- RemoteMerkleTreeReader,
+ RandomWrite, ReadByChunk, RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader,
};
use crate::fsverity::{VerifiedFileEditor, VerifiedFileReader};
@@ -48,24 +47,16 @@
/// `FileConfig` defines the file type supported by AuthFS.
pub enum FileConfig {
/// A file type that is verified against fs-verity signature (thus read-only). The file is
- /// backed by a local file. Debug only.
- LocalVerifiedReadonly {
- reader: VerifiedFileReader<LocalFileReader, LocalFileReader>,
- file_size: u64,
- },
- /// A file type that is a read-only passthrough from a local file. Debug only.
- LocalUnverifiedReadonly { reader: LocalFileReader, file_size: u64 },
- /// A file type that is verified against fs-verity signature (thus read-only). The file is
/// served from a remote server.
- RemoteVerifiedReadonly {
+ VerifiedReadonly {
reader: VerifiedFileReader<RemoteFileReader, RemoteMerkleTreeReader>,
file_size: u64,
},
/// A file type that is a read-only passthrough from a file on a remote serrver.
- RemoteUnverifiedReadonly { reader: RemoteFileReader, file_size: u64 },
+ UnverifiedReadonly { reader: RemoteFileReader, file_size: u64 },
/// A file type that is initially empty, and the content is stored on a remote server. File
/// integrity is guaranteed with private Merkle tree.
- RemoteVerifiedNew { editor: VerifiedFileEditor<RemoteFileEditor> },
+ VerifiedNew { editor: VerifiedFileEditor<RemoteFileEditor> },
}
struct AuthFs {
@@ -207,13 +198,11 @@
// be static.
let inode = num.parse::<Inode>().map_err(|_| io::Error::from_raw_os_error(libc::ENOENT))?;
let st = match self.get_file_config(&inode)? {
- FileConfig::LocalVerifiedReadonly { file_size, .. }
- | FileConfig::LocalUnverifiedReadonly { file_size, .. }
- | FileConfig::RemoteUnverifiedReadonly { file_size, .. }
- | FileConfig::RemoteVerifiedReadonly { file_size, .. } => {
+ FileConfig::UnverifiedReadonly { file_size, .. }
+ | FileConfig::VerifiedReadonly { file_size, .. } => {
create_stat(inode, *file_size, FileMode::ReadOnly)?
}
- FileConfig::RemoteVerifiedNew { editor } => {
+ FileConfig::VerifiedNew { editor } => {
create_stat(inode, editor.size(), FileMode::ReadWrite)?
}
};
@@ -234,13 +223,11 @@
) -> io::Result<(libc::stat64, Duration)> {
Ok((
match self.get_file_config(&inode)? {
- FileConfig::LocalVerifiedReadonly { file_size, .. }
- | FileConfig::LocalUnverifiedReadonly { file_size, .. }
- | FileConfig::RemoteUnverifiedReadonly { file_size, .. }
- | FileConfig::RemoteVerifiedReadonly { file_size, .. } => {
+ FileConfig::UnverifiedReadonly { file_size, .. }
+ | FileConfig::VerifiedReadonly { file_size, .. } => {
create_stat(inode, *file_size, FileMode::ReadOnly)?
}
- FileConfig::RemoteVerifiedNew { editor } => {
+ FileConfig::VerifiedNew { editor } => {
create_stat(inode, editor.size(), FileMode::ReadWrite)?
}
},
@@ -257,13 +244,10 @@
// Since file handle is not really used in later operations (which use Inode directly),
// return None as the handle.
match self.get_file_config(&inode)? {
- FileConfig::LocalVerifiedReadonly { .. }
- | FileConfig::LocalUnverifiedReadonly { .. }
- | FileConfig::RemoteVerifiedReadonly { .. }
- | FileConfig::RemoteUnverifiedReadonly { .. } => {
+ FileConfig::VerifiedReadonly { .. } | FileConfig::UnverifiedReadonly { .. } => {
check_access_mode(flags, libc::O_RDONLY)?;
}
- FileConfig::RemoteVerifiedNew { .. } => {
+ FileConfig::VerifiedNew { .. } => {
// No need to check access modes since all the modes are allowed to the
// read-writable file.
}
@@ -285,19 +269,13 @@
_flags: u32,
) -> io::Result<usize> {
match self.get_file_config(&inode)? {
- FileConfig::LocalVerifiedReadonly { reader, file_size } => {
+ FileConfig::VerifiedReadonly { reader, file_size } => {
read_chunks(w, reader, *file_size, offset, size)
}
- FileConfig::LocalUnverifiedReadonly { reader, file_size } => {
+ FileConfig::UnverifiedReadonly { reader, file_size } => {
read_chunks(w, reader, *file_size, offset, size)
}
- FileConfig::RemoteVerifiedReadonly { reader, file_size } => {
- read_chunks(w, reader, *file_size, offset, size)
- }
- FileConfig::RemoteUnverifiedReadonly { reader, file_size } => {
- read_chunks(w, reader, *file_size, offset, size)
- }
- FileConfig::RemoteVerifiedNew { editor } => {
+ FileConfig::VerifiedNew { editor } => {
// Note that with FsOptions::WRITEBACK_CACHE, it's possible for the kernel to
// request a read even if the file is open with O_WRONLY.
read_chunks(w, editor, editor.size(), offset, size)
@@ -318,7 +296,7 @@
_flags: u32,
) -> io::Result<usize> {
match self.get_file_config(&inode)? {
- FileConfig::RemoteVerifiedNew { editor } => {
+ FileConfig::VerifiedNew { editor } => {
let mut buf = vec![0; size as usize];
r.read_exact(&mut buf)?;
editor.write_at(&buf, offset)
@@ -336,7 +314,7 @@
valid: SetattrValid,
) -> io::Result<(libc::stat64, Duration)> {
match self.get_file_config(&inode)? {
- FileConfig::RemoteVerifiedNew { editor } => {
+ FileConfig::VerifiedNew { editor } => {
// Initialize the default stat.
let mut new_attr = create_stat(inode, editor.size(), FileMode::ReadWrite)?;
// `valid` indicates what fields in `attr` are valid. Update to return correctly.
@@ -383,7 +361,7 @@
size: u32,
) -> io::Result<GetxattrReply> {
match self.get_file_config(&inode)? {
- FileConfig::RemoteVerifiedNew { editor } => {
+ FileConfig::VerifiedNew { 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.
diff --git a/authfs/src/main.rs b/authfs/src/main.rs
index ecb0e68..a6956e2 100644
--- a/authfs/src/main.rs
+++ b/authfs/src/main.rs
@@ -22,18 +22,16 @@
//! each read of file block can be verified individually only when needed.
//!
//! AuthFS only serve files that are specifically configured. A file configuration may include the
-//! source (e.g. local file or remote file server), verification method (e.g. certificate for
-//! fs-verity verification, or no verification if expected to mount over dm-verity), and file ID.
-//! Regardless of the actual file name, the exposed file names through AuthFS are currently integer,
-//! e.g. /mountpoint/42.
+//! source (e.g. remote file server), verification method (e.g. certificate for fs-verity
+//! verification, or no verification if expected to mount over dm-verity), and file ID. Regardless
+//! of the actual file name, the exposed file names through AuthFS are currently integer, e.g.
+//! /mountpoint/42.
use anyhow::{bail, Context, Result};
use log::error;
use std::collections::BTreeMap;
use std::convert::TryInto;
-use std::fs::File;
-use std::io::Read;
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
use structopt::StructOpt;
mod auth;
@@ -44,7 +42,7 @@
mod fusefs;
use auth::FakeAuthenticator;
-use file::{LocalFileReader, RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader};
+use file::{RemoteFileEditor, RemoteFileReader, RemoteMerkleTreeReader};
use fsverity::{VerifiedFileEditor, VerifiedFileReader};
use fusefs::{FileConfig, Inode};
@@ -83,27 +81,11 @@
#[structopt(long, parse(try_from_str = parse_remote_new_rw_file_option))]
remote_new_rw_file: Vec<OptionRemoteRwFile>,
- /// Debug only. A read-only local file with integrity check. Can be multiple.
- #[structopt(long, parse(try_from_str = parse_local_file_ro_option))]
- local_ro_file: Vec<OptionLocalFileRo>,
-
- /// Debug only. A read-only local file without integrity check. Can be multiple.
- #[structopt(long, parse(try_from_str = parse_local_ro_file_unverified_ro_option))]
- local_ro_file_unverified: Vec<OptionLocalRoFileUnverified>,
-
/// Enable debugging features.
#[structopt(long)]
debug: bool,
}
-impl Args {
- fn has_remote_files(&self) -> bool {
- !self.remote_ro_file.is_empty()
- || !self.remote_ro_file_unverified.is_empty()
- || !self.remote_new_rw_file.is_empty()
- }
-}
-
struct OptionRemoteRoFile {
ino: Inode,
@@ -129,30 +111,6 @@
remote_id: i32,
}
-struct OptionLocalFileRo {
- ino: Inode,
-
- /// Local path of the backing file.
- file_path: PathBuf,
-
- /// Local path of the backing file's fs-verity Merkle tree dump.
- merkle_tree_dump_path: PathBuf,
-
- /// Local path of fs-verity signature for the backing file.
- signature_path: PathBuf,
-
- /// Certificate to verify the authenticity of the file's fs-verity signature.
- /// TODO(170494765): Implement PKCS#7 signature verification.
- _certificate_path: PathBuf,
-}
-
-struct OptionLocalRoFileUnverified {
- ino: Inode,
-
- /// Local path of the backing file.
- file_path: PathBuf,
-}
-
fn parse_remote_ro_file_option(option: &str) -> Result<OptionRemoteRoFile> {
let strs: Vec<&str> = option.split(':').collect();
if strs.len() != 3 {
@@ -187,31 +145,6 @@
})
}
-fn parse_local_file_ro_option(option: &str) -> Result<OptionLocalFileRo> {
- let strs: Vec<&str> = option.split(':').collect();
- if strs.len() != 5 {
- bail!("Invalid option: {}", option);
- }
- Ok(OptionLocalFileRo {
- ino: strs[0].parse::<Inode>()?,
- file_path: PathBuf::from(strs[1]),
- merkle_tree_dump_path: PathBuf::from(strs[2]),
- signature_path: PathBuf::from(strs[3]),
- _certificate_path: PathBuf::from(strs[4]),
- })
-}
-
-fn parse_local_ro_file_unverified_ro_option(option: &str) -> Result<OptionLocalRoFileUnverified> {
- let strs: Vec<&str> = option.split(':').collect();
- if strs.len() != 2 {
- bail!("Invalid option: {}", option);
- }
- Ok(OptionLocalRoFileUnverified {
- ino: strs[0].parse::<Inode>()?,
- file_path: PathBuf::from(strs[1]),
- })
-}
-
fn new_config_remote_verified_file(
service: file::VirtFdService,
remote_id: i32,
@@ -220,7 +153,7 @@
let signature = service.readFsveritySignature(remote_id).context("Failed to read signature")?;
let authenticator = FakeAuthenticator::always_succeed();
- Ok(FileConfig::RemoteVerifiedReadonly {
+ Ok(FileConfig::VerifiedReadonly {
reader: VerifiedFileReader::new(
&authenticator,
RemoteFileReader::new(service.clone(), remote_id),
@@ -238,30 +171,7 @@
file_size: u64,
) -> Result<FileConfig> {
let reader = RemoteFileReader::new(service, remote_id);
- Ok(FileConfig::RemoteUnverifiedReadonly { reader, file_size })
-}
-
-fn new_config_local_ro_file(
- protected_file: &Path,
- merkle_tree_dump: &Path,
- signature: &Path,
-) -> Result<FileConfig> {
- let file = File::open(&protected_file)?;
- let file_size = file.metadata()?.len();
- let file_reader = LocalFileReader::new(file)?;
- let merkle_tree_reader = LocalFileReader::new(File::open(merkle_tree_dump)?)?;
- let authenticator = FakeAuthenticator::always_succeed();
- let mut sig = Vec::new();
- let _ = File::open(signature)?.read_to_end(&mut sig)?;
- let reader =
- VerifiedFileReader::new(&authenticator, file_reader, file_size, sig, merkle_tree_reader)?;
- Ok(FileConfig::LocalVerifiedReadonly { reader, file_size })
-}
-
-fn new_config_local_ro_file_unverified(file_path: &Path) -> Result<FileConfig> {
- let reader = LocalFileReader::new(File::open(file_path)?)?;
- let file_size = reader.len();
- Ok(FileConfig::LocalUnverifiedReadonly { reader, file_size })
+ Ok(FileConfig::UnverifiedReadonly { reader, file_size })
}
fn new_config_remote_new_verified_file(
@@ -269,58 +179,41 @@
remote_id: i32,
) -> Result<FileConfig> {
let remote_file = RemoteFileEditor::new(service, remote_id);
- Ok(FileConfig::RemoteVerifiedNew { editor: VerifiedFileEditor::new(remote_file) })
+ Ok(FileConfig::VerifiedNew { editor: VerifiedFileEditor::new(remote_file) })
}
fn prepare_file_pool(args: &Args) -> Result<BTreeMap<Inode, FileConfig>> {
let mut file_pool = BTreeMap::new();
- if args.has_remote_files() {
- let service = file::get_rpc_binder_service(args.cid)?;
+ let service = file::get_rpc_binder_service(args.cid)?;
- for config in &args.remote_ro_file {
- file_pool.insert(
- config.ino,
- new_config_remote_verified_file(
- service.clone(),
- config.remote_id,
- service.getFileSize(config.remote_id)?.try_into()?,
- )?,
- );
- }
-
- for config in &args.remote_ro_file_unverified {
- file_pool.insert(
- config.ino,
- new_config_remote_unverified_file(
- service.clone(),
- config.remote_id,
- service.getFileSize(config.remote_id)?.try_into()?,
- )?,
- );
- }
-
- for config in &args.remote_new_rw_file {
- file_pool.insert(
- config.ino,
- new_config_remote_new_verified_file(service.clone(), config.remote_id)?,
- );
- }
- }
-
- for config in &args.local_ro_file {
+ for config in &args.remote_ro_file {
file_pool.insert(
config.ino,
- new_config_local_ro_file(
- &config.file_path,
- &config.merkle_tree_dump_path,
- &config.signature_path,
+ new_config_remote_verified_file(
+ service.clone(),
+ config.remote_id,
+ service.getFileSize(config.remote_id)?.try_into()?,
)?,
);
}
- for config in &args.local_ro_file_unverified {
- file_pool.insert(config.ino, new_config_local_ro_file_unverified(&config.file_path)?);
+ for config in &args.remote_ro_file_unverified {
+ file_pool.insert(
+ config.ino,
+ new_config_remote_unverified_file(
+ service.clone(),
+ config.remote_id,
+ service.getFileSize(config.remote_id)?.try_into()?,
+ )?,
+ );
+ }
+
+ for config in &args.remote_new_rw_file {
+ file_pool.insert(
+ config.ino,
+ new_config_remote_new_verified_file(service.clone(), config.remote_id)?,
+ );
}
Ok(file_pool)
diff --git a/demo/Android.bp b/demo/Android.bp
index 749ca90..1342a26 100644
--- a/demo/Android.bp
+++ b/demo/Android.bp
@@ -19,5 +19,4 @@
platform_apis: true,
use_embedded_native_libs: true,
v4_signature: true,
- certificate: "platform",
}
diff --git a/demo/AndroidManifest.xml b/demo/AndroidManifest.xml
index 7e1a58d..74ec210 100644
--- a/demo/AndroidManifest.xml
+++ b/demo/AndroidManifest.xml
@@ -6,7 +6,8 @@
<application
android:label="MicrodroidDemo"
- android:theme="@style/Theme.MicrodroidDemo">
+ android:theme="@style/Theme.MicrodroidDemo"
+ android:testOnly="true">
<uses-library android:name="android.system.virtualmachine" android:required="true" />
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
diff --git a/javalib/AndroidManifest.xml b/javalib/AndroidManifest.xml
index f96b39f..2a0b903 100644
--- a/javalib/AndroidManifest.xml
+++ b/javalib/AndroidManifest.xml
@@ -18,7 +18,7 @@
package="com.android.virtualmachine.res">
<permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|development" />
<permission android:name="android.permission.DEBUG_VIRTUAL_MACHINE"
android:protectionLevel="signature" />
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 389ebb0..425657f 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -354,7 +354,7 @@
// For unknown reason, the signed bootloader doesn't work on x86_64. Until the problem
// is fixed, let's use the unsigned bootloader for the architecture.
// TODO(b/185115783): remove this
- src: ":cuttlefish_crosvm_bootloader",
+ src: ":microdroid_crosvm_bootloader",
},
},
filename: "microdroid_bootloader",
@@ -368,7 +368,7 @@
name: "microdroid_bootloader_gen",
tools: ["avbtool"],
srcs: [
- ":cuttlefish_crosvm_bootloader",
+ ":microdroid_crosvm_bootloader",
":avb_testkey_rsa4096",
],
out: ["bootloader-signed"],
@@ -378,7 +378,7 @@
// bootloader file whose size is 1. It can't pass avbtool.
// 3. Add the hash footer. The partition size is set to (image size + 68KB)
// rounded up to 4KB boundary.
- cmd: "cp $(location :cuttlefish_crosvm_bootloader) $(out) && " +
+ cmd: "cp $(location :microdroid_crosvm_bootloader) $(out) && " +
"if [ $$(stat --format=%s $(out)) -gt 4096 ]; then " +
"$(location avbtool) add_hash_footer " +
"--algorithm SHA256_RSA4096 " +
diff --git a/microdroid/bootconfig.app_debuggable b/microdroid/bootconfig.app_debuggable
index 79e2b08..f65d4cd 100644
--- a/microdroid/bootconfig.app_debuggable
+++ b/microdroid/bootconfig.app_debuggable
@@ -1 +1,10 @@
+# TODO(b/203369076) This should be 0 to disable adb rooting. For now, we can't do that because
+# if this is set to 0, adbd enforces the host authentication but we don't put the adb
+# public key (which represents the owner) in the VM yet.
+androidboot.microdroid.debuggable=0
+
+# Console output is not redirect to the host-side.
kernel.console = null
+
+# ADB is supported but rooting is prohibited.
+androidboot.adb.enabled=1
diff --git a/microdroid/bootconfig.full_debuggable b/microdroid/bootconfig.full_debuggable
index d67cd76..f6afdcf 100644
--- a/microdroid/bootconfig.full_debuggable
+++ b/microdroid/bootconfig.full_debuggable
@@ -1 +1,10 @@
+# ro.debuggable is set.
+androidboot.microdroid.debuggable=1
+
+# Kernel message is exported.
kernel.printk.devkmsg=on
+
+# ADB is supported and rooting is possible. Note that
+# ro.adb.secure is still 0 (see build.prop) which means that adbd is started
+# unrooted by default. To root, developer should explicitly execute `adb root`.
+androidboot.adb.enabled=1
diff --git a/microdroid/bootconfig.normal b/microdroid/bootconfig.normal
index 79e2b08..f7cdfc7 100644
--- a/microdroid/bootconfig.normal
+++ b/microdroid/bootconfig.normal
@@ -1 +1,8 @@
+# ro.debuggable is off
+androidboot.microdroid.debuggable=0
+
+# Console output is not redirect to the host-side.
kernel.console = null
+
+# ADB is not enabled.
+androidboot.adb.enabled=0
diff --git a/microdroid/build.prop b/microdroid/build.prop
index eaca63d..ada945d 100644
--- a/microdroid/build.prop
+++ b/microdroid/build.prop
@@ -1,6 +1,5 @@
# build.prop for microdroid
ro.apex.updatable=true
-ro.debuggable=1
ro.adb.secure=0
service.adb.listen_addrs=vsock:5555
diff --git a/microdroid/init.rc b/microdroid/init.rc
index 23434bb..078b51d 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -30,6 +30,8 @@
# payloads are not designed to run with bootstrap bionic
setprop apex_config.done true
+ setprop ro.debuggable ${ro.boot.microdroid.debuggable:-0}
+
on init
# Mount binderfs
mkdir /dev/binderfs
@@ -80,6 +82,7 @@
# TODO(b/185767624): remove hidl after full keymint support
start hwservicemanager
+on init && property:ro.boot.adb.enabled=1
start adbd
on load_persist_props_action