]> git.proxmox.com Git - pxar.git/blob - src/accessor/sync.rs
doc update
[pxar.git] / src / accessor / sync.rs
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 }