blob: 3c5f197fae1b56cd15543b8dde8798fee30616e9 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
//! Filesystem manipulation operations.
use crate::deps::{Key128bit, Mac128bit};
use crate::sgx_fs_inner as fs_imp;
use std::io::{self, Read, Seek, SeekFrom, Write};
use std::path::Path;
/// A reference to an open file on the filesystem.
///
/// An instance of a `File` can be read and/or written depending on what options
/// it was opened with. Files also implement [`Seek`] to alter the logical cursor
/// that the file contains internally.
///
/// Files are automatically closed when they go out of scope.
pub struct ProtectedFile {
inner: fs_imp::SgxFile,
}
/// Options and flags which can be used to configure how a file is opened.
///
/// This builder exposes the ability to configure how a ProtectedFile is opened and
/// what operations are permitted on the open file. The ProtectedFile::open and
/// ProtectedFile::create methods are aliases for commonly used options using this
/// builder.
///
#[derive(Clone, Debug)]
pub struct OpenOptions(fs_imp::OpenOptions);
impl ProtectedFile {
#[cfg(feature = "mesalock_sgx")]
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<ProtectedFile> {
OpenOptions::default().read(true).open(path.as_ref())
}
#[cfg(feature = "mesalock_sgx")]
pub fn create<P: AsRef<Path>>(path: P) -> io::Result<ProtectedFile> {
OpenOptions::default().write(true).open(path.as_ref())
}
pub fn open_ex<P: AsRef<Path>>(path: P, key: &Key128bit) -> io::Result<ProtectedFile> {
OpenOptions::default()
.read(true)
.open_ex(path.as_ref(), key)
}
pub fn append_ex<P: AsRef<Path>>(path: P, key: &Key128bit) -> io::Result<ProtectedFile> {
OpenOptions::default()
.append(true)
.open_ex(path.as_ref(), key)
}
pub fn create_ex<P: AsRef<Path>>(path: P, key: &Key128bit) -> io::Result<ProtectedFile> {
OpenOptions::default()
.write(true)
.open_ex(path.as_ref(), key)
}
pub fn is_eof(&self) -> bool {
self.inner.is_eof()
}
pub fn clearerr(&self) {
self.inner.clearerr()
}
pub fn clear_cache(&self) -> io::Result<()> {
self.inner.clear_cache()
}
pub fn current_meta_gmac(&self) -> io::Result<Mac128bit> {
let mut meta_gmac = Mac128bit::default();
self.inner.get_current_meta_gmac(&mut meta_gmac)?;
Ok(meta_gmac)
}
pub fn rename_meta<P: AsRef<Path>, Q: AsRef<Path>>(
&self,
old_name: P,
new_name: Q,
) -> io::Result<()> {
self.inner.rename_meta(old_name.as_ref(), new_name.as_ref())
}
pub fn read_at(&self, off: usize, dst: &mut [u8]) -> io::Result<usize> {
let pre = self.inner.seek(SeekFrom::Current(0))?;
self.inner.seek(SeekFrom::Start(off as u64))?;
let size = match self.inner.read(dst) {
Ok(size) => size,
Err(e) => {
self.inner.seek(SeekFrom::Start(pre))?;
return Err(e);
}
};
self.inner.seek(SeekFrom::Start(pre))?;
Ok(size)
}
}
impl Read for ProtectedFile {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
}
impl Write for ProtectedFile {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
impl Seek for ProtectedFile {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.inner.seek(pos)
}
}
impl<'a> Read for &'a ProtectedFile {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.read(buf)
}
}
impl<'a> Write for &'a ProtectedFile {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
impl<'a> Seek for &'a ProtectedFile {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.inner.seek(pos)
}
}
impl OpenOptions {
/// Sets the option for read access.
///
/// This option, when true, will indicate that the file should be
/// `read`-able if opened.
///
pub fn read(&mut self, read: bool) -> &mut OpenOptions {
self.0.read(read);
self
}
/// Sets the option for write access.
///
/// This option, when true, will indicate that the file should be
/// `write`-able if opened.
///
pub fn write(&mut self, write: bool) -> &mut OpenOptions {
self.0.write(write);
self
}
/// Sets the option for the append mode.
///
/// This option, when true, means that writes will append to a file instead
/// of overwriting previous contents.
/// Note that setting `.write(true).append(true)` has the same effect as
/// setting only `.append(true)`.
///
/// For most filesystems, the operating system guarantees that all writes are
/// atomic: no writes get mangled because another process writes at the same
/// time.
///
/// One maybe obvious note when using append-mode: make sure that all data
/// that belongs together is written to the file in one operation. This
/// can be done by concatenating strings before passing them to `write()`,
/// or using a buffered writer (with a buffer of adequate size),
/// and calling `flush()` when the message is complete.
///
/// If a file is opened with both read and append access, beware that after
/// opening, and after every write, the position for reading may be set at the
/// end of the file. So, before writing, save the current position (using
/// `seek(SeekFrom::Current(0))`, and restore it before the next read.
///
pub fn append(&mut self, append: bool) -> &mut OpenOptions {
self.0.append(append);
self
}
/// Sets the option for update a previous file.
pub fn update(&mut self, update: bool) -> &mut OpenOptions {
self.0.update(update);
self
}
/// Sets the option for binary a file.
pub fn binary(&mut self, binary: bool) -> &mut OpenOptions {
self.0.binary(binary);
self
}
/// Opens a file at `path` with the options specified by `self`.
pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<ProtectedFile> {
self._open(path.as_ref())
}
pub fn open_ex<P: AsRef<Path>>(&self, path: P, key: &Key128bit) -> io::Result<ProtectedFile> {
self._open_ex(path.as_ref(), key)
}
fn _open(&self, path: &Path) -> io::Result<ProtectedFile> {
let inner = fs_imp::SgxFile::open(path, &self.0)?;
Ok(ProtectedFile { inner })
}
fn _open_ex(&self, path: &Path, key: &Key128bit) -> io::Result<ProtectedFile> {
let inner = fs_imp::SgxFile::open_ex(path, &self.0, key)?;
Ok(ProtectedFile { inner })
}
}
impl Default for OpenOptions {
/// Creates a blank new set of options ready for configuration.
///
/// All options are initially set to `false`.
///
fn default() -> OpenOptions {
OpenOptions(fs_imp::OpenOptions::new())
}
}
pub fn remove_protected_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
fs_imp::remove(path.as_ref())
}
#[cfg(feature = "mesalock_sgx")]
pub fn export_auto_key<P: AsRef<Path>>(path: P) -> io::Result<Key128bit> {
fs_imp::export_auto_key(path.as_ref())
}
#[cfg(feature = "mesalock_sgx")]
pub fn import_auto_key<P: AsRef<Path>>(path: P, key: &Key128bit) -> io::Result<()> {
fs_imp::import_auto_key(path.as_ref(), key)
}
#[cfg(test)]
mod tests {
use crate::remove_protected_file;
use crate::ProtectedFile;
use rand_core::RngCore;
use std::io::{Read, Write};
#[test]
pub fn test_sgxfs() {
let key = [90u8; 16];
let mut write_data: [u8; 16] = [0; 16];
let mut read_data: [u8; 16] = [0; 16];
let write_size;
let read_size;
{
let mut rng = rdrand::RdRand::new().unwrap();
rng.fill_bytes(&mut write_data);
let opt = ProtectedFile::create_ex("sgx_file", &key);
assert_eq!(opt.is_ok(), true);
let mut file = opt.unwrap();
let result = file.write(&write_data);
assert_eq!(result.is_ok(), true);
write_size = result.unwrap();
}
{
let opt = ProtectedFile::open_ex("sgx_file", &key);
assert_eq!(opt.is_ok(), true);
let mut file = opt.unwrap();
let result = file.read(&mut read_data);
assert_eq!(result.is_ok(), true);
read_size = result.unwrap();
}
let result = remove_protected_file("sgx_file");
assert_eq!(result.is_ok(), true);
assert_eq!(write_data, read_data);
assert_eq!(write_size, read_size);
{
let opt = ProtectedFile::open_ex("/", &key);
assert_eq!(opt.is_err(), true);
let opt = ProtectedFile::open_ex(".", &key);
assert_eq!(opt.is_err(), true);
let opt = ProtectedFile::open_ex("..", &key);
assert_eq!(opt.is_err(), true);
let opt = ProtectedFile::open_ex("?", &key);
assert_eq!(opt.is_err(), true);
}
{
let opt = ProtectedFile::create_ex("/", &key);
assert_eq!(opt.is_err(), true);
}
{
let opt = ProtectedFile::create_ex("/proc/100", &key);
assert_eq!(opt.is_err(), true);
let opt = ProtectedFile::create_ex(".", &key);
assert_eq!(opt.is_err(), true);
let opt = ProtectedFile::create_ex("..", &key);
assert_eq!(opt.is_err(), true);
}
}
}