• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! Access to the Trusty storage service.
18 //!
19 //! The secure storage service provides encrypted and tamper proof storage to
20 //! secure apps. All operations that modify the file system state are
21 //! transactional. Files can be opened, create or deleted by name (where the
22 //! name is local to the app). Open files support read, write, get-size and
23 //! set-size operations. There is currently no support for sparse files,
24 //! permissions, quotas or directory operations.
25 //!
26 //! Here's a quick example of how to start a session with the storage service
27 //! and perform basic filesystem operations:
28 //!
29 //!```
30 //! use storage::{Session, Port, OpenMode};
31 //!
32 //! let session = Session::new(Port::TamperDetect)?;
33 //!
34 //! // Write file contents to disk, creating a new
35 //! // file or overwriting an existing one.
36 //! session.write(
37 //!     "foo.txt",
38 //!     "Hello, world!".as_bytes()
39 //! )?;
40 //!
41 //! // Read contents of a file.
42 //! let contents = session.read("foo.txt")?;
43 //! println!("Contents of `bar.txt`: {}", contents);
44 //!
45 //! // Open a file handle in order to perform more
46 //! // advanced operations like setting the size.
47 //! let file = session.open_file(
48 //!     "foo.txt",
49 //!     OpenMode::Open,
50 //! )?;
51 //!
52 //! session.set_size(&file, 1024)?;
53 //! ```
54 //!
55 //! # Getting Started
56 //!
57 //! To interact with the storage service you first need to start a session. The
58 //! storage service listens for connections on multiple different ports, and
59 //! provides different filesystem properties based on which port you connect to.
60 //! See [`Port`] for the available ports and their properties, though
61 //! [`TamperDetect`][Port::TamperDetect] should be your default if you do not
62 //! specifically need the properties provided by other ports.
63 //!
64 //! ```
65 //! use storage::{Session, Port};
66 //!
67 //! let session = Session::new(Port::TamperDetect)?;
68 //! ```
69 //!
70 //! All filesystem operations are performed through the [`Session`] object, see
71 //! the methods on [`Session`] for a list of operations. All filesystem
72 //! operations return a `Result`. If no specific error conditions are documented
73 //! for an operation, general errors may still still occur e.g. if there is an
74 //! issue communicating with the service.
75 //!
76 //! # Using Transactions
77 //!
78 //! It's possible to group multiple file operations into a transaction in order
79 //! to commit or discard all changes as a group. To do so, use
80 //! [`Session::begin_transaction`] to get a [`Transaction`] object.
81 //! [`Transaction`] exposes the same file manipulation methods as [`Session`],
82 //! but any changes done through [`Transaction`] will not be applied
83 //! immediately. Instead, [`Transaction::commit`] and [`Transaction::discard`]
84 //! are used to either commit the pending changes to disk or discard the pending
85 //! changes, respectively.
86 //!
87 //! If a [`Transaction`] object is dropped without being explicitly committed or
88 //! discarded, it will panic with a message reminding you to manually finalize
89 //! the transaction.
90 //!
91 //! ```
92 //! let transaction = session.begin_transaction();
93 //!
94 //! let file = transaction.open_file("foo.txt", OpenMode::Create)?;
95 //! transaction.write_all(&file, "Hello, world!".as_bytes())?;
96 //!
97 //! transaction.commit()?;
98 //! ```
99 //!
100 //! The main reason to use transactions instead of the simpler API provided by
101 //! [`Session`] is that it allows multiple file operations to be committed
102 //! atomically in a single transaction. This can be important for apps that need
103 //! to ensure that files in secure storage do not end up in an invalid state. In
104 //! the future it may also allow for more efficient disk operations by allowing
105 //! the storage service to write changes in bulk, but that is not currently
106 //! implemented.
107 //!
108 //! For simple use cases that only need to read or write an entire file the
109 //! simpler [`Session`] file access APIs can be good enough, but for more
110 //! complex use cases use the transaction API instead.
111 //!
112 //! The storage API is also designed such that while a transaction is open all
113 //! file operations must go through that transaction until it is either
114 //! committed or discarded. This ensures that there's no way to accidentally
115 //! perform a file operation through `Session` that circumvents the atomicity
116 //! guarantees provided by the transaction.
117 //!
118 //! # Listing Files
119 //!
120 //! You can use [`Session::list_files`] and [`Transaction::list_files`] to list
121 //! the files in the current session's storage. Note that the storage service
122 //! does not currently support directories, and so it always lists the files at
123 //! the root directory. The iterator returned by these methods yields the name
124 //! of the file and its current state, i.e. if the file is fully committed to
125 //! disk or if it is being added/removed in the current transaction.
126 //!
127 //! ```
128 //! for entry in session.list_files().unwrap() {
129 //!     let (name, state) = entry.unwrap();
130 //!     println!("File {} is in state {:?}", name, state);
131 //! }
132 //! ```
133 
134 #![feature(allocator_api)]
135 
136 pub use crate::transaction::*;
137 pub use trusty_sys::Error as ErrorCode;
138 
139 use core::alloc::AllocError;
140 use core::ffi::c_void;
141 use core::mem::MaybeUninit;
142 use core::ptr;
143 use trusty_std::alloc::TryAllocFrom;
144 use trusty_std::ffi::{CString, FallibleCString, TryNewError};
145 use trusty_std::string::{FromUtf8Error, String};
146 use trusty_std::vec::Vec;
147 use trusty_sys::c_long;
148 
149 #[cfg(test)]
150 mod test;
151 mod transaction;
152 
153 #[allow(bad_style)]
154 #[allow(unused)]
155 // See: https://github.com/rust-lang/rust-bindgen/issues/1651
156 #[allow(deref_nullptr)]
157 mod sys {
158     #[cfg(test)]
159     use test::assert_eq;
160 
161     include!(env!("BINDGEN_INC_FILE"));
162 }
163 
164 /// An active connection to the storage service.
165 ///
166 /// The `Session` object manages the active connection to the storage service,
167 /// and is used to communicate with the service. The connection is automatically
168 /// closed when the `Session` object is dropped.
169 #[derive(Debug, PartialEq, Eq)]
170 pub struct Session {
171     raw: sys::storage_session_t,
172 }
173 
174 impl Session {
175     /// Opens a new connection to the storage service.
176     ///
177     /// If `wait_for_port` is true and the port is not yet open for connections,
178     /// then this method will block until the port is open and a connection can be
179     /// established. Otherwise it will return an error if the connection cannot be
180     /// established immediately.
181     ///
182     /// # Errors
183     ///
184     /// * Returns an error code if we fail to connect to the storage service.
185     /// * If `wait_for_port` is false, returns an error code if the port is not
186     ///   listening for connections.
new(port: Port, wait_for_port: bool) -> Result<Self, Error>187     pub fn new(port: Port, wait_for_port: bool) -> Result<Self, Error> {
188         use Port::*;
189 
190         // Convert the `port` enum to the corresponding C string expected by the C API.
191         let port = match port {
192             TamperDetect => sys::STORAGE_CLIENT_TD_PORT.as_ptr().cast(),
193             TamperDetectPersist => sys::STORAGE_CLIENT_TDP_PORT.as_ptr().cast(),
194             TamperDetectEarlyAccess => sys::STORAGE_CLIENT_TDEA_PORT.as_ptr().cast(),
195             TamperProof => sys::STORAGE_CLIENT_TP_PORT.as_ptr().cast(),
196 
197             #[cfg(test)]
198             TestPortNonExistent => b"com.android.trusty.storage.client.fake\0".as_ptr().cast(),
199         };
200 
201         let flags = if wait_for_port { trusty_sys::IPC_CONNECT_WAIT_FOR_PORT } else { 0 };
202 
203         // SAFETY: FFI call to underlying C API. Both inputs were constructed in this
204         // function and so are guaranteed to be safe for this call.
205         let handle = Error::check_non_negative(unsafe { trusty_sys::connect(port, flags) })?;
206 
207         Ok(Self { raw: handle.try_into().unwrap() })
208     }
209 
210     /// Drops the `Session` and closes the connection.
211     ///
212     /// The connection is closed automatically when the `Session` object is dropped,
213     /// but this method can be used if you need to explicitly close a session before
214     /// the `Session` object would normally go out of scope.
close(self)215     pub fn close(self) {
216         // NOTE: No logic needed here. We simply take ownership of `self` and then
217         // let the `Drop` impl handle closing the connection.
218     }
219 
220     /// Attempts to open a file at `name` with the options specified by `mode`.
221     ///
222     /// If `mode` specifies that a new file may be created or an existing file may
223     /// be truncated, any resulting file changes are immediately committed as a
224     /// transaction.
225     ///
226     /// # Errors
227     ///
228     /// This function will return an error in the following situations, but is not
229     /// limited to just these cases:
230     ///
231     /// * If `mode` specifies `Open` or `TruncateExisting` and there is no existing
232     ///   file.
233     /// * If `mode` specifies `CreateExclusive` and a file already exists.
open_file(&mut self, name: &str, mode: OpenMode) -> Result<SecureFile, Error>234     pub fn open_file(&mut self, name: &str, mode: OpenMode) -> Result<SecureFile, Error> {
235         self.open_file_impl(name, mode, true)
236     }
237 
238     // NOTE: We split the implementation of `open_file` to a separate method in
239     // order to hide the `complete` argument.
240     //
241     // When client code opens a file directly with `open_file` and a file is
242     // created, we need to immediately commit the transaction. But when going
243     // through the transaction API the individual write operations shouldn't be
244     // committed until the entire transaction is done. We internally provide the
245     // `complete` argument to support both of these cases, but want to avoid
246     // exposing that arg publicly so that client code can't manually trigger a
247     // transaction outside of the full transaction API.
open_file_impl( &mut self, name: &str, mode: OpenMode, complete: bool, ) -> Result<SecureFile, Error>248     fn open_file_impl(
249         &mut self,
250         name: &str,
251         mode: OpenMode,
252         complete: bool,
253     ) -> Result<SecureFile, Error> {
254         use OpenMode::*;
255 
256         // Convert `name` to a `CString` in order to add a null byte to the end.
257         let name = CString::try_new(name)?;
258 
259         // Construct the flags bitmask from selected mode.
260         let flags = match mode {
261             Open => 0,
262             Create => sys::STORAGE_FILE_OPEN_CREATE,
263             CreateExclusive => {
264                 sys::STORAGE_FILE_OPEN_CREATE | sys::STORAGE_FILE_OPEN_CREATE_EXCLUSIVE
265             }
266             TruncateExisting => sys::STORAGE_FILE_OPEN_TRUNCATE,
267             TruncateOrCreate => sys::STORAGE_FILE_OPEN_CREATE | sys::STORAGE_FILE_OPEN_TRUNCATE,
268         };
269 
270         // Convert `complete` into the corresponding flag value.
271         let ops_flags = if complete { sys::STORAGE_OP_COMPLETE } else { 0 };
272 
273         let mut file = MaybeUninit::uninit();
274 
275         // SAFETY: FFI call to underlying C API. Both inputs were constructed in this
276         // function and so are guaranteed to be safe for this call.
277         Error::check_return_code(unsafe {
278             sys::storage_open_file(self.raw, file.as_mut_ptr(), name.as_ptr(), flags, ops_flags)
279         })?;
280 
281         // SAFETY: We've checked the error code returned by `storage_open_file`, so
282         // at this point we know that the file was successfully opened and `file`
283         // was initialized.
284         let raw = unsafe { file.assume_init() };
285 
286         Ok(SecureFile { raw })
287     }
288 
289     /// Reads the contents of the file at `name`.
290     ///
291     /// Reads the contents of the file into `buf`, returning the part of `buf` that
292     /// contains the contents of the file.
293     ///
294     /// # Errors
295     ///
296     /// This function will return an error in the following situations, but is not
297     /// limited to just these cases:
298     ///
299     /// * `name` is not the path to a valid file.
300     /// * `buf` isn't large enough to contain the full contents of the file.
read<'buf>(&mut self, path: &str, buf: &'buf mut [u8]) -> Result<&'buf [u8], Error>301     pub fn read<'buf>(&mut self, path: &str, buf: &'buf mut [u8]) -> Result<&'buf [u8], Error> {
302         let file = self.open_file_impl(path, OpenMode::Open, false)?;
303         self.read_all(&file, buf)
304     }
305 
306     /// Writes the contents of `buf` to a file at `name`.
307     ///
308     /// If no file exists at `name` then a new one is created. If a file already
309     /// exists the length is truncated to fit the length of `buf` if the previous
310     /// size was larger.
311     ///
312     /// This operation implicitly creates its own transaction, so the file contents
313     /// are guaranteed to be written to disk if it completes successfully.
314     ///
315     /// # Errors
316     ///
317     /// This function will return an error in the following situations, but is not
318     /// limited to just these cases:
319     ///
320     /// * `name` refers to a file that is already open as a handle and therefor
321     ///   cannot be overwritten.
write(&mut self, path: &str, buf: &[u8]) -> Result<(), Error>322     pub fn write(&mut self, path: &str, buf: &[u8]) -> Result<(), Error> {
323         let mut file = self.open_file_impl(path, OpenMode::Create, false)?;
324         self.write_all(&mut file, buf)
325     }
326 
327     /// Reads the entire contents of `file` into `buf`.
328     ///
329     /// Reads contents starting from the beginning of the file, regardless of the
330     /// current cursor position in `file`. Returns a slice of `buf` containing the
331     /// read data.
332     ///
333     /// If you only want to read up to `buf.len()` bytes of the file, regardless of
334     /// how large the whole file is, use [`read_at`](Self::read_at) instead.
335     ///
336     /// # Errors
337     ///
338     /// This function will return an error in the following situations, but is not
339     /// limited to just these cases:
340     ///
341     /// * [`ErrorCode::NotEnoughBuffer`] if `buf` isn't large enough to contain the
342     ///   full contents of the file.
read_all<'buf>( &mut self, file: &SecureFile, buf: &'buf mut [u8], ) -> Result<&'buf [u8], Error>343     pub fn read_all<'buf>(
344         &mut self,
345         file: &SecureFile,
346         buf: &'buf mut [u8],
347     ) -> Result<&'buf [u8], Error> {
348         // Validate that `buf` is large enough to hold the full contents of the file
349         // since the underlying `read_at` call does not consider it an error if the
350         // buffer is smaller than the remaining file size.
351         let size = self.get_size(file)?;
352         if buf.len() < size {
353             return Err(Error::Code(ErrorCode::NotEnoughBuffer));
354         }
355 
356         self.read_at(file, 0, buf)
357     }
358 
359     /// Reads the content of `file` starting at the specified offset.
360     ///
361     /// Reads contents starting from the given `offset` in bytes from the start of
362     /// the file. Reads up to `buf.len()` bytes from the file and writes them into
363     /// `buf`. If there isn't enough data after `offset` to fill `buf`, then `buf`
364     /// will only be partially filled. Returns a slice of `buf` that contains the
365     /// read data.
366     ///
367     /// # Errors
368     ///
369     /// This function will return an error in the following situations, but is not
370     /// limited to just these cases:
371     ///
372     /// * `offset` is greater than the length of the file.
read_at<'buf>( &mut self, file: &SecureFile, offset: usize, buf: &'buf mut [u8], ) -> Result<&'buf [u8], Error>373     pub fn read_at<'buf>(
374         &mut self,
375         file: &SecureFile,
376         offset: usize,
377         buf: &'buf mut [u8],
378     ) -> Result<&'buf [u8], Error> {
379         // SAFETY: FFI call to underlying C API. The raw file handle is guaranteed to be
380         // valid until the `SecureFile` object is dropped, and so is valid at this
381         // point.
382         let bytes_read = Error::check_size(unsafe {
383             sys::storage_read(
384                 file.raw,
385                 offset.try_into().unwrap(),
386                 buf.as_mut_ptr() as *mut c_void,
387                 buf.len(),
388             )
389         })?;
390 
391         Ok(&buf[..bytes_read])
392     }
393 
394     /// Overwrites `file` with the contents of `buf`.
395     ///
396     /// Writes the contents of `buf` to the file starting from the beginning of the
397     /// file, regardless of the current cursor position in `file`. The file is then
398     /// truncated to fit the length of `buf` if the previous size was larger.
399     ///
400     /// This operation implicitly creates its own transaction, so the file contents
401     /// are guaranteed to be written to disk if it completes successfully.
write_all(&mut self, file: &mut SecureFile, buf: &[u8]) -> Result<(), Error>402     pub fn write_all(&mut self, file: &mut SecureFile, buf: &[u8]) -> Result<(), Error> {
403         self.write_all_impl(file, buf, true)
404     }
405 
406     // NOTE: We split the implementation of `write_all` to a separate method in
407     // order to hide the `complete` argument. See the comment on `open_file_impl`
408     // for additional context.
write_all_impl( &mut self, file: &mut SecureFile, buf: &[u8], complete: bool, ) -> Result<(), Error>409     fn write_all_impl(
410         &mut self,
411         file: &mut SecureFile,
412         buf: &[u8],
413         complete: bool,
414     ) -> Result<(), Error> {
415         // Set the file size to 0 so that after the write completes the file size will
416         // match the length of `buf`.
417         //
418         // SAFETY: FFI call to underlying C API. The raw file handle is guaranteed to be
419         // valid until the `SecureFile` object is dropped, and so is valid at this
420         // point.
421         Error::check_return_code(unsafe { sys::storage_set_file_size(file.raw, 0, 0) })?;
422 
423         self.write_at_impl(file, 0, buf, complete)
424     }
425 
426     /// Writes to a file starting at the specified offset.
427     ///
428     /// Writes the contents of `buf` to the file starting at `offset` bytes from
429     /// the beginning of the file. If the file is not already long enough to fit
430     /// the new data, it will be extended automatically to fit.
431     ///
432     /// This operation implicitly creates its own transaction, so the file
433     /// contents are guaranteed to be written to disk if it completes
434     /// successfully.
write_at( &mut self, file: &mut SecureFile, offset: usize, buf: &[u8], ) -> Result<(), Error>435     pub fn write_at(
436         &mut self,
437         file: &mut SecureFile,
438         offset: usize,
439         buf: &[u8],
440     ) -> Result<(), Error> {
441         self.write_at_impl(file, offset, buf, true)
442     }
443 
444     // NOTE: We split the implementation of `write_at` to a separate method in
445     // order to hide the `complete` argument. See the comment on `open_file_impl`
446     // for additional context.
write_at_impl( &mut self, file: &mut SecureFile, offset: usize, buf: &[u8], complete: bool, ) -> Result<(), Error>447     fn write_at_impl(
448         &mut self,
449         file: &mut SecureFile,
450         offset: usize,
451         buf: &[u8],
452         complete: bool,
453     ) -> Result<(), Error> {
454         // Convert `complete` into the corresponding flag value.
455         let ops_flags = if complete { sys::STORAGE_OP_COMPLETE } else { 0 };
456 
457         // Write to the file at `offset`.
458         //
459         // SAFETY: FFI call to the underlying C API. Invariants are same as noted above.
460         let bytes_written = Error::check_size(unsafe {
461             sys::storage_write(
462                 file.raw,
463                 offset.try_into().unwrap(),
464                 buf.as_ptr() as *const c_void,
465                 buf.len(),
466                 ops_flags,
467             )
468         })?;
469 
470         // Verify our assumption that the entire write will always succeed.
471         assert_eq!(
472             bytes_written,
473             buf.len(),
474             "File data was only partially written, tried to write {} bytes but only {} bytes written",
475             buf.len(),
476             bytes_written,
477         );
478 
479         Ok(())
480     }
481 
482     /// Returns the size of the file in bytes.
get_size(&mut self, file: &SecureFile) -> Result<usize, Error>483     pub fn get_size(&mut self, file: &SecureFile) -> Result<usize, Error> {
484         let mut size = 0;
485 
486         // SAFETY: FFI call to underlying C API. The raw file handle is guaranteed to be
487         // valid until the `SecureFile` object is dropped, and so is valid at this
488         // point.
489         Error::check_return_code(unsafe { sys::storage_get_file_size(file.raw, &mut size) })?;
490 
491         Ok(size as usize)
492     }
493 
494     /// Truncates or extends the underlying file, updating the size of the file to
495     /// become `size.`
496     ///
497     /// If `size` is less than the current file's size, then the file will be
498     /// shrunk. If it is greater than the current file's size, then the file will be
499     /// extended to `size` and have all of the intermediate data filled with 0s.
500     ///
501     /// This operation implicitly creates its own transaction, so the changes are
502     /// guaranteed to be written to disk if it completes successfully.
set_size(&mut self, file: &mut SecureFile, size: usize) -> Result<(), Error>503     pub fn set_size(&mut self, file: &mut SecureFile, size: usize) -> Result<(), Error> {
504         self.set_size_impl(file, size, true)
505     }
506 
507     // NOTE: We split the implementation of `set_size` to a separate method in
508     // order to hide the `complete` argument. See the comment on `open_file_impl`
509     // for additional context.
set_size_impl( &mut self, file: &mut SecureFile, size: usize, complete: bool, ) -> Result<(), Error>510     fn set_size_impl(
511         &mut self,
512         file: &mut SecureFile,
513         size: usize,
514         complete: bool,
515     ) -> Result<(), Error> {
516         // Convert `complete` into the corresponding flag value.
517         let ops_flags = if complete { sys::STORAGE_OP_COMPLETE } else { 0 };
518 
519         // SAFETY: FFI call to underlying C API. The raw file handle is guaranteed to be
520         // valid until the `SecureFile` object is dropped, and so is valid at this
521         // point.
522         Error::check_return_code(unsafe {
523             sys::storage_set_file_size(file.raw, size as u64, ops_flags)
524         })
525     }
526 
527     /// Renames a file to a new name, replacing the original file if `to` already
528     /// exists.
529     ///
530     /// This operation implicitly creates its own transaction, so the changes are
531     /// guaranteed to be written to disk if it completes successfully.
532     ///
533     /// # Errors
534     ///
535     /// This function will return an error in the following situations, but is not
536     /// limited to just these cases:
537     ///
538     /// * `from` does not exist.
539     /// * `to` exists and cannot be overwritten.
540     /// * A handle to `from` is already open.
rename(&mut self, from: &str, to: &str) -> Result<(), Error>541     pub fn rename(&mut self, from: &str, to: &str) -> Result<(), Error> {
542         self.rename_impl(from, to, true)
543     }
544 
545     // NOTE: We split the implementation of `rename` to a separate method in
546     // order to hide the `complete` argument. See the comment on `open_file_impl`
547     // for additional context.
rename_impl(&mut self, from: &str, to: &str, complete: bool) -> Result<(), Error>548     fn rename_impl(&mut self, from: &str, to: &str, complete: bool) -> Result<(), Error> {
549         // Convert `name` to a `CString` in order to add a null byte to the end.
550         let from = CString::try_new(from)?;
551         let to = CString::try_new(to)?;
552 
553         // Convert `complete` into the corresponding flag value.
554         let ops_flags = if complete { sys::STORAGE_OP_COMPLETE } else { 0 };
555 
556         // SAFETY: FFI call to underlying C API. The raw file handle is guaranteed to be
557         // valid until the `SecureFile` object is dropped, and so is valid at this
558         // point.
559         Error::check_return_code(unsafe {
560             sys::storage_move_file(
561                 self.raw,
562                 0,
563                 from.as_ptr(),
564                 to.as_ptr(),
565                 sys::STORAGE_FILE_MOVE_CREATE,
566                 ops_flags,
567             )
568         })
569     }
570 
571     /// Removes a file from the filesystem.
572     ///
573     /// This operation implicitly creates its own transaction, so the changes are
574     /// guaranteed to be written to disk if it completes successfully.
575     ///
576     /// # Errors
577     ///
578     /// This function will return an error in the following situations, but is not
579     /// limited to just these cases:
580     ///
581     /// * `name` doesn't exist.
582     /// * `name` cannot be deleted because it is open as a file handle.
remove(&mut self, name: &str) -> Result<(), Error>583     pub fn remove(&mut self, name: &str) -> Result<(), Error> {
584         self.remove_impl(name, true)
585     }
586 
587     // NOTE: We split the implementation of `remove` to a separate method in
588     // order to hide the `complete` argument. See the comment on `open_file_impl`
589     // for additional context.
remove_impl(&mut self, name: &str, complete: bool) -> Result<(), Error>590     fn remove_impl(&mut self, name: &str, complete: bool) -> Result<(), Error> {
591         // Convert `name` to a `CString` in order to add a null byte to the end.
592         let name = CString::try_new(name)?;
593 
594         // Convert `complete` into the corresponding flag value.
595         let ops_flags = if complete { sys::STORAGE_OP_COMPLETE } else { 0 };
596 
597         // SAFETY: FFI call to underlying C API. The raw file handle is guaranteed to be
598         // valid until the `SecureFile` object is dropped, and so is valid at this
599         // point.
600         Error::check_return_code(unsafe {
601             sys::storage_delete_file(self.raw, name.as_ptr(), ops_flags)
602         })
603     }
604 
605     /// Creates a new [`Transaction`] object.
606     ///
607     /// See the [crate-level documentation][crate] for information on how any
608     /// why to use transactions.
begin_transaction(&mut self) -> Transaction<'_>609     pub fn begin_transaction(&mut self) -> Transaction<'_> {
610         Transaction { session: self }
611     }
612 
613     /// Returns an iterator that can be used to list the files in storage.
614     ///
615     /// The iterator will yield instances of [`Result<(String, FileState)>`],
616     /// where the contained `String` is the name of a file.
617     ///
618     /// # Errors
619     ///
620     /// This function or the returned iterator will yield an error in the
621     /// following situations, but is not limited to just these cases:
622     ///
623     /// * The session is closed (i.e. the `Session` object is dropped) while the
624     ///   iterator is still in use.
list_files(&mut self) -> Result<DirIter, Error>625     pub fn list_files(&mut self) -> Result<DirIter, Error> {
626         DirIter::new(self)
627     }
628 }
629 
630 impl Drop for Session {
drop(&mut self)631     fn drop(&mut self) {
632         // SAFETY: The raw handle is guaranteed to be valid at this point because we
633         // only ever construct a `Session` with a valid handle, and we only close
634         // the session on drop.
635         unsafe {
636             sys::storage_close_session(self.raw);
637         }
638     }
639 }
640 
641 /// Handle to an open file.
642 ///
643 /// A file handle can be opened with [`Session::open_file`]. File operations
644 /// cannot be performed directly with the file handle. Instead, use methods on
645 /// [`Session`] like [`Session::read_all`] and [`Session::write_all`] to read
646 /// and write the contents of a file.
647 ///
648 /// The handle is automatically closed on drop.
649 #[derive(Debug)]
650 pub struct SecureFile {
651     raw: sys::file_handle_t,
652 }
653 
654 impl SecureFile {
655     /// Drops the `SecureFile` and closes the handle.
656     ///
657     /// The connection is closed automatically when the `SecureFile` object is
658     /// dropped, but this method can be used if you need to explicitly close a
659     /// handle before the `SecureFile` object would normally go out of scope.
close(self)660     pub fn close(self) {
661         // NOTE: No logic needed here. We simply take ownership of `self` and then
662         // let the `Drop` impl handle closing the handle.
663     }
664 }
665 
666 impl Drop for SecureFile {
drop(&mut self)667     fn drop(&mut self) {
668         // SAFETY: The raw handle is guaranteed to be valid at this point because we
669         // only ever construct a `SecureFile` with a valid handle, and we only close
670         // the file on drop.
671         unsafe {
672             sys::storage_close_file(self.raw);
673         }
674     }
675 }
676 
677 /// Common error type for file operations.
678 ///
679 /// Errors mostly originate from the storage service, but some error variants
680 /// are generated locally.
681 #[derive(Debug, PartialEq, Eq)]
682 pub enum Error {
683     /// An error code returned by the storage service.
684     ///
685     /// Check the contained [`trusty_sys::Error`] to determine the specific
686     /// error code that was returned.
687     Code(ErrorCode),
688 
689     // An error converting a string or path for FFI.
690     TryNew(TryNewError),
691 
692     Alloc(AllocError),
693 
694     Utf8(FromUtf8Error),
695 }
696 
697 impl Error {
698     /// Checks a return code and converts it to an `Error` if necessary.
699     ///
700     /// Returns a `Result` so that this method can be used with `?` in order to
701     /// quickly propagate errors returned from the storage service, e.g.:
702     ///
703     /// ```
704     /// Error::try_from_code(unsafe {
705     ///     sys::some_ffi_call()
706     /// })?;
707     /// ```
708     ///
709     /// Note that only a value of 0 is considered success, and all other values are
710     /// treated as an error. If positive values should not be considered errors, use
711     /// [`check_non_negative`](Self::check_non_negative). If the return code
712     /// represents a length or index that you want to handle as a `usize`, use
713     /// [`check_size`](Self::check_size).
check_return_code(code: impl Into<c_long>) -> Result<(), Self>714     fn check_return_code(code: impl Into<c_long>) -> Result<(), Self> {
715         let code = code.into();
716         if ErrorCode::is_err(code) {
717             return Err(Error::Code(ErrorCode::from(code)));
718         }
719 
720         Ok(())
721     }
722 
723     /// Checks a return code where positive values do not represent an error.
724     ///
725     /// This helper is for use with FFI functions that return a signed value where
726     /// negative values indicate an error but positive values have some non-error
727     /// meaning, e.g. a function that returns a handle value on success. The
728     /// original value is returned in the success case to allow calling code to
729     /// easily continue using it:
730     ///
731     /// ```
732     /// let handle = Error::check_non_negative(unsafe {
733     ///     sys::some_ffi_call()
734     /// })?;
735     ///
736     /// println!("Handle value: {}", handle);
737     /// ```
738     ///
739     /// For cases where the return value specifically represents a length or index
740     /// value that you then want to use in Rust code as a `usize`, use
741     /// [`check_size`](Self::check_size) instead.
check_non_negative<T: Into<c_long> + Copy>(code: T) -> Result<T, Self>742     fn check_non_negative<T: Into<c_long> + Copy>(code: T) -> Result<T, Self> {
743         let long_code = code.into();
744 
745         // NOTE: We directly check `code < 0` here instead of using `is_err` because
746         // `is_err` will also treat positive values as errors, whereas in this case
747         // positive values are the success case.
748         if long_code < 0 {
749             Err(Error::Code(ErrorCode::from(long_code)))
750         } else {
751             Ok(code)
752         }
753     }
754 
755     /// Checks a return code that may also represent a length or index value.
756     ///
757     /// This helper is for use with FFI functions that return a size value that may
758     /// be negative to indicate an error, i.e. when reading or writing a file's
759     /// contents. If `size` does not encode an error, it is converted to a `usize`
760     /// for further use in Rust code.
761     ///
762     /// ```
763     /// let len = Error::check_size(unsafe {
764     ///     sys::some_ffi_call()
765     /// })?;
766     ///
767     /// println!("Bytes written: {}", len);
768     /// ```
check_size(size: isize) -> Result<usize, Self>769     fn check_size(size: isize) -> Result<usize, Self> {
770         Self::check_non_negative(size as c_long)?;
771 
772         // We've confirmed that `size` is not negative, so it's safe to cast it to
773         // a `usize`.
774         Ok(size as usize)
775     }
776 }
777 
778 impl From<TryNewError> for Error {
from(from: TryNewError) -> Self779     fn from(from: TryNewError) -> Self {
780         Error::TryNew(from)
781     }
782 }
783 
784 impl From<AllocError> for Error {
from(from: AllocError) -> Self785     fn from(from: AllocError) -> Self {
786         Error::Alloc(from)
787     }
788 }
789 
790 impl From<FromUtf8Error> for Error {
from(from: FromUtf8Error) -> Self791     fn from(from: FromUtf8Error) -> Self {
792         Error::Utf8(from)
793     }
794 }
795 
796 /// The port to use when connecting to the storage service.
797 ///
798 /// The different ports provide different guarantees for how data is stored, and
799 /// so an appropriate port to connect to will need to be chosen based on the
800 /// needs of your client service.
801 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
802 pub enum Port {
803     /// Provides storage with tamper and rollback protection.
804     ///
805     /// This port is only available once the non-secure OS has booted. This port
806     /// should be used by most apps as it can offer more storage and better
807     /// performance than the other choices.
808     ///
809     /// Note, though, that this storage is not persistent across factory resets.
810     /// Data that requires persistence can use `TamperDetectPersist` instead.
811     TamperDetect,
812 
813     /// Provides storage that will be preserved during a normal device wipe.
814     ///
815     /// Also provides tamper and rollback protection, same as
816     /// [`TamperDetect`][Self::TamperDetect].
817     TamperDetectPersist,
818 
819     /// Provides access to storage before the non-secure OS has booted.
820     ///
821     /// Also provides tamper and rollback protection, same as
822     /// [`TamperDetect`][Self::TamperDetect]. This storage might also not be wiped
823     /// when device user data is wiped (i.e. during a factory reset), but that
824     /// property is not guaranteed.
825     TamperDetectEarlyAccess,
826 
827     /// Provides tamper-proof storage.
828     ///
829     /// Note that non-secure code can prevent read and write operations from
830     /// succeeding, but it cannot modify on-disk data.
831     TamperProof,
832 
833     // HACK: Fake port used to test how the library handles attempting to connect
834     // to a port that doesn't exist. All of the normal ports are active while
835     // running the test, so without this we don't have a way to specify an invalid
836     // port.
837     #[cfg(test)]
838     #[doc(hidden)]
839     TestPortNonExistent,
840 }
841 
842 /// Configuration for how opening a file should be handled.
843 ///
844 /// When you request to open a file handle, the storage service needs to know
845 /// how you want to answer the following questions:
846 ///
847 /// * If no file already exists should one be created?
848 /// * If a file already exists should it be opened, or should that be treated as
849 ///   an error?
850 /// * If an existing file may be opened should its contents be preserved, or
851 ///   should it be truncated so that it looks like a new file?
852 ///
853 /// The variants of this enum represent the valid ways to answer all of these
854 /// questions. Not all combinations of answers are represented because they
855 /// would be contradictory.
856 ///
857 /// The default mode is `Open`.
858 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
859 pub enum OpenMode {
860     /// Open an existing file.
861     ///
862     /// Generates an error if no file already exists.
863     Open,
864 
865     /// Create a new file if one does not already exist.
866     ///
867     /// If a file already exists the file is opened.
868     Create,
869 
870     /// Create a new file only if no file already exists.
871     ///
872     /// Generates an error if the file already exists.
873     CreateExclusive,
874 
875     /// Truncates the file and opens it as a new file.
876     ///
877     /// Generates an error if no file already exists.
878     TruncateExisting,
879 
880     /// Truncates the file and opens it as a new file.
881     ///
882     /// Creates a new file if no file already exists.
883     TruncateOrCreate,
884 }
885 
886 impl Default for OpenMode {
default() -> Self887     fn default() -> Self {
888         OpenMode::Open
889     }
890 }
891 
892 /// Iterator over the entries in a directory.
893 ///
894 /// This iterator is returned from [`Session::list_files`] and
895 /// [`Transaction::list_files`] and will yield instances of [`Result<(String,
896 /// FileState)>`]. If the iterator yields an error at any point during iteration,
897 /// subsequent calls to [`next`][Iterator::next] will always return `None`.
898 ///
899 /// # Errors
900 ///
901 /// Yields an error if the session was closed.
902 #[derive(Debug)]
903 pub struct DirIter {
904     session: sys::storage_session_t,
905     raw: *mut sys::storage_open_dir_state,
906     finished: bool,
907 }
908 
909 impl DirIter {
new(session: &mut Session) -> Result<Self, Error>910     fn new(session: &mut Session) -> Result<Self, Error> {
911         let mut dir_ptr = ptr::null_mut();
912 
913         // SAFETY: FFI call to underlying C API. The raw session handle is
914         // guaranteed to be valid until the `Session` object is dropped.
915         Error::check_return_code(unsafe {
916             sys::storage_open_dir(session.raw, ptr::null(), &mut dir_ptr)
917         })?;
918 
919         if dir_ptr.is_null() {
920             return Err(Error::Code(ErrorCode::Generic));
921         }
922 
923         Ok(DirIter { session: session.raw, raw: dir_ptr, finished: false })
924     }
925 
926     /// Attempts to get the next file item in the directory.
927     ///
928     /// This method provides the bulk of the logic for the `Iterator` impl for
929     /// `DirIter`, but returns a `Result<Option>` instead of an `Option<Result>` the way
930     /// the `next` method needs to. This allows us to use `?` within `try_next` to
931     /// propagate errors. The `Iterator::next` method can then simply do
932     /// `try_next().transpose()` to convert the result to the correct form for the
933     /// `Iterator` API.
try_next(&mut self) -> Result<Option<(String, FileState)>, Error>934     fn try_next(&mut self) -> Result<Option<(String, FileState)>, Error> {
935         let mut buf = [0; sys::STORAGE_MAX_NAME_LENGTH_BYTES as usize];
936         let mut flags = 0;
937 
938         // SAFETY: FFI call to underlying C API. The raw session handle is guaranteed to
939         // be valid while the `Session` object is alive, and the raw dir iter pointer is
940         // likewise guaranteed to be valid while the `DirIter` object is alive.
941         let bytes_read = Error::check_size(unsafe {
942             let code = sys::storage_read_dir(
943                 self.session,
944                 self.raw,
945                 &mut flags,
946                 buf.as_mut_ptr().cast(),
947                 buf.len(),
948             );
949 
950             code as isize
951         })?;
952 
953         // Convert the output `flags` value into the corresponding `FileState`, and
954         // handle the end of iteration as necessary.
955         let state =
956             match u32::from(flags) & sys::storage_file_list_flag_STORAGE_FILE_LIST_STATE_MASK {
957                 sys::storage_file_list_flag_STORAGE_FILE_LIST_COMMITTED => FileState::Committed,
958                 sys::storage_file_list_flag_STORAGE_FILE_LIST_ADDED => FileState::Added,
959                 sys::storage_file_list_flag_STORAGE_FILE_LIST_REMOVED => FileState::Removed,
960 
961                 sys::storage_file_list_flag_STORAGE_FILE_LIST_END => return Ok(None),
962 
963                 flag => {
964                     panic!("Unexpected flag returned from `storage_read_dir`: {:#x}", flag);
965                 }
966             };
967 
968         // Convert our temporary stack-allocated buffer into a `String` so that it can
969         // be returned to the caller.
970         //
971         // NOTE: `bytes_read` should only ever be 0 when indicating that we've finished
972         // enumerating the directory, in which case we should have already returned
973         // `None`. But we perform an extra check here to make sure we don't overflow
974         // when doing `bytes_read - 1` if there is a case where `bytes_read` can be 0.
975         let name = if bytes_read > 0 {
976             // NOTE: `bytes_read` will include the terminating nul byte at the end of the C
977             // string, so we subtract 1 when creating the `String` to exclude that
978             // terminating byte.
979             let buf = Vec::try_alloc_from(&buf[..bytes_read - 1])?;
980             String::from_utf8(buf)?
981         } else {
982             panic!("`storage_read_dir` returned file name length 0");
983         };
984 
985         Ok(Some((name, state)))
986     }
987 }
988 
989 impl Iterator for DirIter {
990     type Item = Result<(String, FileState), Error>;
991 
next(&mut self) -> Option<Self::Item>992     fn next(&mut self) -> Option<Self::Item> {
993         // Always return `None` if we should no longer continue enumerating entries.
994         if self.finished {
995             return None;
996         }
997 
998         let next = self.try_next().transpose();
999 
1000         // Once we yield `None` or an error once we want to effectively "fuse" the
1001         // iterator so that we only return `None` going forward. This avoids the
1002         // iterator getting stuck in a loop where it continues to yield errors forever
1003         // if the underlying iterator gets into an invalid state (e.g. if the session is
1004         // closed).
1005         self.finished = match &next {
1006             None | Some(Err(..)) => true,
1007             _ => false,
1008         };
1009 
1010         next
1011     }
1012 }
1013 
1014 impl Drop for DirIter {
drop(&mut self)1015     fn drop(&mut self) {
1016         // SAFETY: FFI call to underlying C API. The raw session handle is guaranteed to
1017         // be valid until the `Session` object is dropped, and the raw dir state pointer
1018         // is guaranteed to be valid until this point.
1019         unsafe { sys::storage_close_dir(self.session, self.raw) }
1020     }
1021 }
1022 
1023 /// The state of a file when listing directory entries.
1024 ///
1025 /// Part of the element yielded by [`DirIter`].
1026 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1027 pub enum FileState {
1028     /// The file is committed and has not been removed in a pending transaction.
1029     Committed,
1030 
1031     /// The file will be added by the current transaction.
1032     Added,
1033 
1034     /// The file will be removed by the current transaction.
1035     Removed,
1036 }
1037