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