]>
Commit | Line | Data |
---|---|---|
6cd4f635 WB |
1 | //! Blocking `pxar` random access handling. |
2 | ||
3 | use std::io; | |
4 | use std::os::unix::fs::FileExt; | |
5 | use std::path::Path; | |
6 | use std::pin::Pin; | |
b764a2b1 | 7 | use std::sync::Arc; |
e72062a9 | 8 | use std::task::Context; |
6cd4f635 | 9 | |
c13df752 | 10 | use crate::accessor::{self, cache::Cache, ContentRange, MaybeReady, ReadAt, ReadAtOperation}; |
6cd4f635 | 11 | use crate::decoder::Decoder; |
06070d26 | 12 | use crate::format::GoodbyeItem; |
6cd4f635 | 13 | use crate::util::poll_result_once; |
3ee98e10 | 14 | use crate::{Entry, PxarVariant}; |
6cd4f635 WB |
15 | |
16 | /// Blocking `pxar` random-access decoder. | |
17 | /// | |
18 | /// This is the blocking I/O version of the `pxar` accessor. This will *not* work with an | |
19 | /// asynchronous I/O object. I/O must always return `Poll::Ready`. | |
20 | /// | |
21 | /// Attempting to use a `Waker` from this context *will* `panic!` | |
22 | /// | |
23 | /// If you need to use asynchronous I/O, use `aio::Accessor`. | |
24 | #[repr(transparent)] | |
25 | pub struct Accessor<T> { | |
26 | inner: accessor::AccessorImpl<T>, | |
27 | } | |
28 | ||
29c17fc0 | 29 | impl<T: FileExt> Accessor<FileReader<T>> { |
6cd4f635 WB |
30 | /// Decode a `pxar` archive from a standard file implementing `FileExt`. |
31 | #[inline] | |
29c17fc0 | 32 | pub fn from_file_and_size(input: T, size: u64) -> io::Result<Self> { |
3ee98e10 | 33 | Accessor::new(PxarVariant::Unified(FileReader::new(input)), size) |
6cd4f635 WB |
34 | } |
35 | } | |
36 | ||
37 | impl Accessor<FileReader<std::fs::File>> { | |
38 | /// Decode a `pxar` archive from a regular `std::io::File` input. | |
39 | #[inline] | |
40 | pub fn from_file(input: std::fs::File) -> io::Result<Self> { | |
41 | let size = input.metadata()?.len(); | |
42 | Accessor::from_file_and_size(input, size) | |
43 | } | |
44 | ||
45 | /// Convenience shortcut for `File::open` followed by `Accessor::from_file`. | |
46 | pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> { | |
47 | Self::from_file(std::fs::File::open(path.as_ref())?) | |
48 | } | |
49 | } | |
50 | ||
29c17fc0 WB |
51 | impl<T: Clone + std::ops::Deref<Target = std::fs::File>> Accessor<FileRefReader<T>> { |
52 | /// Open an `Arc` or `Rc` of `File`. | |
53 | pub fn from_file_ref(input: T) -> io::Result<Self> { | |
54 | let size = input.deref().metadata()?.len(); | |
55 | Accessor::from_file_ref_and_size(input, size) | |
56 | } | |
57 | } | |
58 | ||
59 | impl<T> Accessor<FileRefReader<T>> | |
60 | where | |
61 | T: Clone + std::ops::Deref, | |
62 | T::Target: FileExt, | |
63 | { | |
64 | /// Open an `Arc` or `Rc` of `File`. | |
65 | pub fn from_file_ref_and_size(input: T, size: u64) -> io::Result<Accessor<FileRefReader<T>>> { | |
3ee98e10 | 66 | Accessor::new(PxarVariant::Unified(FileRefReader::new(input)), size) |
29c17fc0 WB |
67 | } |
68 | } | |
69 | ||
6cd4f635 WB |
70 | impl<T: ReadAt> Accessor<T> { |
71 | /// Create a *blocking* random-access decoder from an input implementing our internal read | |
72 | /// interface. | |
73 | /// | |
74 | /// Note that the `input`'s `SeqRead` implementation must always return `Poll::Ready` and is | |
75 | /// not allowed to use the `Waker`, as this will cause a `panic!`. | |
3ee98e10 | 76 | pub fn new(input: PxarVariant<T, (T, u64)>, size: u64) -> io::Result<Self> { |
6cd4f635 WB |
77 | Ok(Self { |
78 | inner: poll_result_once(accessor::AccessorImpl::new(input, size))?, | |
79 | }) | |
80 | } | |
81 | ||
82 | /// Open a directory handle to the root of the pxar archive. | |
9e7287ab | 83 | pub fn open_root_ref(&self) -> io::Result<Directory<&dyn ReadAt>> { |
29c17fc0 WB |
84 | Ok(Directory::new(poll_result_once( |
85 | self.inner.open_root_ref(), | |
86 | )?)) | |
87 | } | |
b764a2b1 | 88 | |
e5a2495e | 89 | /// Set a cache for the goodbye tables to reduce random disk access. |
b764a2b1 WB |
90 | pub fn set_goodbye_table_cache<C>(&mut self, cache: Option<C>) |
91 | where | |
92 | C: Cache<u64, [GoodbyeItem]> + Send + Sync + 'static, | |
93 | { | |
94 | self.inner | |
95 | .set_goodbye_table_cache(cache.map(|cache| Arc::new(cache) as _)) | |
96 | } | |
a2530fb7 WB |
97 | |
98 | /// Get the full archive size we're allowed to access. | |
99 | #[inline] | |
100 | pub fn size(&self) -> u64 { | |
101 | self.inner.size() | |
102 | } | |
29c17fc0 WB |
103 | } |
104 | ||
105 | impl<T: Clone + ReadAt> Accessor<T> { | |
e5a2495e | 106 | /// Open the "root" directory entry of this pxar archive. |
29c17fc0 | 107 | pub fn open_root(&self) -> io::Result<Directory<T>> { |
6cd4f635 WB |
108 | Ok(Directory::new(poll_result_once(self.inner.open_root())?)) |
109 | } | |
ceb83806 WB |
110 | |
111 | /// Allow opening a directory at a specified offset. | |
1b25fc08 WB |
112 | /// |
113 | /// # Safety | |
114 | /// | |
115 | /// This should only be used with offsets known to point to the end of a directory, otherwise | |
116 | /// this usually fails, unless the data otherwise happens to look like a valid directory. | |
34da7103 | 117 | pub unsafe fn open_dir_at_end(&self, offset: u64) -> io::Result<Directory<T>> { |
609e3a63 WB |
118 | Ok(Directory::new(poll_result_once(unsafe { |
119 | self.inner.open_dir_at_end(offset) | |
120 | })?)) | |
ceb83806 | 121 | } |
6b9e2478 WB |
122 | |
123 | /// Allow opening a regular file from a specified range. | |
1b25fc08 WB |
124 | /// |
125 | /// # Safety | |
126 | /// | |
127 | /// Should only be used with `entry_range_info`s originating from the same archive, otherwise | |
128 | /// the result will be undefined and likely fail (or contain unexpected data). | |
06070d26 WB |
129 | pub unsafe fn open_file_at_range( |
130 | &self, | |
131 | entry_range_info: &accessor::EntryRangeInfo, | |
132 | ) -> io::Result<FileEntry<T>> { | |
6b9e2478 | 133 | Ok(FileEntry { |
609e3a63 | 134 | inner: poll_result_once(unsafe { self.inner.open_file_at_range(entry_range_info) })?, |
6b9e2478 WB |
135 | }) |
136 | } | |
137 | ||
138 | /// Allow opening arbitrary contents from a specific range. | |
1b25fc08 WB |
139 | /// |
140 | /// # Safety | |
141 | /// | |
142 | /// This will provide a reader over an arbitrary range of the archive file, so unless this | |
143 | /// comes from a actual file entry data, the contents might not make much sense. | |
c13df752 CE |
144 | pub unsafe fn open_contents_at_range( |
145 | &self, | |
146 | range: &ContentRange, | |
147 | ) -> io::Result<FileContents<T>> { | |
148 | Ok(FileContents { | |
149 | inner: poll_result_once(self.inner.open_contents_at_range(range))?, | |
6b9e2478 | 150 | at: 0, |
c13df752 | 151 | }) |
6b9e2478 | 152 | } |
6bfadb8a WB |
153 | |
154 | /// Following a hardlink. | |
06070d26 | 155 | pub fn follow_hardlink(&self, entry: &FileEntry<T>) -> io::Result<FileEntry<T>> { |
6bfadb8a | 156 | Ok(FileEntry { |
06070d26 | 157 | inner: poll_result_once(self.inner.follow_hardlink(&entry.inner))?, |
6bfadb8a WB |
158 | }) |
159 | } | |
6cd4f635 WB |
160 | } |
161 | ||
e5a2495e | 162 | /// Adapter for `FileExt` readers to make it usable via the `ReadAt` trait. |
29c17fc0 | 163 | #[derive(Clone)] |
6cd4f635 WB |
164 | pub struct FileReader<T> { |
165 | inner: T, | |
166 | } | |
167 | ||
168 | impl<T: FileExt> FileReader<T> { | |
e5a2495e | 169 | /// Wrap a regular reader to access it via the `ReadAt` trait. |
6cd4f635 WB |
170 | pub fn new(inner: T) -> Self { |
171 | Self { inner } | |
172 | } | |
173 | } | |
174 | ||
175 | impl<T: FileExt> ReadAt for FileReader<T> { | |
e72062a9 WB |
176 | fn start_read_at<'a>( |
177 | self: Pin<&'a Self>, | |
6cd4f635 | 178 | _cx: &mut Context, |
e72062a9 | 179 | buf: &'a mut [u8], |
6cd4f635 | 180 | offset: u64, |
e72062a9 WB |
181 | ) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> { |
182 | MaybeReady::Ready(self.get_ref().inner.read_at(buf, offset)) | |
183 | } | |
184 | ||
185 | fn poll_complete<'a>( | |
186 | self: Pin<&'a Self>, | |
187 | _op: ReadAtOperation<'a>, | |
188 | ) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> { | |
189 | panic!("start_read_at on sync file returned Pending"); | |
6cd4f635 WB |
190 | } |
191 | } | |
192 | ||
e5a2495e | 193 | /// Adapter for `Arc` or `Rc` to `FileExt` readers to make it usable via the `ReadAt` trait. |
29c17fc0 WB |
194 | #[derive(Clone)] |
195 | pub struct FileRefReader<T: Clone> { | |
196 | inner: T, | |
197 | } | |
198 | ||
199 | impl<T: Clone> FileRefReader<T> { | |
e5a2495e | 200 | /// Wrap a reference to a `FileExt` reader. |
29c17fc0 WB |
201 | pub fn new(inner: T) -> Self { |
202 | Self { inner } | |
203 | } | |
204 | } | |
205 | ||
206 | impl<T> ReadAt for FileRefReader<T> | |
207 | where | |
208 | T: Clone + std::ops::Deref, | |
209 | T::Target: FileExt, | |
210 | { | |
e72062a9 WB |
211 | fn start_read_at<'a>( |
212 | self: Pin<&'a Self>, | |
29c17fc0 | 213 | _cx: &mut Context, |
e72062a9 | 214 | buf: &'a mut [u8], |
29c17fc0 | 215 | offset: u64, |
e72062a9 WB |
216 | ) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> { |
217 | MaybeReady::Ready(self.get_ref().inner.read_at(buf, offset)) | |
218 | } | |
219 | ||
220 | fn poll_complete<'a>( | |
221 | self: Pin<&'a Self>, | |
222 | _op: ReadAtOperation<'a>, | |
223 | ) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> { | |
224 | panic!("start_read_at on sync file returned Pending"); | |
29c17fc0 WB |
225 | } |
226 | } | |
227 | ||
aabb78a4 | 228 | /// A pxar directory entry. This provdies blocking access to the contents of a directory. |
6cd4f635 | 229 | #[repr(transparent)] |
29c17fc0 WB |
230 | pub struct Directory<T> { |
231 | inner: accessor::DirectoryImpl<T>, | |
6cd4f635 WB |
232 | } |
233 | ||
29c17fc0 WB |
234 | impl<T: Clone + ReadAt> Directory<T> { |
235 | fn new(inner: accessor::DirectoryImpl<T>) -> Self { | |
6cd4f635 WB |
236 | Self { inner } |
237 | } | |
238 | ||
239 | /// Get a decoder for the directory contents. | |
29c17fc0 | 240 | pub fn decode_full(&self) -> io::Result<Decoder<accessor::SeqReadAtAdapter<T>>> { |
6cd4f635 WB |
241 | Ok(Decoder::from_impl(poll_result_once( |
242 | self.inner.decode_full(), | |
243 | )?)) | |
244 | } | |
245 | ||
a5922fbc WB |
246 | /// Get a `FileEntry` referencing the directory itself. |
247 | /// | |
248 | /// Helper function for our fuse implementation. | |
249 | pub fn lookup_self(&self) -> io::Result<FileEntry<T>> { | |
250 | Ok(FileEntry { | |
251 | inner: poll_result_once(self.inner.lookup_self())?, | |
252 | }) | |
253 | } | |
254 | ||
4af15944 WB |
255 | /// Lookup an entry starting from this current directory. |
256 | /// | |
257 | /// For convenience, this already resolves paths with multiple components. | |
29c17fc0 | 258 | pub fn lookup<P: AsRef<Path>>(&self, path: P) -> io::Result<Option<FileEntry<T>>> { |
6cd4f635 WB |
259 | if let Some(file_entry) = poll_result_once(self.inner.lookup(path.as_ref()))? { |
260 | Ok(Some(FileEntry { inner: file_entry })) | |
261 | } else { | |
262 | Ok(None) | |
263 | } | |
264 | } | |
265 | ||
266 | /// Get an iterator over the directory's contents. | |
1b25fc08 | 267 | pub fn read_dir(&self) -> ReadDir<T> { |
6cd4f635 WB |
268 | ReadDir { |
269 | inner: self.inner.read_dir(), | |
270 | } | |
271 | } | |
d3a83ee3 WB |
272 | |
273 | /// Get the number of entries in this directory. | |
274 | #[inline] | |
275 | pub fn entry_count(&self) -> usize { | |
276 | self.inner.entry_count() | |
277 | } | |
6cd4f635 WB |
278 | } |
279 | ||
aabb78a4 WB |
280 | /// A file entry in a direcotry, retrieved via the `lookup` method or from |
281 | /// `DirEntry::decode_entry``. | |
6cd4f635 | 282 | #[repr(transparent)] |
29c17fc0 WB |
283 | pub struct FileEntry<T: Clone + ReadAt> { |
284 | inner: accessor::FileEntryImpl<T>, | |
6cd4f635 WB |
285 | } |
286 | ||
29c17fc0 | 287 | impl<T: Clone + ReadAt> FileEntry<T> { |
aabb78a4 | 288 | /// Get a handle to the subdirectory this file entry points to, if it is in fact a directory. |
29c17fc0 | 289 | pub fn enter_directory(&self) -> io::Result<Directory<T>> { |
6cd4f635 WB |
290 | Ok(Directory::new(poll_result_once( |
291 | self.inner.enter_directory(), | |
292 | )?)) | |
293 | } | |
294 | ||
6b9e2478 | 295 | /// For use with unsafe accessor methods. |
c13df752 | 296 | pub fn content_range(&self) -> io::Result<Option<ContentRange>> { |
6b9e2478 WB |
297 | self.inner.content_range() |
298 | } | |
299 | ||
e5a2495e | 300 | /// Get the file's contents. |
98b894a9 WB |
301 | pub fn contents(&self) -> io::Result<FileContents<T>> { |
302 | Ok(FileContents { | |
303 | inner: poll_result_once(self.inner.contents())?, | |
304 | at: 0, | |
305 | }) | |
306 | } | |
307 | ||
e5a2495e WB |
308 | /// Convenience shortcut for when only the metadata contained in the [`Entry`] struct is of |
309 | /// interest. | |
6cd4f635 WB |
310 | #[inline] |
311 | pub fn into_entry(self) -> Entry { | |
312 | self.inner.into_entry() | |
313 | } | |
314 | ||
e5a2495e | 315 | /// Access the contained [`Entry`] for metadata access. |
6cd4f635 WB |
316 | #[inline] |
317 | pub fn entry(&self) -> &Entry { | |
ceb4a546 | 318 | self.inner.entry() |
6cd4f635 | 319 | } |
ceb83806 WB |
320 | |
321 | /// Exposed for raw by-offset access methods (use with `open_dir_at_end`). | |
322 | #[inline] | |
06070d26 WB |
323 | pub fn entry_range_info(&self) -> &accessor::EntryRangeInfo { |
324 | self.inner.entry_range_info() | |
ceb83806 | 325 | } |
6cd4f635 WB |
326 | } |
327 | ||
29c17fc0 | 328 | impl<T: Clone + ReadAt> std::ops::Deref for FileEntry<T> { |
05005b14 WB |
329 | type Target = Entry; |
330 | ||
331 | fn deref(&self) -> &Self::Target { | |
332 | self.entry() | |
333 | } | |
334 | } | |
335 | ||
6cd4f635 WB |
336 | /// An iterator over the contents of a `Directory`. |
337 | #[repr(transparent)] | |
29c17fc0 WB |
338 | pub struct ReadDir<'a, T> { |
339 | inner: accessor::ReadDirImpl<'a, T>, | |
6cd4f635 WB |
340 | } |
341 | ||
98b894a9 WB |
342 | impl<'a, T: Clone + ReadAt> ReadDir<'a, T> { |
343 | /// Efficient alternative to `Iterator::skip`. | |
344 | #[inline] | |
345 | pub fn skip(self, n: usize) -> Self { | |
346 | Self { | |
347 | inner: self.inner.skip(n), | |
348 | } | |
349 | } | |
350 | ||
351 | /// Efficient alternative to `Iterator::count`. | |
352 | #[inline] | |
353 | pub fn count(self) -> usize { | |
354 | self.inner.count() | |
355 | } | |
356 | } | |
357 | ||
29c17fc0 WB |
358 | impl<'a, T: Clone + ReadAt> Iterator for ReadDir<'a, T> { |
359 | type Item = io::Result<DirEntry<'a, T>>; | |
6cd4f635 WB |
360 | |
361 | fn next(&mut self) -> Option<Self::Item> { | |
362 | match poll_result_once(self.inner.next()) { | |
363 | Ok(Some(inner)) => Some(Ok(DirEntry { inner })), | |
364 | Ok(None) => None, | |
365 | Err(err) => Some(Err(err)), | |
366 | } | |
367 | } | |
368 | } | |
369 | ||
29c17fc0 | 370 | impl<'a, T: Clone + ReadAt> std::iter::FusedIterator for ReadDir<'a, T> {} |
6cd4f635 WB |
371 | |
372 | /// A directory entry. When iterating through the contents of a directory we first get access to | |
373 | /// the file name. The remaining information can be decoded afterwards. | |
374 | #[repr(transparent)] | |
29c17fc0 WB |
375 | pub struct DirEntry<'a, T: Clone + ReadAt> { |
376 | inner: accessor::DirEntryImpl<'a, T>, | |
6cd4f635 WB |
377 | } |
378 | ||
29c17fc0 | 379 | impl<'a, T: Clone + ReadAt> DirEntry<'a, T> { |
aabb78a4 | 380 | /// Get the current file name. |
6cd4f635 WB |
381 | pub fn file_name(&self) -> &Path { |
382 | self.inner.file_name() | |
383 | } | |
384 | ||
aabb78a4 WB |
385 | /// Decode the entry. |
386 | /// | |
387 | /// When iterating over a directory, the file name is read separately from the file attributes, | |
388 | /// so only the file name is available here, while the attributes still need to be decoded. | |
389 | pub fn decode_entry(&self) -> io::Result<FileEntry<T>> { | |
390 | poll_result_once(self.inner.decode_entry()).map(|inner| FileEntry { inner }) | |
6cd4f635 | 391 | } |
ceb83806 | 392 | |
ceb83806 WB |
393 | /// Exposed for raw by-offset access methods. |
394 | #[inline] | |
06070d26 WB |
395 | pub fn entry_range_info(&self) -> &accessor::EntryRangeInfo { |
396 | self.inner.entry_range_info() | |
ceb83806 | 397 | } |
6cd4f635 | 398 | } |
98b894a9 WB |
399 | |
400 | /// A reader for file contents. | |
401 | pub struct FileContents<T> { | |
402 | inner: accessor::FileContentsImpl<T>, | |
403 | at: u64, | |
404 | } | |
405 | ||
406 | impl<T: Clone + ReadAt> io::Read for FileContents<T> { | |
407 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
408 | let got = poll_result_once(self.inner.read_at(buf, self.at))?; | |
409 | self.at += got as u64; | |
410 | Ok(got) | |
411 | } | |
412 | } | |
413 | ||
414 | impl<T: Clone + ReadAt> FileExt for FileContents<T> { | |
415 | fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { | |
416 | poll_result_once(self.inner.read_at(buf, offset)) | |
417 | } | |
418 | ||
419 | fn write_at(&self, _buf: &[u8], _offset: u64) -> io::Result<usize> { | |
420 | io_bail!("write_at on read-only file"); | |
421 | } | |
422 | } | |
423 | ||
424 | impl<T: Clone + ReadAt> ReadAt for FileContents<T> { | |
e72062a9 WB |
425 | fn start_read_at<'a>( |
426 | self: Pin<&'a Self>, | |
427 | cx: &mut Context, | |
428 | buf: &'a mut [u8], | |
98b894a9 | 429 | offset: u64, |
e72062a9 WB |
430 | ) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> { |
431 | match unsafe { self.map_unchecked(|this| &this.inner) }.start_read_at(cx, buf, offset) { | |
432 | MaybeReady::Ready(ready) => MaybeReady::Ready(ready), | |
433 | MaybeReady::Pending(_) => panic!("start_read_at on sync file returned Pending"), | |
434 | } | |
435 | } | |
436 | ||
437 | fn poll_complete<'a>( | |
438 | self: Pin<&'a Self>, | |
439 | _op: ReadAtOperation<'a>, | |
440 | ) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> { | |
441 | panic!("start_read_at on sync file returned Pending"); | |
98b894a9 WB |
442 | } |
443 | } |