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