]> git.proxmox.com Git - pxar.git/blame - src/encoder.rs
decoder: handle fifos and sockets properly
[pxar.git] / src / encoder.rs
CommitLineData
70acf637
WB
1//! The `pxar` encoder state machine.
2//!
3//! This is the implementation used by both the synchronous and async pxar wrappers.
4
5use std::io;
6use std::mem::{forget, size_of, size_of_val, take};
7use std::os::unix::ffi::OsStrExt;
8use std::path::Path;
9use std::pin::Pin;
10use std::task::{Context, Poll};
11
12use endian_trait::Endian;
13
749855a4 14use crate::binary_tree_array;
70acf637
WB
15use crate::decoder::SeqRead;
16use crate::format::{self, GoodbyeItem};
17use crate::poll_fn::poll_fn;
18use crate::Metadata;
19
54109840 20pub mod aio;
70acf637
WB
21pub mod sync;
22
23#[doc(inline)]
24pub use sync::Encoder;
25
26/// Sequential write interface used by the encoder's state machine.
27///
28/// This is our internal writer trait which is available for `std::io::Write` types in the
29/// synchronous wrapper and for both `tokio` and `future` `AsyncWrite` types in the asynchronous
30/// wrapper.
31pub trait SeqWrite {
32 fn poll_seq_write(
33 self: Pin<&mut Self>,
34 cx: &mut Context,
35 buf: &[u8],
36 ) -> Poll<io::Result<usize>>;
37
327e33f2 38 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>>;
05356884 39
327e33f2 40 fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>>;
05356884 41
70acf637
WB
42 /// While writing to a pxar archive we need to remember how much dat we've written to track some
43 /// offsets. Particularly items like the goodbye table need to be able to compute offsets to
44 /// further back in the archive.
45 fn poll_position(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<u64>>;
46
47 /// To avoid recursively borrowing each time we nest into a subdirectory we add this helper.
48 /// Otherwise starting a subdirectory will get a trait object pointing to `T`, nesting another
49 /// subdirectory in that would have a trait object pointing to the trait object, and so on.
50 fn as_trait_object(&mut self) -> &mut dyn SeqWrite
51 where
52 Self: Sized,
53 {
54 self as &mut dyn SeqWrite
55 }
56}
57
58/// Allow using trait objects for generics taking a `SeqWrite`.
59impl<'a> SeqWrite for &mut (dyn SeqWrite + 'a) {
60 fn poll_seq_write(
61 self: Pin<&mut Self>,
62 cx: &mut Context,
63 buf: &[u8],
64 ) -> Poll<io::Result<usize>> {
65 unsafe {
66 self.map_unchecked_mut(|this| &mut **this)
67 .poll_seq_write(cx, buf)
68 }
69 }
70
05356884
WB
71 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
72 unsafe { self.map_unchecked_mut(|this| &mut **this).poll_flush(cx) }
73 }
74
75 fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
76 unsafe { self.map_unchecked_mut(|this| &mut **this).poll_close(cx) }
77 }
78
70acf637
WB
79 fn poll_position(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<u64>> {
80 unsafe { self.map_unchecked_mut(|this| &mut **this).poll_position(cx) }
81 }
82
83 fn as_trait_object(&mut self) -> &mut dyn SeqWrite
84 where
85 Self: Sized,
86 {
87 &mut **self
88 }
89}
90
70acf637
WB
91impl<'a> dyn SeqWrite + 'a {
92 /// awaitable version of `poll_position`.
93 async fn position(&mut self) -> io::Result<u64> {
94 poll_fn(|cx| unsafe { Pin::new_unchecked(&mut *self).poll_position(cx) }).await
95 }
96
97 /// awaitable verison of `poll_seq_write`.
98 async fn seq_write(&mut self, buf: &[u8]) -> io::Result<usize> {
99 poll_fn(|cx| unsafe { Pin::new_unchecked(&mut *self).poll_seq_write(cx, buf) }).await
100 }
101
102 /// Write the entire contents of a buffer, handling short writes.
103 async fn seq_write_all(&mut self, mut buf: &[u8]) -> io::Result<()> {
104 while !buf.is_empty() {
105 let got = self.seq_write(buf).await?;
106 buf = &buf[got..];
107 }
108 Ok(())
109 }
110
111 /// Write an endian-swappable struct.
112 async fn seq_write_struct<E: Endian>(&mut self, data: E) -> io::Result<()> {
113 let data = data.to_le();
114 self.seq_write_all(unsafe {
115 std::slice::from_raw_parts(&data as *const E as *const u8, size_of_val(&data))
116 })
117 .await
118 }
119
120 /// Write a pxar entry.
121 async fn seq_write_pxar_entry(&mut self, htype: u64, data: &[u8]) -> io::Result<()> {
122 self.seq_write_struct(format::Header::with_content_size(htype, data.len() as u64))
123 .await?;
124 self.seq_write_all(data).await
125 }
126
127 /// Write a pxar entry terminated by an additional zero which is not contained in the provided
128 /// data buffer.
129 async fn seq_write_pxar_entry_zero(&mut self, htype: u64, data: &[u8]) -> io::Result<()> {
130 self.seq_write_struct(format::Header::with_content_size(
131 htype,
132 1 + data.len() as u64,
133 ))
134 .await?;
135 self.seq_write_all(data).await?;
136 self.seq_write_all(&[0u8]).await
137 }
138
139 /// Write a pxar entry consiting of an endian-swappable struct.
b5a471a3
WB
140 async fn seq_write_pxar_struct_entry<E: Endian>(
141 &mut self,
142 htype: u64,
143 data: E,
144 ) -> io::Result<()> {
70acf637 145 let data = data.to_le();
b5a471a3 146 self.seq_write_pxar_entry(htype, unsafe {
70acf637
WB
147 std::slice::from_raw_parts(&data as *const E as *const u8, size_of_val(&data))
148 })
149 .await
150 }
151}
152
59e6f679
WB
153/// Error conditions caused by wrong usage of this crate.
154#[derive(Clone, Copy, Debug, Eq, PartialEq)]
155pub enum EncodeError {
156 /// The user dropped a `File` without without finishing writing all of its contents.
157 ///
158 /// This is required because the payload lengths is written out at the begining and decoding
159 /// requires there to follow the right amount of data.
160 IncompleteFile,
161
162 /// The user dropped a directory without finalizing it.
163 ///
164 /// Finalizing is required to build the goodbye table at the end of a directory.
165 IncompleteDirectory,
166}
167
70acf637
WB
168#[derive(Default)]
169struct EncoderState {
170 /// Goodbye items for this directory, excluding the tail.
171 items: Vec<GoodbyeItem>,
172
59e6f679
WB
173 /// User caused error conditions.
174 encode_error: Option<EncodeError>,
70acf637
WB
175
176 /// Offset of this directory's ENTRY.
177 entry_offset: u64,
178
179 /// Offset to this directory's first FILENAME.
180 files_offset: u64,
181
182 /// If this is a subdirectory, this points to the this directory's FILENAME.
183 file_offset: Option<u64>,
749855a4
WB
184
185 /// If this is a subdirectory, this contains this directory's hash for the goodbye item.
186 file_hash: u64,
70acf637
WB
187}
188
59e6f679
WB
189impl EncoderState {
190 fn merge_error(&mut self, error: Option<EncodeError>) {
191 // one error is enough:
192 if self.encode_error.is_none() {
193 self.encode_error = error;
194 }
195 }
196
197 fn add_error(&mut self, error: EncodeError) {
198 self.merge_error(Some(error));
199 }
200}
201
70acf637
WB
202/// The encoder state machine implementation for a directory.
203///
204/// We use `async fn` to implement the encoder state machine so that we can easily plug in both
205/// synchronous or `async` I/O objects in as output.
206pub(crate) struct EncoderImpl<'a, T: SeqWrite + 'a> {
207 output: T,
208 state: EncoderState,
209 parent: Option<&'a mut EncoderState>,
210 finished: bool,
211}
212
213impl<'a, T: SeqWrite + 'a> Drop for EncoderImpl<'a, T> {
214 fn drop(&mut self) {
215 if let Some(ref mut parent) = self.parent {
216 // propagate errors:
59e6f679
WB
217 parent.merge_error(self.state.encode_error);
218 if !self.finished {
219 parent.add_error(EncodeError::IncompleteDirectory);
70acf637
WB
220 }
221 } else if !self.finished {
222 // FIXME: how do we deal with this?
223 eprintln!("Encoder dropped without finishing!");
224 }
225 }
226}
227
228impl<'a, T: SeqWrite + 'a> EncoderImpl<'a, T> {
229 pub async fn new(output: T, metadata: &Metadata) -> io::Result<EncoderImpl<'a, T>> {
230 if !metadata.is_dir() {
231 io_bail!("directory metadata must contain the directory mode flag");
232 }
233 let mut this = Self {
234 output,
235 state: EncoderState::default(),
236 parent: None,
237 finished: false,
238 };
239
240 this.encode_metadata(metadata).await?;
241 this.state.files_offset = (&mut this.output as &mut dyn SeqWrite).position().await?;
242
243 Ok(this)
244 }
245
246 fn check(&self) -> io::Result<()> {
59e6f679
WB
247 match self.state.encode_error {
248 Some(EncodeError::IncompleteFile) => io_bail!("incomplete file"),
249 Some(EncodeError::IncompleteDirectory) => io_bail!("directory not finalized"),
250 None => Ok(())
70acf637
WB
251 }
252 }
253
254 pub async fn create_file<'b>(
255 &'b mut self,
256 metadata: &Metadata,
257 file_name: &Path,
258 file_size: u64,
259 ) -> io::Result<FileImpl<'b>>
260 where
261 'a: 'b,
262 {
263 self.create_file_do(metadata, file_name.as_os_str().as_bytes(), file_size)
264 .await
265 }
266
267 async fn create_file_do<'b>(
268 &'b mut self,
269 metadata: &Metadata,
270 file_name: &[u8],
271 file_size: u64,
272 ) -> io::Result<FileImpl<'b>>
273 where
274 'a: 'b,
275 {
276 self.check()?;
277
278 let file_offset = (&mut self.output as &mut dyn SeqWrite).position().await?;
279 self.start_file_do(metadata, file_name).await?;
280
281 (&mut self.output as &mut dyn SeqWrite)
282 .seq_write_struct(format::Header::with_content_size(
283 format::PXAR_PAYLOAD,
284 file_size,
285 ))
286 .await?;
287
288 let payload_data_offset = (&mut self.output as &mut dyn SeqWrite).position().await?;
289
290 let meta_size = payload_data_offset - file_offset;
291
292 Ok(FileImpl {
293 output: &mut self.output,
294 goodbye_item: GoodbyeItem {
295 hash: format::hash_filename(file_name),
296 offset: file_offset,
297 size: file_size + meta_size,
298 },
299 remaining_size: file_size,
300 parent: &mut self.state,
301 })
302 }
303
304 pub async fn add_file(
305 &mut self,
306 metadata: &Metadata,
307 file_name: &Path,
308 file_size: u64,
309 content: &mut dyn SeqRead,
310 ) -> io::Result<()> {
311 let mut file = self.create_file(metadata, file_name, file_size).await?;
312 let mut buf = crate::util::vec_new(4096);
313 loop {
314 let got = content.seq_read(&mut buf).await?;
315 if got == 0 {
316 break;
317 } else {
318 file.write_all(&buf[..got]).await?;
319 }
320 }
321 Ok(())
322 }
323
2fb73e7b
WB
324 pub async fn add_symlink(
325 &mut self,
326 metadata: &Metadata,
327 file_name: &Path,
328 target: &Path,
0abc4121
WB
329 ) -> io::Result<()> {
330 self.add_link(metadata, file_name, target, format::PXAR_SYMLINK)
331 .await
332 }
333
334 pub async fn add_hardlink(
335 &mut self,
336 metadata: &Metadata,
337 file_name: &Path,
338 target: &Path,
339 ) -> io::Result<()> {
340 self.add_link(metadata, file_name, target, format::PXAR_HARDLINK)
341 .await
342 }
343
344 async fn add_link(
345 &mut self,
346 metadata: &Metadata,
347 file_name: &Path,
348 target: &Path,
349 htype: u64,
3fe26522
WB
350 ) -> io::Result<()> {
351 self.add_file_entry(metadata, file_name, htype, target.as_os_str().as_bytes())
352 .await
353 }
354
355 pub async fn add_device(
356 &mut self,
357 metadata: &Metadata,
358 file_name: &Path,
359 device: format::Device,
360 ) -> io::Result<()> {
361 let device = device.to_le();
362 let device = unsafe {
363 std::slice::from_raw_parts(
364 &device as *const format::Device as *const u8,
365 size_of::<format::Device>(),
366 )
367 };
368 self.add_file_entry(metadata, file_name, format::PXAR_DEVICE, device)
369 .await
370 }
371
372 async fn add_file_entry(
373 &mut self,
374 metadata: &Metadata,
375 file_name: &Path,
376 htype: u64,
377 entry_data: &[u8],
2fb73e7b 378 ) -> io::Result<()> {
05356884 379 self.check()?;
2fb73e7b
WB
380
381 let file_offset = (&mut self.output as &mut dyn SeqWrite).position().await?;
382
383 let file_name = file_name.as_os_str().as_bytes();
2fb73e7b
WB
384
385 self.start_file_do(metadata, file_name).await?;
386 (&mut self.output as &mut dyn SeqWrite)
3fe26522 387 .seq_write_pxar_entry_zero(htype, entry_data)
2fb73e7b
WB
388 .await?;
389
390 let end_offset = (&mut self.output as &mut dyn SeqWrite).position().await?;
391
392 self.state.items.push(GoodbyeItem {
393 hash: format::hash_filename(file_name),
394 offset: file_offset,
395 size: end_offset - file_offset,
396 });
397
398 Ok(())
399 }
400
70acf637
WB
401 /// Helper
402 #[inline]
403 async fn position(&mut self) -> io::Result<u64> {
404 (&mut self.output as &mut dyn SeqWrite).position().await
405 }
406
407 pub async fn create_directory<'b>(
408 &'b mut self,
409 file_name: &Path,
410 metadata: &Metadata,
411 ) -> io::Result<EncoderImpl<'b, &'b mut dyn SeqWrite>>
412 where
413 'a: 'b,
414 {
415 self.check()?;
416
417 if !metadata.is_dir() {
418 io_bail!("directory metadata must contain the directory mode flag");
419 }
420
421 let file_name = file_name.as_os_str().as_bytes();
749855a4 422 let file_hash = format::hash_filename(file_name);
70acf637
WB
423
424 let file_offset = self.position().await?;
425 self.encode_filename(file_name).await?;
426
427 let entry_offset = self.position().await?;
428 self.encode_metadata(&metadata).await?;
429
430 let files_offset = self.position().await?;
431
432 Ok(EncoderImpl {
433 output: self.output.as_trait_object(),
434 state: EncoderState {
435 entry_offset,
436 files_offset,
437 file_offset: Some(file_offset),
749855a4 438 file_hash: file_hash,
70acf637
WB
439 ..Default::default()
440 },
441 parent: Some(&mut self.state),
442 finished: false,
443 })
444 }
445
446 async fn start_file_do(&mut self, metadata: &Metadata, file_name: &[u8]) -> io::Result<()> {
447 self.encode_filename(file_name).await?;
448 self.encode_metadata(&metadata).await?;
449 Ok(())
450 }
451
452 async fn encode_metadata(&mut self, metadata: &Metadata) -> io::Result<()> {
453 (&mut self.output as &mut dyn SeqWrite)
b9122056 454 .seq_write_pxar_struct_entry(format::PXAR_ENTRY, metadata.stat.clone())
70acf637 455 .await?;
b5a471a3
WB
456
457 for xattr in &metadata.xattrs {
458 self.write_xattr(xattr).await?;
459 }
460
461 self.write_acls(&metadata.acl).await?;
462
463 if let Some(fcaps) = &metadata.fcaps {
464 self.write_file_capabilities(fcaps).await?;
465 }
466
467 if let Some(qpid) = &metadata.quota_project_id {
468 self.write_quota_project_id(qpid).await?;
469 }
470
471 Ok(())
472 }
473
474 async fn write_xattr(&mut self, xattr: &format::XAttr) -> io::Result<()> {
475 (&mut self.output as &mut dyn SeqWrite)
476 .seq_write_pxar_entry(format::PXAR_XATTR, &xattr.data)
477 .await
478 }
479
480 async fn write_acls(&mut self, acl: &crate::Acl) -> io::Result<()> {
481 for acl in &acl.users {
482 (&mut self.output as &mut dyn SeqWrite)
483 .seq_write_pxar_struct_entry(format::PXAR_ACL_USER, acl.clone())
484 .await?;
485 }
486
487 for acl in &acl.groups {
488 (&mut self.output as &mut dyn SeqWrite)
489 .seq_write_pxar_struct_entry(format::PXAR_ACL_GROUP, acl.clone())
490 .await?;
491 }
492
493 if let Some(acl) = &acl.group_obj {
494 (&mut self.output as &mut dyn SeqWrite)
495 .seq_write_pxar_struct_entry(format::PXAR_ACL_GROUP_OBJ, acl.clone())
496 .await?;
497 }
498
499 if let Some(acl) = &acl.default {
500 (&mut self.output as &mut dyn SeqWrite)
501 .seq_write_pxar_struct_entry(format::PXAR_ACL_DEFAULT, acl.clone())
502 .await?;
503 }
504
505 for acl in &acl.default_users {
506 (&mut self.output as &mut dyn SeqWrite)
507 .seq_write_pxar_struct_entry(format::PXAR_ACL_DEFAULT_USER, acl.clone())
508 .await?;
509 }
510
511 for acl in &acl.default_groups {
512 (&mut self.output as &mut dyn SeqWrite)
513 .seq_write_pxar_struct_entry(format::PXAR_ACL_DEFAULT_GROUP, acl.clone())
514 .await?;
515 }
516
70acf637
WB
517 Ok(())
518 }
519
b5a471a3
WB
520 async fn write_file_capabilities(&mut self, fcaps: &format::FCaps) -> io::Result<()> {
521 (&mut self.output as &mut dyn SeqWrite)
522 .seq_write_pxar_entry(format::PXAR_FCAPS, &fcaps.data)
523 .await
524 }
525
526 async fn write_quota_project_id(
527 &mut self,
528 quota_project_id: &format::QuotaProjectId,
529 ) -> io::Result<()> {
530 (&mut self.output as &mut dyn SeqWrite)
b9122056 531 .seq_write_pxar_struct_entry(format::PXAR_QUOTA_PROJID, quota_project_id.clone())
b5a471a3
WB
532 .await
533 }
534
70acf637
WB
535 async fn encode_filename(&mut self, file_name: &[u8]) -> io::Result<()> {
536 (&mut self.output as &mut dyn SeqWrite)
537 .seq_write_pxar_entry_zero(format::PXAR_FILENAME, file_name)
538 .await
539 }
540
541 pub async fn finish(mut self) -> io::Result<()> {
542 let tail_bytes = self.finish_goodbye_table().await?;
543 (&mut self.output as &mut dyn SeqWrite)
544 .seq_write_pxar_entry(format::PXAR_GOODBYE, &tail_bytes)
545 .await?;
749855a4
WB
546 if let Some(parent) = &mut self.parent {
547 let file_offset = self
548 .state
549 .file_offset
550 .expect("internal error: parent set but no file_offset?");
551
552 let end_offset = (&mut self.output as &mut dyn SeqWrite).position().await?;
553
554 parent.items.push(GoodbyeItem {
555 hash: self.state.file_hash,
556 offset: file_offset,
557 size: end_offset - file_offset,
558 });
559 }
70acf637
WB
560 self.finished = true;
561 Ok(())
562 }
563
564 async fn finish_goodbye_table(&mut self) -> io::Result<Vec<u8>> {
565 let goodbye_offset = (&mut self.output as &mut dyn SeqWrite).position().await?;
566
567 // "take" out the tail (to not leave an array of endian-swapped structs in `self`)
568 let mut tail = take(&mut self.state.items);
569 let tail_size = (tail.len() + 1) * size_of::<GoodbyeItem>();
570 let goodbye_size = tail_size as u64 + size_of::<format::Header>() as u64;
571
749855a4
WB
572 // sort, then create a BST
573 tail.sort_unstable_by(|a, b| a.hash.cmp(&b.hash));
70acf637 574
749855a4
WB
575 let mut bst = Vec::with_capacity(tail.len() + 1);
576 unsafe {
577 bst.set_len(tail.len());
70acf637 578 }
749855a4
WB
579 binary_tree_array::copy(tail.len(), |src, dest| {
580 let mut item = tail[src].clone();
581 // fixup the goodbye table offsets to be relative and with the right endianess
582 item.offset = goodbye_offset - item.offset;
583 unsafe {
584 std::ptr::write(&mut bst[dest], item.to_le());
585 }
586 });
587 drop(tail);
588
589 bst.push(
590 GoodbyeItem {
591 hash: format::PXAR_GOODBYE_TAIL_MARKER,
592 offset: goodbye_offset - self.state.entry_offset,
593 size: goodbye_size,
594 }
595 .to_le(),
596 );
70acf637
WB
597
598 // turn this into a byte vector since after endian-swapping we can no longer guarantee that
599 // the items make sense:
749855a4
WB
600 let data = bst.as_mut_ptr() as *mut u8;
601 let capacity = bst.capacity() * size_of::<GoodbyeItem>();
602 forget(bst);
70acf637
WB
603 Ok(unsafe { Vec::from_raw_parts(data, tail_size, capacity) })
604 }
605}
606
607/// Writer for a file object in a directory.
608pub struct FileImpl<'a> {
609 output: &'a mut dyn SeqWrite,
610
611 /// This file's `GoodbyeItem`. FIXME: We currently don't touch this, can we just push it
612 /// directly instead of on Drop of FileImpl?
613 goodbye_item: GoodbyeItem,
614
615 /// While writing data to this file, this is how much space we still have left, this must reach
616 /// exactly zero.
617 remaining_size: u64,
618
59e6f679 619 /// The directory containing this file. This is where we propagate the `IncompleteFile` error
70acf637
WB
620 /// to, and where we insert our `GoodbyeItem`.
621 parent: &'a mut EncoderState,
622}
623
624impl<'a> Drop for FileImpl<'a> {
625 fn drop(&mut self) {
626 if self.remaining_size != 0 {
59e6f679 627 self.parent.add_error(EncodeError::IncompleteFile);
70acf637
WB
628 }
629
630 self.parent.items.push(self.goodbye_item.clone());
631 }
632}
633
634impl<'a> FileImpl<'a> {
635 fn check_remaining(&self, size: usize) -> io::Result<()> {
636 if size as u64 > self.remaining_size {
637 io_bail!("attempted to write more than previously allocated");
638 } else {
639 Ok(())
640 }
641 }
642
05356884
WB
643 /// Poll write interface to more easily connect to tokio/futures.
644 #[cfg(any(feature = "tokio-io", feature = "futures-io"))]
645 pub fn poll_write(
646 self: Pin<&mut Self>,
647 cx: &mut Context,
648 data: &[u8],
649 ) -> Poll<io::Result<usize>> {
650 let this = self.get_mut();
651 this.check_remaining(data.len())?;
652 let output = unsafe { Pin::new_unchecked(&mut *this.output) };
653 match output.poll_seq_write(cx, data) {
654 Poll::Ready(Ok(put)) => {
655 this.remaining_size -= put as u64;
656 Poll::Ready(Ok(put))
657 }
658 other => other,
659 }
660 }
661
662 /// Poll flush interface to more easily connect to tokio/futures.
663 #[cfg(any(feature = "tokio-io", feature = "futures-io"))]
664 pub fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
665 unsafe {
327e33f2
WB
666 self.map_unchecked_mut(|this| &mut this.output)
667 .poll_flush(cx)
05356884
WB
668 }
669 }
670
671 /// Poll close/shutdown interface to more easily connect to tokio/futures.
672 #[cfg(any(feature = "tokio-io", feature = "futures-io"))]
673 pub fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
674 unsafe {
327e33f2
WB
675 self.map_unchecked_mut(|this| &mut this.output)
676 .poll_close(cx)
05356884
WB
677 }
678 }
679
70acf637
WB
680 /// Write file data for the current file entry in a pxar archive.
681 ///
682 /// This forwards to the output's `SeqWrite::poll_seq_write` and may write fewer bytes than
683 /// requested. Check the return value for how many. There's also a `write_all` method available
684 /// for convenience.
685 pub async fn write(&mut self, data: &[u8]) -> io::Result<usize> {
686 self.check_remaining(data.len())?;
687 let put = self.output.seq_write(data).await?;
688 self.remaining_size -= put as u64;
689 Ok(put)
690 }
691
692 /// Completely write file data for the current file entry in a pxar archive.
693 pub async fn write_all(&mut self, data: &[u8]) -> io::Result<()> {
694 self.check_remaining(data.len())?;
695 self.output.seq_write_all(data).await?;
696 self.remaining_size -= data.len() as u64;
697 Ok(())
698 }
699}
05356884
WB
700
701#[cfg(feature = "tokio-io")]
702impl<'a> tokio::io::AsyncWrite for FileImpl<'a> {
327e33f2 703 fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll<io::Result<usize>> {
05356884
WB
704 FileImpl::poll_write(self, cx, buf)
705 }
706
707 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
708 FileImpl::poll_flush(self, cx)
709 }
710
711 fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
712 FileImpl::poll_close(self, cx)
713 }
714}
715
716#[cfg(feature = "futures-io")]
717impl<'a> futures::io::AsyncWrite for FileImpl<'a> {
327e33f2 718 fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll<io::Result<usize>> {
05356884
WB
719 FileImpl::poll_write(self, cx, buf)
720 }
721
722 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
723 FileImpl::poll_flush(self, cx)
724 }
725
726 fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
727 FileImpl::poll_close(self, cx)
728 }
729}