1 use crate::{sys, DirIter, Error, OpenMode, SecureFile, Session}; 2 3 /// A pending transaction used to group multiple file operations for efficiency. 4 /// 5 /// See the [crate-level documentation][crate] for information on how and why to 6 /// use transactions. 7 /// 8 /// # Panics 9 /// 10 /// * The [`Drop`] impl for `Transaction` panics unconditionally. Use 11 /// [`commit`][Self::commit] or [`discard`][Self::discard] to finalize the 12 /// transaction as appropriate. 13 #[derive(Debug)] 14 pub struct Transaction<'s> { 15 pub(crate) session: &'s mut Session, 16 } 17 18 impl Transaction<'_> { 19 /// Applies the changes in the current transaction to disk. 20 /// 21 /// If an error occurs, any pending changes in the transaction are lost. commit(self) -> Result<(), Error>22 pub fn commit(self) -> Result<(), Error> { 23 self.end_transaction(true) 24 } 25 26 /// Ends the current transaction without committing pending changes to disk. 27 /// 28 /// Any pending changes made as part of the transaction will be lost. discard(self) -> Result<(), Error>29 pub fn discard(self) -> Result<(), Error> { 30 self.end_transaction(false) 31 } 32 end_transaction(self, complete: bool) -> Result<(), Error>33 fn end_transaction(self, complete: bool) -> Result<(), Error> { 34 // SAFETY: FFI call to underlying C API. The raw session handle is guaranteed to 35 // be valid until the `Session` object is dropped, and so is valid at this 36 // point. 37 let result = Error::check_return_code(unsafe { 38 sys::storage_end_transaction(self.session.raw, complete) 39 }); 40 41 // Make sure `self` doesn't get dropped since the `Drop` impl also ends the 42 // transaction. 43 core::mem::forget(self); 44 45 result 46 } 47 48 /// Attempts to open a file at `name` with the options specified by `mode`. 49 /// 50 /// If `mode` specifies that a new file may be created or an existing file may 51 /// be truncated, any resulting file changes are included in the transaction and 52 /// will not be applied to disk until the transaction is committed. 53 /// 54 /// # Errors 55 /// 56 /// This function will return an error in the following situations, but is not 57 /// limited to just these cases: 58 /// 59 /// * If `mode` specifies `Open` or `TruncateExisting` and there is no existing 60 /// file. 61 /// * If `mode` specifies `CreateExclusive` and a file already exists. open_file(&mut self, name: &str, mode: OpenMode) -> Result<SecureFile, Error>62 pub fn open_file(&mut self, name: &str, mode: OpenMode) -> Result<SecureFile, Error> { 63 self.session.open_file_impl(name, mode, false) 64 } 65 66 /// Reads the entire contents of `file` into `buf`. 67 /// 68 /// Reads contents starting from the beginning of the file, regardless of the 69 /// current cursor position in `file`. Returns a slice of `buf` containing the 70 /// read data. 71 /// 72 /// If you only want to read up to `buf.len()` bytes of the file, regardless of 73 /// how large the whole file is, use [`read_at`](Self::read_at) instead. 74 /// 75 /// # Errors 76 /// 77 /// This function will return an error in the following situations, but is not 78 /// limited to just these cases: 79 /// 80 /// * [`crate::ErrorCode::NotEnoughBuffer`] if `buf` isn't large enough to contain the 81 /// full contents of the file. read_all<'buf>( &mut self, file: &SecureFile, buf: &'buf mut [u8], ) -> Result<&'buf [u8], Error>82 pub fn read_all<'buf>( 83 &mut self, 84 file: &SecureFile, 85 buf: &'buf mut [u8], 86 ) -> Result<&'buf [u8], Error> { 87 self.session.read_all(file, buf) 88 } 89 90 /// Reads the content of `file` starting at the specified offset. 91 /// 92 /// Reads contents starting from the given `offset` in bytes from the start of 93 /// the file. Reads up to `buf.len()` bytes from the file and writes them into 94 /// `buf`. If there isn't enough data after `offset` to fill `buf`, then `buf` 95 /// will only be partially filled. Returns a slice of `buf` that contains the 96 /// read data. 97 /// 98 /// # Errors 99 /// 100 /// This function will return an error in the following situations, but is not 101 /// limited to just these cases: 102 /// 103 /// * `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>104 pub fn read_at<'buf>( 105 &mut self, 106 file: &SecureFile, 107 offset: usize, 108 buf: &'buf mut [u8], 109 ) -> Result<&'buf [u8], Error> { 110 self.session.read_at(file, offset, buf) 111 } 112 113 /// Overwrites `file` with the contents of `buf`. 114 /// 115 /// Writes the contents of `buf` to the file starting from the beginning of the 116 /// file, regardless of the current cursor position in `file`. The file is then 117 /// truncated to fit the length of `buf` if the previous size was larger. write_all(&mut self, file: &mut SecureFile, buf: &[u8]) -> Result<(), Error>118 pub fn write_all(&mut self, file: &mut SecureFile, buf: &[u8]) -> Result<(), Error> { 119 self.session.write_all_impl(file, buf, false) 120 } 121 122 /// Writes to a file starting at the specified offset. 123 /// 124 /// Writes the contents of `buf` to the file starting at `offset` bytes from 125 /// the beginning of the file. If the file is not already long enough to fit 126 /// the new data, it will be extended automatically to fit. write_at( &mut self, file: &mut SecureFile, offset: usize, buf: &[u8], ) -> Result<(), Error>127 pub fn write_at( 128 &mut self, 129 file: &mut SecureFile, 130 offset: usize, 131 buf: &[u8], 132 ) -> Result<(), Error> { 133 self.session.write_at_impl(file, offset, buf, false) 134 } 135 136 /// Returns the size of the file in bytes. get_size(&mut self, file: &SecureFile) -> Result<usize, Error>137 pub fn get_size(&mut self, file: &SecureFile) -> Result<usize, Error> { 138 self.session.get_size(file) 139 } 140 141 /// Truncates or extends the underlying file, updating the size of the file to 142 /// become `size.` 143 /// 144 /// If `size` is less than the current file's size, then the file will be 145 /// shrunk. If it is greater than the current file's size, then the file will be 146 /// extended to `size` and have all of the intermediate data filled with 0s. set_size(&mut self, file: &mut SecureFile, size: usize) -> Result<(), Error>147 pub fn set_size(&mut self, file: &mut SecureFile, size: usize) -> Result<(), Error> { 148 self.session.set_size_impl(file, size, false) 149 } 150 151 /// Renames a file to a new name, replacing the original file if `to` already 152 /// exists. 153 /// 154 /// # Errors 155 /// 156 /// This function will return an error in the following situations, but is not 157 /// limited to just these cases: 158 /// 159 /// * `from` does not exist. 160 /// * `to` exists and cannot be overwritten. 161 /// * A handle to `from` is already open. rename(&mut self, from: &str, to: &str) -> Result<(), Error>162 pub fn rename(&mut self, from: &str, to: &str) -> Result<(), Error> { 163 self.session.rename_impl(from, to, false) 164 } 165 166 /// Removes a file from the filesystem. 167 /// 168 /// # Errors 169 /// 170 /// This function will return an error in the following situations, but is not 171 /// limited to just these cases: 172 /// 173 /// * `name` doesn't exist. 174 /// * `name` cannot be deleted because it is open as a file handle. remove(&mut self, name: &str) -> Result<(), Error>175 pub fn remove(&mut self, name: &str) -> Result<(), Error> { 176 self.session.remove_impl(name, false) 177 } 178 179 /// Returns an iterator that can be used to list the files in storage. 180 /// 181 /// The iterator will yield instances of [`Result<(String, FileState)>`], 182 /// where the contained `String` is the name of a file. 183 /// 184 /// # Errors 185 /// 186 /// This function or the returned iterator will yield an error in the 187 /// following situations, but is not limited to just these cases: 188 /// 189 /// * The session is closed (i.e. the `Session` object is dropped) while the 190 /// iterator is still in use. list_files(&mut self) -> Result<DirIter, Error>191 pub fn list_files(&mut self) -> Result<DirIter, Error> { 192 self.session.list_files() 193 } 194 } 195 196 impl Drop for Transaction<'_> { drop(&mut self)197 fn drop(&mut self) { 198 // SAFETY: FFI call to underlying C API. The raw session handle is guaranteed to 199 // be valid until the `Session` object is dropped, and so is valid at this 200 // point. 201 Error::check_return_code(unsafe { sys::storage_end_transaction(self.session.raw, false) }) 202 .expect("Error occurred while dropping an unfinished `Transaction`"); 203 } 204 } 205