]> git.proxmox.com Git - pxar.git/blame - src/accessor/sync.rs
doc update
[pxar.git] / src / accessor / sync.rs
CommitLineData
6cd4f635
WB
1//! Blocking `pxar` random access handling.
2
3use std::io;
ceb83806 4use std::ops::Range;
6cd4f635
WB
5use std::os::unix::fs::FileExt;
6use std::path::Path;
7use std::pin::Pin;
b764a2b1 8use std::sync::Arc;
6cd4f635
WB
9use std::task::{Context, Poll};
10
b764a2b1 11use crate::accessor::{self, cache::Cache, ReadAt};
6cd4f635 12use crate::decoder::Decoder;
06070d26 13use crate::format::GoodbyeItem;
6cd4f635
WB
14use crate::util::poll_result_once;
15use 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)]
26pub struct Accessor<T> {
27 inner: accessor::AccessorImpl<T>,
28}
29
29c17fc0 30impl<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
38impl 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
52impl<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
60impl<T> Accessor<FileRefReader<T>>
61where
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
71impl<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.
29c17fc0
WB
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 }
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
105impl<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.
34da7103 111 pub unsafe fn open_dir_at_end(&self, offset: u64) -> io::Result<Directory<T>> {
d3a83ee3
WB
112 Ok(Directory::new(poll_result_once(
113 self.inner.open_dir_at_end(offset),
114 )?))
ceb83806 115 }
6b9e2478
WB
116
117 /// Allow opening a regular file from a specified range.
06070d26
WB
118 pub unsafe fn open_file_at_range(
119 &self,
120 entry_range_info: &accessor::EntryRangeInfo,
121 ) -> io::Result<FileEntry<T>> {
6b9e2478 122 Ok(FileEntry {
06070d26 123 inner: poll_result_once(self.inner.open_file_at_range(entry_range_info))?,
6b9e2478
WB
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 }
6bfadb8a
WB
134
135 /// Following a hardlink.
06070d26 136 pub fn follow_hardlink(&self, entry: &FileEntry<T>) -> io::Result<FileEntry<T>> {
6bfadb8a 137 Ok(FileEntry {
06070d26 138 inner: poll_result_once(self.inner.follow_hardlink(&entry.inner))?,
6bfadb8a
WB
139 })
140 }
6cd4f635
WB
141}
142
143/// Adapter for FileExt readers.
29c17fc0 144#[derive(Clone)]
6cd4f635
WB
145pub struct FileReader<T> {
146 inner: T,
147}
148
149impl<T: FileExt> FileReader<T> {
150 pub fn new(inner: T) -> Self {
151 Self { inner }
152 }
153}
154
155impl<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
29c17fc0
WB
166/// Adapter for `Arc` or `Rc` to FileExt readers.
167#[derive(Clone)]
168pub struct FileRefReader<T: Clone> {
169 inner: T,
170}
171
172impl<T: Clone> FileRefReader<T> {
173 pub fn new(inner: T) -> Self {
174 Self { inner }
175 }
176}
177
178impl<T> ReadAt for FileRefReader<T>
179where
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
aabb78a4 193/// A pxar directory entry. This provdies blocking access to the contents of a directory.
6cd4f635 194#[repr(transparent)]
29c17fc0
WB
195pub struct Directory<T> {
196 inner: accessor::DirectoryImpl<T>,
6cd4f635
WB
197}
198
29c17fc0
WB
199impl<T: Clone + ReadAt> Directory<T> {
200 fn new(inner: accessor::DirectoryImpl<T>) -> Self {
6cd4f635
WB
201 Self { inner }
202 }
203
204 /// Get a decoder for the directory contents.
29c17fc0 205 pub fn decode_full(&self) -> io::Result<Decoder<accessor::SeqReadAtAdapter<T>>> {
6cd4f635
WB
206 Ok(Decoder::from_impl(poll_result_once(
207 self.inner.decode_full(),
208 )?))
209 }
210
a5922fbc
WB
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
4af15944
WB
220 /// Lookup an entry starting from this current directory.
221 ///
222 /// For convenience, this already resolves paths with multiple components.
29c17fc0 223 pub fn lookup<P: AsRef<Path>>(&self, path: P) -> io::Result<Option<FileEntry<T>>> {
6cd4f635
WB
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.
29c17fc0 232 pub fn read_dir<'a>(&'a self) -> ReadDir<'a, T> {
6cd4f635
WB
233 ReadDir {
234 inner: self.inner.read_dir(),
235 }
236 }
d3a83ee3
WB
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 }
6cd4f635
WB
243}
244
aabb78a4
WB
245/// A file entry in a direcotry, retrieved via the `lookup` method or from
246/// `DirEntry::decode_entry``.
6cd4f635 247#[repr(transparent)]
29c17fc0
WB
248pub struct FileEntry<T: Clone + ReadAt> {
249 inner: accessor::FileEntryImpl<T>,
6cd4f635
WB
250}
251
29c17fc0 252impl<T: Clone + ReadAt> FileEntry<T> {
aabb78a4 253 /// Get a handle to the subdirectory this file entry points to, if it is in fact a directory.
29c17fc0 254 pub fn enter_directory(&self) -> io::Result<Directory<T>> {
6cd4f635
WB
255 Ok(Directory::new(poll_result_once(
256 self.inner.enter_directory(),
257 )?))
258 }
259
6b9e2478
WB
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
98b894a9
WB
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
6cd4f635
WB
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 }
ceb83806
WB
281
282 /// Exposed for raw by-offset access methods (use with `open_dir_at_end`).
283 #[inline]
06070d26
WB
284 pub fn entry_range_info(&self) -> &accessor::EntryRangeInfo {
285 self.inner.entry_range_info()
ceb83806 286 }
6cd4f635
WB
287}
288
29c17fc0 289impl<T: Clone + ReadAt> std::ops::Deref for FileEntry<T> {
05005b14
WB
290 type Target = Entry;
291
292 fn deref(&self) -> &Self::Target {
293 self.entry()
294 }
295}
296
6cd4f635
WB
297/// An iterator over the contents of a `Directory`.
298#[repr(transparent)]
29c17fc0
WB
299pub struct ReadDir<'a, T> {
300 inner: accessor::ReadDirImpl<'a, T>,
6cd4f635
WB
301}
302
98b894a9
WB
303impl<'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
29c17fc0
WB
319impl<'a, T: Clone + ReadAt> Iterator for ReadDir<'a, T> {
320 type Item = io::Result<DirEntry<'a, T>>;
6cd4f635
WB
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
29c17fc0 331impl<'a, T: Clone + ReadAt> std::iter::FusedIterator for ReadDir<'a, T> {}
6cd4f635
WB
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)]
29c17fc0
WB
336pub struct DirEntry<'a, T: Clone + ReadAt> {
337 inner: accessor::DirEntryImpl<'a, T>,
6cd4f635
WB
338}
339
29c17fc0 340impl<'a, T: Clone + ReadAt> DirEntry<'a, T> {
aabb78a4 341 /// Get the current file name.
6cd4f635
WB
342 pub fn file_name(&self) -> &Path {
343 self.inner.file_name()
344 }
345
aabb78a4
WB
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 })
6cd4f635 352 }
ceb83806 353
ceb83806
WB
354 /// Exposed for raw by-offset access methods.
355 #[inline]
06070d26
WB
356 pub fn entry_range_info(&self) -> &accessor::EntryRangeInfo {
357 self.inner.entry_range_info()
ceb83806 358 }
6cd4f635 359}
98b894a9
WB
360
361/// A reader for file contents.
362pub struct FileContents<T> {
363 inner: accessor::FileContentsImpl<T>,
364 at: u64,
365}
366
367impl<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
375impl<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
385impl<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}