]>
Commit | Line | Data |
---|---|---|
1 | //! Blocking `pxar` random access handling. | |
2 | ||
3 | use std::io; | |
4 | use std::ops::Range; | |
5 | use std::os::unix::fs::FileExt; | |
6 | use std::path::Path; | |
7 | use std::pin::Pin; | |
8 | use std::sync::Arc; | |
9 | use std::task::{Context, Poll}; | |
10 | ||
11 | use crate::accessor::{self, cache::Cache, ReadAt}; | |
12 | use crate::decoder::Decoder; | |
13 | use crate::format::GoodbyeItem; | |
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 | ||
30 | impl<T: FileExt> Accessor<FileReader<T>> { | |
31 | /// Decode a `pxar` archive from a standard file implementing `FileExt`. | |
32 | #[inline] | |
33 | pub fn from_file_and_size(input: T, size: u64) -> io::Result<Self> { | |
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 | ||
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 | ||
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. | |
84 | pub fn open_root_ref<'a>(&'a self) -> io::Result<Directory<&'a dyn ReadAt>> { | |
85 | Ok(Directory::new(poll_result_once( | |
86 | self.inner.open_root_ref(), | |
87 | )?)) | |
88 | } | |
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 | } | |
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 | } | |
103 | } | |
104 | ||
105 | impl<T: Clone + ReadAt> Accessor<T> { | |
106 | pub fn open_root(&self) -> io::Result<Directory<T>> { | |
107 | Ok(Directory::new(poll_result_once(self.inner.open_root())?)) | |
108 | } | |
109 | ||
110 | /// Allow opening a directory at a specified offset. | |
111 | pub unsafe fn open_dir_at_end(&self, offset: u64) -> io::Result<Directory<T>> { | |
112 | Ok(Directory::new(poll_result_once( | |
113 | self.inner.open_dir_at_end(offset), | |
114 | )?)) | |
115 | } | |
116 | ||
117 | /// Allow opening a regular file from a specified range. | |
118 | pub unsafe fn open_file_at_range( | |
119 | &self, | |
120 | entry_range_info: &accessor::EntryRangeInfo, | |
121 | ) -> io::Result<FileEntry<T>> { | |
122 | Ok(FileEntry { | |
123 | inner: poll_result_once(self.inner.open_file_at_range(entry_range_info))?, | |
124 | }) | |
125 | } | |
126 | ||
127 | /// Allow opening arbitrary contents from a specific range. | |
128 | pub unsafe fn open_contents_at_range(&self, range: Range<u64>) -> FileContents<T> { | |
129 | FileContents { | |
130 | inner: self.inner.open_contents_at_range(range), | |
131 | at: 0, | |
132 | } | |
133 | } | |
134 | ||
135 | /// Following a hardlink. | |
136 | pub fn follow_hardlink(&self, entry: &FileEntry<T>) -> io::Result<FileEntry<T>> { | |
137 | Ok(FileEntry { | |
138 | inner: poll_result_once(self.inner.follow_hardlink(&entry.inner))?, | |
139 | }) | |
140 | } | |
141 | } | |
142 | ||
143 | /// Adapter for FileExt readers. | |
144 | #[derive(Clone)] | |
145 | pub struct FileReader<T> { | |
146 | inner: T, | |
147 | } | |
148 | ||
149 | impl<T: FileExt> FileReader<T> { | |
150 | pub fn new(inner: T) -> Self { | |
151 | Self { inner } | |
152 | } | |
153 | } | |
154 | ||
155 | impl<T: FileExt> ReadAt for FileReader<T> { | |
156 | fn poll_read_at( | |
157 | self: Pin<&Self>, | |
158 | _cx: &mut Context, | |
159 | buf: &mut [u8], | |
160 | offset: u64, | |
161 | ) -> Poll<io::Result<usize>> { | |
162 | Poll::Ready(self.get_ref().inner.read_at(buf, offset)) | |
163 | } | |
164 | } | |
165 | ||
166 | /// Adapter for `Arc` or `Rc` to FileExt readers. | |
167 | #[derive(Clone)] | |
168 | pub struct FileRefReader<T: Clone> { | |
169 | inner: T, | |
170 | } | |
171 | ||
172 | impl<T: Clone> FileRefReader<T> { | |
173 | pub fn new(inner: T) -> Self { | |
174 | Self { inner } | |
175 | } | |
176 | } | |
177 | ||
178 | impl<T> ReadAt for FileRefReader<T> | |
179 | where | |
180 | T: Clone + std::ops::Deref, | |
181 | T::Target: FileExt, | |
182 | { | |
183 | fn poll_read_at( | |
184 | self: Pin<&Self>, | |
185 | _cx: &mut Context, | |
186 | buf: &mut [u8], | |
187 | offset: u64, | |
188 | ) -> Poll<io::Result<usize>> { | |
189 | Poll::Ready(self.get_ref().inner.read_at(buf, offset)) | |
190 | } | |
191 | } | |
192 | ||
193 | /// A pxar directory entry. This provdies blocking access to the contents of a directory. | |
194 | #[repr(transparent)] | |
195 | pub struct Directory<T> { | |
196 | inner: accessor::DirectoryImpl<T>, | |
197 | } | |
198 | ||
199 | impl<T: Clone + ReadAt> Directory<T> { | |
200 | fn new(inner: accessor::DirectoryImpl<T>) -> Self { | |
201 | Self { inner } | |
202 | } | |
203 | ||
204 | /// Get a decoder for the directory contents. | |
205 | pub fn decode_full(&self) -> io::Result<Decoder<accessor::SeqReadAtAdapter<T>>> { | |
206 | Ok(Decoder::from_impl(poll_result_once( | |
207 | self.inner.decode_full(), | |
208 | )?)) | |
209 | } | |
210 | ||
211 | /// Get a `FileEntry` referencing the directory itself. | |
212 | /// | |
213 | /// Helper function for our fuse implementation. | |
214 | pub fn lookup_self(&self) -> io::Result<FileEntry<T>> { | |
215 | Ok(FileEntry { | |
216 | inner: poll_result_once(self.inner.lookup_self())?, | |
217 | }) | |
218 | } | |
219 | ||
220 | /// Lookup an entry starting from this current directory. | |
221 | /// | |
222 | /// For convenience, this already resolves paths with multiple components. | |
223 | pub fn lookup<P: AsRef<Path>>(&self, path: P) -> io::Result<Option<FileEntry<T>>> { | |
224 | if let Some(file_entry) = poll_result_once(self.inner.lookup(path.as_ref()))? { | |
225 | Ok(Some(FileEntry { inner: file_entry })) | |
226 | } else { | |
227 | Ok(None) | |
228 | } | |
229 | } | |
230 | ||
231 | /// Get an iterator over the directory's contents. | |
232 | pub fn read_dir<'a>(&'a self) -> ReadDir<'a, T> { | |
233 | ReadDir { | |
234 | inner: self.inner.read_dir(), | |
235 | } | |
236 | } | |
237 | ||
238 | /// Get the number of entries in this directory. | |
239 | #[inline] | |
240 | pub fn entry_count(&self) -> usize { | |
241 | self.inner.entry_count() | |
242 | } | |
243 | } | |
244 | ||
245 | /// A file entry in a direcotry, retrieved via the `lookup` method or from | |
246 | /// `DirEntry::decode_entry``. | |
247 | #[repr(transparent)] | |
248 | pub struct FileEntry<T: Clone + ReadAt> { | |
249 | inner: accessor::FileEntryImpl<T>, | |
250 | } | |
251 | ||
252 | impl<T: Clone + ReadAt> FileEntry<T> { | |
253 | /// Get a handle to the subdirectory this file entry points to, if it is in fact a directory. | |
254 | pub fn enter_directory(&self) -> io::Result<Directory<T>> { | |
255 | Ok(Directory::new(poll_result_once( | |
256 | self.inner.enter_directory(), | |
257 | )?)) | |
258 | } | |
259 | ||
260 | /// For use with unsafe accessor methods. | |
261 | pub fn content_range(&self) -> io::Result<Option<Range<u64>>> { | |
262 | self.inner.content_range() | |
263 | } | |
264 | ||
265 | pub fn contents(&self) -> io::Result<FileContents<T>> { | |
266 | Ok(FileContents { | |
267 | inner: poll_result_once(self.inner.contents())?, | |
268 | at: 0, | |
269 | }) | |
270 | } | |
271 | ||
272 | #[inline] | |
273 | pub fn into_entry(self) -> Entry { | |
274 | self.inner.into_entry() | |
275 | } | |
276 | ||
277 | #[inline] | |
278 | pub fn entry(&self) -> &Entry { | |
279 | &self.inner.entry() | |
280 | } | |
281 | ||
282 | /// Exposed for raw by-offset access methods (use with `open_dir_at_end`). | |
283 | #[inline] | |
284 | pub fn entry_range_info(&self) -> &accessor::EntryRangeInfo { | |
285 | self.inner.entry_range_info() | |
286 | } | |
287 | } | |
288 | ||
289 | impl<T: Clone + ReadAt> std::ops::Deref for FileEntry<T> { | |
290 | type Target = Entry; | |
291 | ||
292 | fn deref(&self) -> &Self::Target { | |
293 | self.entry() | |
294 | } | |
295 | } | |
296 | ||
297 | /// An iterator over the contents of a `Directory`. | |
298 | #[repr(transparent)] | |
299 | pub struct ReadDir<'a, T> { | |
300 | inner: accessor::ReadDirImpl<'a, T>, | |
301 | } | |
302 | ||
303 | impl<'a, T: Clone + ReadAt> ReadDir<'a, T> { | |
304 | /// Efficient alternative to `Iterator::skip`. | |
305 | #[inline] | |
306 | pub fn skip(self, n: usize) -> Self { | |
307 | Self { | |
308 | inner: self.inner.skip(n), | |
309 | } | |
310 | } | |
311 | ||
312 | /// Efficient alternative to `Iterator::count`. | |
313 | #[inline] | |
314 | pub fn count(self) -> usize { | |
315 | self.inner.count() | |
316 | } | |
317 | } | |
318 | ||
319 | impl<'a, T: Clone + ReadAt> Iterator for ReadDir<'a, T> { | |
320 | type Item = io::Result<DirEntry<'a, T>>; | |
321 | ||
322 | fn next(&mut self) -> Option<Self::Item> { | |
323 | match poll_result_once(self.inner.next()) { | |
324 | Ok(Some(inner)) => Some(Ok(DirEntry { inner })), | |
325 | Ok(None) => None, | |
326 | Err(err) => Some(Err(err)), | |
327 | } | |
328 | } | |
329 | } | |
330 | ||
331 | impl<'a, T: Clone + ReadAt> std::iter::FusedIterator for ReadDir<'a, T> {} | |
332 | ||
333 | /// A directory entry. When iterating through the contents of a directory we first get access to | |
334 | /// the file name. The remaining information can be decoded afterwards. | |
335 | #[repr(transparent)] | |
336 | pub struct DirEntry<'a, T: Clone + ReadAt> { | |
337 | inner: accessor::DirEntryImpl<'a, T>, | |
338 | } | |
339 | ||
340 | impl<'a, T: Clone + ReadAt> DirEntry<'a, T> { | |
341 | /// Get the current file name. | |
342 | pub fn file_name(&self) -> &Path { | |
343 | self.inner.file_name() | |
344 | } | |
345 | ||
346 | /// Decode the entry. | |
347 | /// | |
348 | /// When iterating over a directory, the file name is read separately from the file attributes, | |
349 | /// so only the file name is available here, while the attributes still need to be decoded. | |
350 | pub fn decode_entry(&self) -> io::Result<FileEntry<T>> { | |
351 | poll_result_once(self.inner.decode_entry()).map(|inner| FileEntry { inner }) | |
352 | } | |
353 | ||
354 | /// Exposed for raw by-offset access methods. | |
355 | #[inline] | |
356 | pub fn entry_range_info(&self) -> &accessor::EntryRangeInfo { | |
357 | self.inner.entry_range_info() | |
358 | } | |
359 | } | |
360 | ||
361 | /// A reader for file contents. | |
362 | pub struct FileContents<T> { | |
363 | inner: accessor::FileContentsImpl<T>, | |
364 | at: u64, | |
365 | } | |
366 | ||
367 | impl<T: Clone + ReadAt> io::Read for FileContents<T> { | |
368 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
369 | let got = poll_result_once(self.inner.read_at(buf, self.at))?; | |
370 | self.at += got as u64; | |
371 | Ok(got) | |
372 | } | |
373 | } | |
374 | ||
375 | impl<T: Clone + ReadAt> FileExt for FileContents<T> { | |
376 | fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { | |
377 | poll_result_once(self.inner.read_at(buf, offset)) | |
378 | } | |
379 | ||
380 | fn write_at(&self, _buf: &[u8], _offset: u64) -> io::Result<usize> { | |
381 | io_bail!("write_at on read-only file"); | |
382 | } | |
383 | } | |
384 | ||
385 | impl<T: Clone + ReadAt> ReadAt for FileContents<T> { | |
386 | fn poll_read_at( | |
387 | self: Pin<&Self>, | |
388 | _cx: &mut Context, | |
389 | buf: &mut [u8], | |
390 | offset: u64, | |
391 | ) -> Poll<io::Result<usize>> { | |
392 | Poll::Ready(poll_result_once(self.get_ref().inner.read_at(buf, offset))) | |
393 | } | |
394 | } |