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