blob: bdcad98337d87c1ba231a38deec137228f55386c [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
fidl_fuchsia_io::*,
files_async::*,
fuchsia_zircon::Status,
io_util::{directory::*, file::*},
std::path::PathBuf,
};
// A convenience wrapper over a FIDL DirectoryProxy.
// Functions of this struct do not tolerate errors and will panic when they encounter them.
pub struct Directory {
proxy: DirectoryProxy,
}
impl Directory {
// Create a Directory object from a path in the namespace
pub fn from_namespace(path: PathBuf) -> Directory {
let path_str = path.into_os_string().into_string().unwrap();
let proxy = io_util::directory::open_in_namespace(
&path_str,
OPEN_RIGHT_WRITABLE | OPEN_RIGHT_READABLE,
)
.unwrap();
Directory { proxy }
}
// Create a new file in the directory with a given |filename|.
pub async fn create(&self, filename: &str) -> File {
let proxy = open_file(
&self.proxy,
filename,
OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_IF_ABSENT | OPEN_RIGHT_WRITABLE,
)
.await
.unwrap();
File { proxy }
}
// Open a file that already exists in the directory with the given |filename|.
pub async fn open(&self, filename: &str) -> File {
let proxy = open_file(&self.proxy, filename, OPEN_RIGHT_READABLE).await.unwrap();
File { proxy }
}
// Delete a file from the directory
pub async fn remove(&self, filename: &str) {
let raw_status_code = self.proxy.unlink(filename).await.unwrap();
Status::ok(raw_status_code).unwrap();
}
// Return a list of filenames in the directory
pub async fn entries(&self) -> Vec<String> {
readdir(&self.proxy)
.await
.unwrap()
.iter()
.map(|entry| {
assert_eq!(entry.kind, DirentKind::File);
entry.name.clone()
})
.collect()
}
}
// A convenience wrapper over a FIDL FileProxy.
// Most functions of this struct do not tolerate errors and will panic when they encounter them.
#[derive(Debug)]
pub struct File {
proxy: FileProxy,
}
impl File {
// Set the length of the file
pub async fn truncate(&self, length: u64) {
let raw_status_code = self.proxy.truncate(length).await.unwrap();
// TODO(fxbug.dev/58749): Bubble the status code up
Status::ok(raw_status_code).unwrap();
}
// Write the contents of |data| to the file.
// This function will return an error if the filesystem reports there is no space left.
// All other errors will cause a panic.
pub async fn write(&self, data: &Vec<u8>) -> Result<(), Status> {
let length = data.len() as u64;
let mut cur: u64 = 0;
while cur < length {
let end = std::cmp::min(cur + MAX_BUF, length);
let to_write = &data[cur as usize..end as usize];
let (raw_status_code, bytes_written) = self.proxy.write(to_write).await.unwrap();
Status::ok(raw_status_code)?;
assert!(bytes_written != 0);
cur += bytes_written;
}
Ok(())
}
// Flush all writes to this file to disk
pub async fn flush(&self) {
let raw_status_code = self.proxy.sync().await.unwrap();
// TODO(fxbug.dev/58749): Bubble the status code up
Status::ok(raw_status_code).unwrap();
}
// Get the size of this file as it exists on disk
pub async fn size_on_disk(&self) -> u64 {
let (raw_status_code, attr) = self.proxy.get_attr().await.unwrap();
// TODO(fxbug.dev/58749): Bubble the status code up
Status::ok(raw_status_code).unwrap();
attr.storage_size
}
// Get the uncompressed size of this file (as it would exist in memory)
pub async fn uncompressed_size(&self) -> u64 {
let (raw_status_code, attr) = self.proxy.get_attr().await.unwrap();
// TODO(fxbug.dev/58749): Bubble the status code up
Status::ok(raw_status_code).unwrap();
attr.content_size
}
// Read exactly |num_bytes| of the file from the current offset.
pub async fn read(&self, mut num_bytes: u64) -> Vec<u8> {
let mut data = vec![];
// Read in chunks of |MAX_BUF| bytes.
// This is the maximum buffer size supported over FIDL.
while num_bytes > 0 {
let bytes_to_read = std::cmp::min(num_bytes, fidl_fuchsia_io::MAX_BUF);
let (raw_status_code, mut bytes) = self.proxy.read(bytes_to_read).await.unwrap();
// TODO(fxbug.dev/58749): Bubble the status code up
Status::ok(raw_status_code).unwrap();
assert_eq!(bytes.len() as u64, bytes_to_read);
data.append(&mut bytes);
num_bytes -= bytes_to_read;
}
data
}
// Read from the current offset until EOF
pub async fn read_until_eof(&self) -> Vec<u8> {
read(&self.proxy).await.unwrap()
}
// Set the offset of the file relative to the start
pub async fn seek_from_start(&self, offset: u64) {
let (raw_status_code, _) = self.proxy.seek(offset as i64, SeekOrigin::Start).await.unwrap();
// TODO(fxbug.dev/58749): Bubble the status code up
Status::ok(raw_status_code).unwrap();
}
// Gracefully close the file by informing the filesystem
pub async fn close(self) {
io_util::file::close(self.proxy).await.unwrap();
}
}
impl Clone for Directory {
fn clone(&self) -> Self {
let new_proxy = clone_no_describe(&self.proxy, Some(CLONE_FLAG_SAME_RIGHTS)).unwrap();
Self { proxy: new_proxy }
}
}