]> git.proxmox.com Git - pxar.git/blame - src/accessor/sync.rs
fixup upload command
[pxar.git] / src / accessor / sync.rs
CommitLineData
6cd4f635
WB
1//! Blocking `pxar` random access handling.
2
3use std::io;
4use std::os::unix::fs::FileExt;
5use std::path::Path;
6use std::pin::Pin;
b764a2b1 7use std::sync::Arc;
e72062a9 8use std::task::Context;
6cd4f635 9
c13df752 10use crate::accessor::{self, cache::Cache, ContentRange, MaybeReady, ReadAt, ReadAtOperation};
6cd4f635 11use crate::decoder::Decoder;
06070d26 12use crate::format::GoodbyeItem;
6cd4f635 13use crate::util::poll_result_once;
3ee98e10 14use 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)]
25pub struct Accessor<T> {
26 inner: accessor::AccessorImpl<T>,
27}
28
29c17fc0 29impl<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
37impl 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
51impl<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
59impl<T> Accessor<FileRefReader<T>>
60where
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
70impl<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
105impl<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
164pub struct FileReader<T> {
165 inner: T,
166}
167
168impl<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
175impl<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)]
195pub struct FileRefReader<T: Clone> {
196 inner: T,
197}
198
199impl<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
206impl<T> ReadAt for FileRefReader<T>
207where
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
230pub struct Directory<T> {
231 inner: accessor::DirectoryImpl<T>,
6cd4f635
WB
232}
233
29c17fc0
WB
234impl<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
283pub struct FileEntry<T: Clone + ReadAt> {
284 inner: accessor::FileEntryImpl<T>,
6cd4f635
WB
285}
286
29c17fc0 287impl<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 328impl<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
338pub struct ReadDir<'a, T> {
339 inner: accessor::ReadDirImpl<'a, T>,
6cd4f635
WB
340}
341
98b894a9
WB
342impl<'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
358impl<'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 370impl<'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
375pub struct DirEntry<'a, T: Clone + ReadAt> {
376 inner: accessor::DirEntryImpl<'a, T>,
6cd4f635
WB
377}
378
29c17fc0 379impl<'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.
401pub struct FileContents<T> {
402 inner: accessor::FileContentsImpl<T>,
403 at: u64,
404}
405
406impl<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
414impl<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
424impl<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}