1#[cfg(unix)]
2use std::os::unix::prelude::*;
3#[cfg(windows)]
4use std::os::windows::prelude::*;
5
6use std::borrow::Cow;
7use std::fmt;
8use std::fs;
9use std::io;
10use std::iter;
11use std::iter::repeat;
12use std::mem;
13use std::path::{Component, Path, PathBuf};
14use std::str;
15
16use crate::other;
17use crate::EntryType;
18
19/// Representation of the header of an entry in an archive
20#[repr(C)]
21#[allow(missing_docs)]
22pub struct Header {
23 bytes: [u8; 512],
24}
25
26/// Declares the information that should be included when filling a Header
27/// from filesystem metadata.
28#[derive(Clone, Copy, PartialEq, Eq, Debug)]
29#[non_exhaustive]
30pub enum HeaderMode {
31 /// All supported metadata, including mod/access times and ownership will
32 /// be included.
33 Complete,
34
35 /// Only metadata that is directly relevant to the identity of a file will
36 /// be included. In particular, ownership and mod/access times are excluded.
37 Deterministic,
38}
39
40/// Representation of the header of an entry in an archive
41#[repr(C)]
42#[allow(missing_docs)]
43pub struct OldHeader {
44 pub name: [u8; 100],
45 pub mode: [u8; 8],
46 pub uid: [u8; 8],
47 pub gid: [u8; 8],
48 pub size: [u8; 12],
49 pub mtime: [u8; 12],
50 pub cksum: [u8; 8],
51 pub linkflag: [u8; 1],
52 pub linkname: [u8; 100],
53 pub pad: [u8; 255],
54}
55
56/// Representation of the header of an entry in an archive
57#[repr(C)]
58#[allow(missing_docs)]
59pub struct UstarHeader {
60 pub name: [u8; 100],
61 pub mode: [u8; 8],
62 pub uid: [u8; 8],
63 pub gid: [u8; 8],
64 pub size: [u8; 12],
65 pub mtime: [u8; 12],
66 pub cksum: [u8; 8],
67 pub typeflag: [u8; 1],
68 pub linkname: [u8; 100],
69
70 // UStar format
71 pub magic: [u8; 6],
72 pub version: [u8; 2],
73 pub uname: [u8; 32],
74 pub gname: [u8; 32],
75 pub dev_major: [u8; 8],
76 pub dev_minor: [u8; 8],
77 pub prefix: [u8; 155],
78 pub pad: [u8; 12],
79}
80
81/// Representation of the header of an entry in an archive
82#[repr(C)]
83#[allow(missing_docs)]
84pub struct GnuHeader {
85 pub name: [u8; 100],
86 pub mode: [u8; 8],
87 pub uid: [u8; 8],
88 pub gid: [u8; 8],
89 pub size: [u8; 12],
90 pub mtime: [u8; 12],
91 pub cksum: [u8; 8],
92 pub typeflag: [u8; 1],
93 pub linkname: [u8; 100],
94
95 // GNU format
96 pub magic: [u8; 6],
97 pub version: [u8; 2],
98 pub uname: [u8; 32],
99 pub gname: [u8; 32],
100 pub dev_major: [u8; 8],
101 pub dev_minor: [u8; 8],
102 pub atime: [u8; 12],
103 pub ctime: [u8; 12],
104 pub offset: [u8; 12],
105 pub longnames: [u8; 4],
106 pub unused: [u8; 1],
107 pub sparse: [GnuSparseHeader; 4],
108 pub isextended: [u8; 1],
109 pub realsize: [u8; 12],
110 pub pad: [u8; 17],
111}
112
113/// Description of the header of a spare entry.
114///
115/// Specifies the offset/number of bytes of a chunk of data in octal.
116#[repr(C)]
117#[allow(missing_docs)]
118pub struct GnuSparseHeader {
119 pub offset: [u8; 12],
120 pub numbytes: [u8; 12],
121}
122
123/// Representation of the entry found to represent extended GNU sparse files.
124///
125/// When a `GnuHeader` has the `isextended` flag set to `1` then the contents of
126/// the next entry will be one of these headers.
127#[repr(C)]
128#[allow(missing_docs)]
129pub struct GnuExtSparseHeader {
130 pub sparse: [GnuSparseHeader; 21],
131 pub isextended: [u8; 1],
132 pub padding: [u8; 7],
133}
134
135impl Header {
136 /// Creates a new blank GNU header.
137 ///
138 /// The GNU style header is the default for this library and allows various
139 /// extensions such as long path names, long link names, and setting the
140 /// atime/ctime metadata attributes of files.
141 pub fn new_gnu() -> Header {
142 let mut header = Header { bytes: [0; 512] };
143 unsafe {
144 let gnu = cast_mut::<_, GnuHeader>(&mut header);
145 gnu.magic = *b"ustar ";
146 gnu.version = *b" \0";
147 }
148 header.set_mtime(0);
149 header
150 }
151
152 /// Creates a new blank UStar header.
153 ///
154 /// The UStar style header is an extension of the original archive header
155 /// which enables some extra metadata along with storing a longer (but not
156 /// too long) path name.
157 ///
158 /// UStar is also the basis used for pax archives.
159 pub fn new_ustar() -> Header {
160 let mut header = Header { bytes: [0; 512] };
161 unsafe {
162 let gnu = cast_mut::<_, UstarHeader>(&mut header);
163 gnu.magic = *b"ustar\0";
164 gnu.version = *b"00";
165 }
166 header.set_mtime(0);
167 header
168 }
169
170 /// Creates a new blank old header.
171 ///
172 /// This header format is the original archive header format which all other
173 /// versions are compatible with (e.g. they are a superset). This header
174 /// format limits the path name limit and isn't able to contain extra
175 /// metadata like atime/ctime.
176 pub fn new_old() -> Header {
177 let mut header = Header { bytes: [0; 512] };
178 header.set_mtime(0);
179 header
180 }
181
182 fn is_ustar(&self) -> bool {
183 let ustar = unsafe { cast::<_, UstarHeader>(self) };
184 ustar.magic[..] == b"ustar\0"[..] && ustar.version[..] == b"00"[..]
185 }
186
187 fn is_gnu(&self) -> bool {
188 let ustar = unsafe { cast::<_, UstarHeader>(self) };
189 ustar.magic[..] == b"ustar "[..] && ustar.version[..] == b" \0"[..]
190 }
191
192 /// View this archive header as a raw "old" archive header.
193 ///
194 /// This view will always succeed as all archive header formats will fill
195 /// out at least the fields specified in the old header format.
196 pub fn as_old(&self) -> &OldHeader {
197 unsafe { cast(self) }
198 }
199
200 /// Same as `as_old`, but the mutable version.
201 pub fn as_old_mut(&mut self) -> &mut OldHeader {
202 unsafe { cast_mut(self) }
203 }
204
205 /// View this archive header as a raw UStar archive header.
206 ///
207 /// The UStar format is an extension to the tar archive format which enables
208 /// longer pathnames and a few extra attributes such as the group and user
209 /// name.
210 ///
211 /// This cast may not succeed as this function will test whether the
212 /// magic/version fields of the UStar format have the appropriate values,
213 /// returning `None` if they aren't correct.
214 pub fn as_ustar(&self) -> Option<&UstarHeader> {
215 if self.is_ustar() {
216 Some(unsafe { cast(self) })
217 } else {
218 None
219 }
220 }
221
222 /// Same as `as_ustar_mut`, but the mutable version.
223 pub fn as_ustar_mut(&mut self) -> Option<&mut UstarHeader> {
224 if self.is_ustar() {
225 Some(unsafe { cast_mut(self) })
226 } else {
227 None
228 }
229 }
230
231 /// View this archive header as a raw GNU archive header.
232 ///
233 /// The GNU format is an extension to the tar archive format which enables
234 /// longer pathnames and a few extra attributes such as the group and user
235 /// name.
236 ///
237 /// This cast may not succeed as this function will test whether the
238 /// magic/version fields of the GNU format have the appropriate values,
239 /// returning `None` if they aren't correct.
240 pub fn as_gnu(&self) -> Option<&GnuHeader> {
241 if self.is_gnu() {
242 Some(unsafe { cast(self) })
243 } else {
244 None
245 }
246 }
247
248 /// Same as `as_gnu`, but the mutable version.
249 pub fn as_gnu_mut(&mut self) -> Option<&mut GnuHeader> {
250 if self.is_gnu() {
251 Some(unsafe { cast_mut(self) })
252 } else {
253 None
254 }
255 }
256
257 /// Treats the given byte slice as a header.
258 ///
259 /// Panics if the length of the passed slice is not equal to 512.
260 pub fn from_byte_slice(bytes: &[u8]) -> &Header {
261 assert_eq!(bytes.len(), mem::size_of::<Header>());
262 assert_eq!(mem::align_of_val(bytes), mem::align_of::<Header>());
263 unsafe { &*(bytes.as_ptr() as *const Header) }
264 }
265
266 /// Returns a view into this header as a byte array.
267 pub fn as_bytes(&self) -> &[u8; 512] {
268 &self.bytes
269 }
270
271 /// Returns a view into this header as a byte array.
272 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
273 &mut self.bytes
274 }
275
276 /// Blanket sets the metadata in this header from the metadata argument
277 /// provided.
278 ///
279 /// This is useful for initializing a `Header` from the OS's metadata from a
280 /// file. By default, this will use `HeaderMode::Complete` to include all
281 /// metadata.
282 pub fn set_metadata(&mut self, meta: &fs::Metadata) {
283 self.fill_from(meta, HeaderMode::Complete);
284 }
285
286 /// Sets only the metadata relevant to the given HeaderMode in this header
287 /// from the metadata argument provided.
288 pub fn set_metadata_in_mode(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
289 self.fill_from(meta, mode);
290 }
291
292 /// Returns the size of entry's data this header represents.
293 ///
294 /// This is different from `Header::size` for sparse files, which have
295 /// some longer `size()` but shorter `entry_size()`. The `entry_size()`
296 /// listed here should be the number of bytes in the archive this header
297 /// describes.
298 ///
299 /// May return an error if the field is corrupted.
300 pub fn entry_size(&self) -> io::Result<u64> {
301 num_field_wrapper_from(&self.as_old().size).map_err(|err| {
302 io::Error::new(
303 err.kind(),
304 format!("{} when getting size for {}", err, self.path_lossy()),
305 )
306 })
307 }
308
309 /// Returns the file size this header represents.
310 ///
311 /// May return an error if the field is corrupted.
312 pub fn size(&self) -> io::Result<u64> {
313 if self.entry_type().is_gnu_sparse() {
314 self.as_gnu()
315 .ok_or_else(|| other("sparse header was not a gnu header"))
316 .and_then(|h| h.real_size())
317 } else {
318 self.entry_size()
319 }
320 }
321
322 /// Encodes the `size` argument into the size field of this header.
323 pub fn set_size(&mut self, size: u64) {
324 num_field_wrapper_into(&mut self.as_old_mut().size, size);
325 }
326
327 /// Returns the raw path name stored in this header.
328 ///
329 /// This method may fail if the pathname is not valid Unicode and this is
330 /// called on a Windows platform.
331 ///
332 /// Note that this function will convert any `\` characters to directory
333 /// separators.
334 pub fn path(&self) -> io::Result<Cow<Path>> {
335 bytes2path(self.path_bytes())
336 }
337
338 /// Returns the pathname stored in this header as a byte array.
339 ///
340 /// This function is guaranteed to succeed, but you may wish to call the
341 /// `path` method to convert to a `Path`.
342 ///
343 /// Note that this function will convert any `\` characters to directory
344 /// separators.
345 pub fn path_bytes(&self) -> Cow<[u8]> {
346 if let Some(ustar) = self.as_ustar() {
347 ustar.path_bytes()
348 } else {
349 let name = truncate(&self.as_old().name);
350 Cow::Borrowed(name)
351 }
352 }
353
354 /// Gets the path in a "lossy" way, used for error reporting ONLY.
355 fn path_lossy(&self) -> String {
356 String::from_utf8_lossy(&self.path_bytes()).to_string()
357 }
358
359 /// Sets the path name for this header.
360 ///
361 /// This function will set the pathname listed in this header, encoding it
362 /// in the appropriate format. May fail if the path is too long or if the
363 /// path specified is not Unicode and this is a Windows platform. Will
364 /// strip out any "." path component, which signifies the current directory.
365 ///
366 /// Note: This function does not support names over 100 bytes, or paths
367 /// over 255 bytes, even for formats that support longer names. Instead,
368 /// use `Builder` methods to insert a long-name extension at the same time
369 /// as the file content.
370 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
371 self._set_path(p.as_ref())
372 }
373
374 fn _set_path(&mut self, path: &Path) -> io::Result<()> {
375 if let Some(ustar) = self.as_ustar_mut() {
376 return ustar.set_path(path);
377 }
378 copy_path_into(&mut self.as_old_mut().name, path, false).map_err(|err| {
379 io::Error::new(
380 err.kind(),
381 format!("{} when setting path for {}", err, self.path_lossy()),
382 )
383 })
384 }
385
386 /// Returns the link name stored in this header, if any is found.
387 ///
388 /// This method may fail if the pathname is not valid Unicode and this is
389 /// called on a Windows platform. `Ok(None)` being returned, however,
390 /// indicates that the link name was not present.
391 ///
392 /// Note that this function will convert any `\` characters to directory
393 /// separators.
394 pub fn link_name(&self) -> io::Result<Option<Cow<Path>>> {
395 match self.link_name_bytes() {
396 Some(bytes) => bytes2path(bytes).map(Some),
397 None => Ok(None),
398 }
399 }
400
401 /// Returns the link name stored in this header as a byte array, if any.
402 ///
403 /// This function is guaranteed to succeed, but you may wish to call the
404 /// `link_name` method to convert to a `Path`.
405 ///
406 /// Note that this function will convert any `\` characters to directory
407 /// separators.
408 pub fn link_name_bytes(&self) -> Option<Cow<[u8]>> {
409 let old = self.as_old();
410 if old.linkname[0] != 0 {
411 Some(Cow::Borrowed(truncate(&old.linkname)))
412 } else {
413 None
414 }
415 }
416
417 /// Sets the link name for this header.
418 ///
419 /// This function will set the linkname listed in this header, encoding it
420 /// in the appropriate format. May fail if the link name is too long or if
421 /// the path specified is not Unicode and this is a Windows platform. Will
422 /// strip out any "." path component, which signifies the current directory.
423 ///
424 /// To use GNU long link names, prefer instead [`crate::Builder::append_link`].
425 pub fn set_link_name<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
426 self._set_link_name(p.as_ref())
427 }
428
429 fn _set_link_name(&mut self, path: &Path) -> io::Result<()> {
430 copy_path_into(&mut self.as_old_mut().linkname, path, true).map_err(|err| {
431 io::Error::new(
432 err.kind(),
433 format!("{} when setting link name for {}", err, self.path_lossy()),
434 )
435 })
436 }
437
438 /// Sets the link name for this header without any transformation.
439 ///
440 /// This function is like [`Self::set_link_name`] but accepts an arbitrary byte array.
441 /// Hence it will not perform any canonicalization, such as replacing duplicate `//` with `/`.
442 pub fn set_link_name_literal<P: AsRef<[u8]>>(&mut self, p: P) -> io::Result<()> {
443 self._set_link_name_literal(p.as_ref())
444 }
445
446 fn _set_link_name_literal(&mut self, bytes: &[u8]) -> io::Result<()> {
447 copy_into(&mut self.as_old_mut().linkname, bytes)
448 }
449
450 /// Returns the mode bits for this file
451 ///
452 /// May return an error if the field is corrupted.
453 pub fn mode(&self) -> io::Result<u32> {
454 octal_from(&self.as_old().mode)
455 .map(|u| u as u32)
456 .map_err(|err| {
457 io::Error::new(
458 err.kind(),
459 format!("{} when getting mode for {}", err, self.path_lossy()),
460 )
461 })
462 }
463
464 /// Encodes the `mode` provided into this header.
465 pub fn set_mode(&mut self, mode: u32) {
466 octal_into(&mut self.as_old_mut().mode, mode);
467 }
468
469 /// Returns the value of the owner's user ID field
470 ///
471 /// May return an error if the field is corrupted.
472 pub fn uid(&self) -> io::Result<u64> {
473 num_field_wrapper_from(&self.as_old().uid)
474 .map(|u| u as u64)
475 .map_err(|err| {
476 io::Error::new(
477 err.kind(),
478 format!("{} when getting uid for {}", err, self.path_lossy()),
479 )
480 })
481 }
482
483 /// Encodes the `uid` provided into this header.
484 pub fn set_uid(&mut self, uid: u64) {
485 num_field_wrapper_into(&mut self.as_old_mut().uid, uid);
486 }
487
488 /// Returns the value of the group's user ID field
489 pub fn gid(&self) -> io::Result<u64> {
490 num_field_wrapper_from(&self.as_old().gid)
491 .map(|u| u as u64)
492 .map_err(|err| {
493 io::Error::new(
494 err.kind(),
495 format!("{} when getting gid for {}", err, self.path_lossy()),
496 )
497 })
498 }
499
500 /// Encodes the `gid` provided into this header.
501 pub fn set_gid(&mut self, gid: u64) {
502 num_field_wrapper_into(&mut self.as_old_mut().gid, gid);
503 }
504
505 /// Returns the last modification time in Unix time format
506 pub fn mtime(&self) -> io::Result<u64> {
507 num_field_wrapper_from(&self.as_old().mtime).map_err(|err| {
508 io::Error::new(
509 err.kind(),
510 format!("{} when getting mtime for {}", err, self.path_lossy()),
511 )
512 })
513 }
514
515 /// Encodes the `mtime` provided into this header.
516 ///
517 /// Note that this time is typically a number of seconds passed since
518 /// January 1, 1970.
519 pub fn set_mtime(&mut self, mtime: u64) {
520 num_field_wrapper_into(&mut self.as_old_mut().mtime, mtime);
521 }
522
523 /// Return the user name of the owner of this file.
524 ///
525 /// A return value of `Ok(Some(..))` indicates that the user name was
526 /// present and was valid utf-8, `Ok(None)` indicates that the user name is
527 /// not present in this archive format, and `Err` indicates that the user
528 /// name was present but was not valid utf-8.
529 pub fn username(&self) -> Result<Option<&str>, str::Utf8Error> {
530 match self.username_bytes() {
531 Some(bytes) => str::from_utf8(bytes).map(Some),
532 None => Ok(None),
533 }
534 }
535
536 /// Returns the user name of the owner of this file, if present.
537 ///
538 /// A return value of `None` indicates that the user name is not present in
539 /// this header format.
540 pub fn username_bytes(&self) -> Option<&[u8]> {
541 if let Some(ustar) = self.as_ustar() {
542 Some(ustar.username_bytes())
543 } else if let Some(gnu) = self.as_gnu() {
544 Some(gnu.username_bytes())
545 } else {
546 None
547 }
548 }
549
550 /// Sets the username inside this header.
551 ///
552 /// This function will return an error if this header format cannot encode a
553 /// user name or the name is too long.
554 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
555 if let Some(ustar) = self.as_ustar_mut() {
556 return ustar.set_username(name);
557 }
558 if let Some(gnu) = self.as_gnu_mut() {
559 gnu.set_username(name)
560 } else {
561 Err(other("not a ustar or gnu archive, cannot set username"))
562 }
563 }
564
565 /// Return the group name of the owner of this file.
566 ///
567 /// A return value of `Ok(Some(..))` indicates that the group name was
568 /// present and was valid utf-8, `Ok(None)` indicates that the group name is
569 /// not present in this archive format, and `Err` indicates that the group
570 /// name was present but was not valid utf-8.
571 pub fn groupname(&self) -> Result<Option<&str>, str::Utf8Error> {
572 match self.groupname_bytes() {
573 Some(bytes) => str::from_utf8(bytes).map(Some),
574 None => Ok(None),
575 }
576 }
577
578 /// Returns the group name of the owner of this file, if present.
579 ///
580 /// A return value of `None` indicates that the group name is not present in
581 /// this header format.
582 pub fn groupname_bytes(&self) -> Option<&[u8]> {
583 if let Some(ustar) = self.as_ustar() {
584 Some(ustar.groupname_bytes())
585 } else if let Some(gnu) = self.as_gnu() {
586 Some(gnu.groupname_bytes())
587 } else {
588 None
589 }
590 }
591
592 /// Sets the group name inside this header.
593 ///
594 /// This function will return an error if this header format cannot encode a
595 /// group name or the name is too long.
596 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
597 if let Some(ustar) = self.as_ustar_mut() {
598 return ustar.set_groupname(name);
599 }
600 if let Some(gnu) = self.as_gnu_mut() {
601 gnu.set_groupname(name)
602 } else {
603 Err(other("not a ustar or gnu archive, cannot set groupname"))
604 }
605 }
606
607 /// Returns the device major number, if present.
608 ///
609 /// This field may not be present in all archives, and it may not be
610 /// correctly formed in all archives. `Ok(Some(..))` means it was present
611 /// and correctly decoded, `Ok(None)` indicates that this header format does
612 /// not include the device major number, and `Err` indicates that it was
613 /// present and failed to decode.
614 pub fn device_major(&self) -> io::Result<Option<u32>> {
615 if let Some(ustar) = self.as_ustar() {
616 ustar.device_major().map(Some)
617 } else if let Some(gnu) = self.as_gnu() {
618 gnu.device_major().map(Some)
619 } else {
620 Ok(None)
621 }
622 }
623
624 /// Encodes the value `major` into the dev_major field of this header.
625 ///
626 /// This function will return an error if this header format cannot encode a
627 /// major device number.
628 pub fn set_device_major(&mut self, major: u32) -> io::Result<()> {
629 if let Some(ustar) = self.as_ustar_mut() {
630 ustar.set_device_major(major);
631 Ok(())
632 } else if let Some(gnu) = self.as_gnu_mut() {
633 gnu.set_device_major(major);
634 Ok(())
635 } else {
636 Err(other("not a ustar or gnu archive, cannot set dev_major"))
637 }
638 }
639
640 /// Returns the device minor number, if present.
641 ///
642 /// This field may not be present in all archives, and it may not be
643 /// correctly formed in all archives. `Ok(Some(..))` means it was present
644 /// and correctly decoded, `Ok(None)` indicates that this header format does
645 /// not include the device minor number, and `Err` indicates that it was
646 /// present and failed to decode.
647 pub fn device_minor(&self) -> io::Result<Option<u32>> {
648 if let Some(ustar) = self.as_ustar() {
649 ustar.device_minor().map(Some)
650 } else if let Some(gnu) = self.as_gnu() {
651 gnu.device_minor().map(Some)
652 } else {
653 Ok(None)
654 }
655 }
656
657 /// Encodes the value `minor` into the dev_minor field of this header.
658 ///
659 /// This function will return an error if this header format cannot encode a
660 /// minor device number.
661 pub fn set_device_minor(&mut self, minor: u32) -> io::Result<()> {
662 if let Some(ustar) = self.as_ustar_mut() {
663 ustar.set_device_minor(minor);
664 Ok(())
665 } else if let Some(gnu) = self.as_gnu_mut() {
666 gnu.set_device_minor(minor);
667 Ok(())
668 } else {
669 Err(other("not a ustar or gnu archive, cannot set dev_minor"))
670 }
671 }
672
673 /// Returns the type of file described by this header.
674 pub fn entry_type(&self) -> EntryType {
675 EntryType::new(self.as_old().linkflag[0])
676 }
677
678 /// Sets the type of file that will be described by this header.
679 pub fn set_entry_type(&mut self, ty: EntryType) {
680 self.as_old_mut().linkflag = [ty.as_byte()];
681 }
682
683 /// Returns the checksum field of this header.
684 ///
685 /// May return an error if the field is corrupted.
686 pub fn cksum(&self) -> io::Result<u32> {
687 octal_from(&self.as_old().cksum)
688 .map(|u| u as u32)
689 .map_err(|err| {
690 io::Error::new(
691 err.kind(),
692 format!("{} when getting cksum for {}", err, self.path_lossy()),
693 )
694 })
695 }
696
697 /// Sets the checksum field of this header based on the current fields in
698 /// this header.
699 pub fn set_cksum(&mut self) {
700 let cksum = self.calculate_cksum();
701 octal_into(&mut self.as_old_mut().cksum, cksum);
702 }
703
704 fn calculate_cksum(&self) -> u32 {
705 let old = self.as_old();
706 let start = old as *const _ as usize;
707 let cksum_start = old.cksum.as_ptr() as *const _ as usize;
708 let offset = cksum_start - start;
709 let len = old.cksum.len();
710 self.bytes[0..offset]
711 .iter()
712 .chain(iter::repeat(&b' ').take(len))
713 .chain(&self.bytes[offset + len..])
714 .fold(0, |a, b| a + (*b as u32))
715 }
716
717 fn fill_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
718 self.fill_platform_from(meta, mode);
719 // Set size of directories to zero
720 self.set_size(if meta.is_dir() || meta.file_type().is_symlink() {
721 0
722 } else {
723 meta.len()
724 });
725 if let Some(ustar) = self.as_ustar_mut() {
726 ustar.set_device_major(0);
727 ustar.set_device_minor(0);
728 }
729 if let Some(gnu) = self.as_gnu_mut() {
730 gnu.set_device_major(0);
731 gnu.set_device_minor(0);
732 }
733 }
734
735 #[cfg(target_arch = "wasm32")]
736 #[allow(unused_variables)]
737 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
738 unimplemented!();
739 }
740
741 #[cfg(unix)]
742 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
743 match mode {
744 HeaderMode::Complete => {
745 self.set_mtime(meta.mtime() as u64);
746 self.set_uid(meta.uid() as u64);
747 self.set_gid(meta.gid() as u64);
748 self.set_mode(meta.mode() as u32);
749 }
750 HeaderMode::Deterministic => {
751 // We could in theory set the mtime to zero here, but not all
752 // tools seem to behave well when ingesting files with a 0
753 // timestamp. For example rust-lang/cargo#9512 shows that lldb
754 // doesn't ingest files with a zero timestamp correctly.
755 //
756 // We just need things to be deterministic here so just pick
757 // something that isn't zero. This time, chosen after careful
758 // deliberation, corresponds to Jul 23, 2006 -- the date of the
759 // first commit for what would become Rust.
760 self.set_mtime(1153704088);
761
762 self.set_uid(0);
763 self.set_gid(0);
764
765 // Use a default umask value, but propagate the (user) execute bit.
766 let fs_mode = if meta.is_dir() || (0o100 & meta.mode() == 0o100) {
767 0o755
768 } else {
769 0o644
770 };
771 self.set_mode(fs_mode);
772 }
773 }
774
775 // Note that if we are a GNU header we *could* set atime/ctime, except
776 // the `tar` utility doesn't do that by default and it causes problems
777 // with 7-zip [1].
778 //
779 // It's always possible to fill them out manually, so we just don't fill
780 // it out automatically here.
781 //
782 // [1]: https://github.com/alexcrichton/tar-rs/issues/70
783
784 // TODO: need to bind more file types
785 self.set_entry_type(entry_type(meta.mode()));
786
787 fn entry_type(mode: u32) -> EntryType {
788 match mode as libc::mode_t & libc::S_IFMT {
789 libc::S_IFREG => EntryType::file(),
790 libc::S_IFLNK => EntryType::symlink(),
791 libc::S_IFCHR => EntryType::character_special(),
792 libc::S_IFBLK => EntryType::block_special(),
793 libc::S_IFDIR => EntryType::dir(),
794 libc::S_IFIFO => EntryType::fifo(),
795 _ => EntryType::new(b' '),
796 }
797 }
798 }
799
800 #[cfg(windows)]
801 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
802 // There's no concept of a file mode on Windows, so do a best approximation here.
803 match mode {
804 HeaderMode::Complete => {
805 self.set_uid(0);
806 self.set_gid(0);
807 // The dates listed in tarballs are always seconds relative to
808 // January 1, 1970. On Windows, however, the timestamps are returned as
809 // dates relative to January 1, 1601 (in 100ns intervals), so we need to
810 // add in some offset for those dates.
811 let mtime = (meta.last_write_time() / (1_000_000_000 / 100)) - 11644473600;
812 self.set_mtime(mtime);
813 let fs_mode = {
814 const FILE_ATTRIBUTE_READONLY: u32 = 0x00000001;
815 let readonly = meta.file_attributes() & FILE_ATTRIBUTE_READONLY;
816 match (meta.is_dir(), readonly != 0) {
817 (true, false) => 0o755,
818 (true, true) => 0o555,
819 (false, false) => 0o644,
820 (false, true) => 0o444,
821 }
822 };
823 self.set_mode(fs_mode);
824 }
825 HeaderMode::Deterministic => {
826 self.set_uid(0);
827 self.set_gid(0);
828 self.set_mtime(123456789); // see above in unix
829 let fs_mode = if meta.is_dir() { 0o755 } else { 0o644 };
830 self.set_mode(fs_mode);
831 }
832 }
833
834 let ft = meta.file_type();
835 self.set_entry_type(if ft.is_dir() {
836 EntryType::dir()
837 } else if ft.is_file() {
838 EntryType::file()
839 } else if ft.is_symlink() {
840 EntryType::symlink()
841 } else {
842 EntryType::new(b' ')
843 });
844 }
845
846 fn debug_fields(&self, b: &mut fmt::DebugStruct) {
847 if let Ok(entry_size) = self.entry_size() {
848 b.field("entry_size", &entry_size);
849 }
850 if let Ok(size) = self.size() {
851 b.field("size", &size);
852 }
853 if let Ok(path) = self.path() {
854 b.field("path", &path);
855 }
856 if let Ok(link_name) = self.link_name() {
857 b.field("link_name", &link_name);
858 }
859 if let Ok(mode) = self.mode() {
860 b.field("mode", &DebugAsOctal(mode));
861 }
862 if let Ok(uid) = self.uid() {
863 b.field("uid", &uid);
864 }
865 if let Ok(gid) = self.gid() {
866 b.field("gid", &gid);
867 }
868 if let Ok(mtime) = self.mtime() {
869 b.field("mtime", &mtime);
870 }
871 if let Ok(username) = self.username() {
872 b.field("username", &username);
873 }
874 if let Ok(groupname) = self.groupname() {
875 b.field("groupname", &groupname);
876 }
877 if let Ok(device_major) = self.device_major() {
878 b.field("device_major", &device_major);
879 }
880 if let Ok(device_minor) = self.device_minor() {
881 b.field("device_minor", &device_minor);
882 }
883 if let Ok(cksum) = self.cksum() {
884 b.field("cksum", &cksum);
885 b.field("cksum_valid", &(cksum == self.calculate_cksum()));
886 }
887 }
888}
889
890struct DebugAsOctal<T>(T);
891
892impl<T: fmt::Octal> fmt::Debug for DebugAsOctal<T> {
893 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
894 fmt::Octal::fmt(&self.0, f)
895 }
896}
897
898unsafe fn cast<T, U>(a: &T) -> &U {
899 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
900 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
901 &*(a as *const T as *const U)
902}
903
904unsafe fn cast_mut<T, U>(a: &mut T) -> &mut U {
905 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
906 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
907 &mut *(a as *mut T as *mut U)
908}
909
910impl Clone for Header {
911 fn clone(&self) -> Header {
912 Header { bytes: self.bytes }
913 }
914}
915
916impl fmt::Debug for Header {
917 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
918 if let Some(me: &UstarHeader) = self.as_ustar() {
919 me.fmt(f)
920 } else if let Some(me: &GnuHeader) = self.as_gnu() {
921 me.fmt(f)
922 } else {
923 self.as_old().fmt(f)
924 }
925 }
926}
927
928impl OldHeader {
929 /// Views this as a normal `Header`
930 pub fn as_header(&self) -> &Header {
931 unsafe { cast(self) }
932 }
933
934 /// Views this as a normal `Header`
935 pub fn as_header_mut(&mut self) -> &mut Header {
936 unsafe { cast_mut(self) }
937 }
938}
939
940impl fmt::Debug for OldHeader {
941 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
942 let mut f: DebugStruct<'_, '_> = f.debug_struct(name:"OldHeader");
943 self.as_header().debug_fields(&mut f);
944 f.finish()
945 }
946}
947
948impl UstarHeader {
949 /// See `Header::path_bytes`
950 pub fn path_bytes(&self) -> Cow<[u8]> {
951 if self.prefix[0] == 0 && !self.name.contains(&b'\\') {
952 Cow::Borrowed(truncate(&self.name))
953 } else {
954 let mut bytes = Vec::new();
955 let prefix = truncate(&self.prefix);
956 if !prefix.is_empty() {
957 bytes.extend_from_slice(prefix);
958 bytes.push(b'/');
959 }
960 bytes.extend_from_slice(truncate(&self.name));
961 Cow::Owned(bytes)
962 }
963 }
964
965 /// Gets the path in a "lossy" way, used for error reporting ONLY.
966 fn path_lossy(&self) -> String {
967 String::from_utf8_lossy(&self.path_bytes()).to_string()
968 }
969
970 /// See `Header::set_path`
971 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
972 self._set_path(p.as_ref())
973 }
974
975 fn _set_path(&mut self, path: &Path) -> io::Result<()> {
976 // This can probably be optimized quite a bit more, but for now just do
977 // something that's relatively easy and readable.
978 //
979 // First up, if the path fits within `self.name` then we just shove it
980 // in there. If not then we try to split it between some existing path
981 // components where it can fit in name/prefix. To do that we peel off
982 // enough until the path fits in `prefix`, then we try to put both
983 // halves into their destination.
984 let bytes = path2bytes(path)?;
985 let (maxnamelen, maxprefixlen) = (self.name.len(), self.prefix.len());
986 if bytes.len() <= maxnamelen {
987 copy_path_into(&mut self.name, path, false).map_err(|err| {
988 io::Error::new(
989 err.kind(),
990 format!("{} when setting path for {}", err, self.path_lossy()),
991 )
992 })?;
993 } else {
994 let mut prefix = path;
995 let mut prefixlen;
996 loop {
997 match prefix.parent() {
998 Some(parent) => prefix = parent,
999 None => {
1000 return Err(other(&format!(
1001 "path cannot be split to be inserted into archive: {}",
1002 path.display()
1003 )));
1004 }
1005 }
1006 prefixlen = path2bytes(prefix)?.len();
1007 if prefixlen <= maxprefixlen {
1008 break;
1009 }
1010 }
1011 copy_path_into(&mut self.prefix, prefix, false).map_err(|err| {
1012 io::Error::new(
1013 err.kind(),
1014 format!("{} when setting path for {}", err, self.path_lossy()),
1015 )
1016 })?;
1017 let path = bytes2path(Cow::Borrowed(&bytes[prefixlen + 1..]))?;
1018 copy_path_into(&mut self.name, &path, false).map_err(|err| {
1019 io::Error::new(
1020 err.kind(),
1021 format!("{} when setting path for {}", err, self.path_lossy()),
1022 )
1023 })?;
1024 }
1025 Ok(())
1026 }
1027
1028 /// See `Header::username_bytes`
1029 pub fn username_bytes(&self) -> &[u8] {
1030 truncate(&self.uname)
1031 }
1032
1033 /// See `Header::set_username`
1034 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1035 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1036 io::Error::new(
1037 err.kind(),
1038 format!("{} when setting username for {}", err, self.path_lossy()),
1039 )
1040 })
1041 }
1042
1043 /// See `Header::groupname_bytes`
1044 pub fn groupname_bytes(&self) -> &[u8] {
1045 truncate(&self.gname)
1046 }
1047
1048 /// See `Header::set_groupname`
1049 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1050 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1051 io::Error::new(
1052 err.kind(),
1053 format!("{} when setting groupname for {}", err, self.path_lossy()),
1054 )
1055 })
1056 }
1057
1058 /// See `Header::device_major`
1059 pub fn device_major(&self) -> io::Result<u32> {
1060 octal_from(&self.dev_major)
1061 .map(|u| u as u32)
1062 .map_err(|err| {
1063 io::Error::new(
1064 err.kind(),
1065 format!(
1066 "{} when getting device_major for {}",
1067 err,
1068 self.path_lossy()
1069 ),
1070 )
1071 })
1072 }
1073
1074 /// See `Header::set_device_major`
1075 pub fn set_device_major(&mut self, major: u32) {
1076 octal_into(&mut self.dev_major, major);
1077 }
1078
1079 /// See `Header::device_minor`
1080 pub fn device_minor(&self) -> io::Result<u32> {
1081 octal_from(&self.dev_minor)
1082 .map(|u| u as u32)
1083 .map_err(|err| {
1084 io::Error::new(
1085 err.kind(),
1086 format!(
1087 "{} when getting device_minor for {}",
1088 err,
1089 self.path_lossy()
1090 ),
1091 )
1092 })
1093 }
1094
1095 /// See `Header::set_device_minor`
1096 pub fn set_device_minor(&mut self, minor: u32) {
1097 octal_into(&mut self.dev_minor, minor);
1098 }
1099
1100 /// Views this as a normal `Header`
1101 pub fn as_header(&self) -> &Header {
1102 unsafe { cast(self) }
1103 }
1104
1105 /// Views this as a normal `Header`
1106 pub fn as_header_mut(&mut self) -> &mut Header {
1107 unsafe { cast_mut(self) }
1108 }
1109}
1110
1111impl fmt::Debug for UstarHeader {
1112 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1113 let mut f: DebugStruct<'_, '_> = f.debug_struct(name:"UstarHeader");
1114 self.as_header().debug_fields(&mut f);
1115 f.finish()
1116 }
1117}
1118
1119impl GnuHeader {
1120 /// See `Header::username_bytes`
1121 pub fn username_bytes(&self) -> &[u8] {
1122 truncate(&self.uname)
1123 }
1124
1125 /// Gets the fullname (group:user) in a "lossy" way, used for error reporting ONLY.
1126 fn fullname_lossy(&self) -> String {
1127 format!(
1128 "{}:{}",
1129 String::from_utf8_lossy(self.groupname_bytes()),
1130 String::from_utf8_lossy(self.username_bytes()),
1131 )
1132 }
1133
1134 /// See `Header::set_username`
1135 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
1136 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1137 io::Error::new(
1138 err.kind(),
1139 format!(
1140 "{} when setting username for {}",
1141 err,
1142 self.fullname_lossy()
1143 ),
1144 )
1145 })
1146 }
1147
1148 /// See `Header::groupname_bytes`
1149 pub fn groupname_bytes(&self) -> &[u8] {
1150 truncate(&self.gname)
1151 }
1152
1153 /// See `Header::set_groupname`
1154 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
1155 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1156 io::Error::new(
1157 err.kind(),
1158 format!(
1159 "{} when setting groupname for {}",
1160 err,
1161 self.fullname_lossy()
1162 ),
1163 )
1164 })
1165 }
1166
1167 /// See `Header::device_major`
1168 pub fn device_major(&self) -> io::Result<u32> {
1169 octal_from(&self.dev_major)
1170 .map(|u| u as u32)
1171 .map_err(|err| {
1172 io::Error::new(
1173 err.kind(),
1174 format!(
1175 "{} when getting device_major for {}",
1176 err,
1177 self.fullname_lossy()
1178 ),
1179 )
1180 })
1181 }
1182
1183 /// See `Header::set_device_major`
1184 pub fn set_device_major(&mut self, major: u32) {
1185 octal_into(&mut self.dev_major, major);
1186 }
1187
1188 /// See `Header::device_minor`
1189 pub fn device_minor(&self) -> io::Result<u32> {
1190 octal_from(&self.dev_minor)
1191 .map(|u| u as u32)
1192 .map_err(|err| {
1193 io::Error::new(
1194 err.kind(),
1195 format!(
1196 "{} when getting device_minor for {}",
1197 err,
1198 self.fullname_lossy()
1199 ),
1200 )
1201 })
1202 }
1203
1204 /// See `Header::set_device_minor`
1205 pub fn set_device_minor(&mut self, minor: u32) {
1206 octal_into(&mut self.dev_minor, minor);
1207 }
1208
1209 /// Returns the last modification time in Unix time format
1210 pub fn atime(&self) -> io::Result<u64> {
1211 num_field_wrapper_from(&self.atime).map_err(|err| {
1212 io::Error::new(
1213 err.kind(),
1214 format!("{} when getting atime for {}", err, self.fullname_lossy()),
1215 )
1216 })
1217 }
1218
1219 /// Encodes the `atime` provided into this header.
1220 ///
1221 /// Note that this time is typically a number of seconds passed since
1222 /// January 1, 1970.
1223 pub fn set_atime(&mut self, atime: u64) {
1224 num_field_wrapper_into(&mut self.atime, atime);
1225 }
1226
1227 /// Returns the last modification time in Unix time format
1228 pub fn ctime(&self) -> io::Result<u64> {
1229 num_field_wrapper_from(&self.ctime).map_err(|err| {
1230 io::Error::new(
1231 err.kind(),
1232 format!("{} when getting ctime for {}", err, self.fullname_lossy()),
1233 )
1234 })
1235 }
1236
1237 /// Encodes the `ctime` provided into this header.
1238 ///
1239 /// Note that this time is typically a number of seconds passed since
1240 /// January 1, 1970.
1241 pub fn set_ctime(&mut self, ctime: u64) {
1242 num_field_wrapper_into(&mut self.ctime, ctime);
1243 }
1244
1245 /// Returns the "real size" of the file this header represents.
1246 ///
1247 /// This is applicable for sparse files where the returned size here is the
1248 /// size of the entire file after the sparse regions have been filled in.
1249 pub fn real_size(&self) -> io::Result<u64> {
1250 octal_from(&self.realsize).map_err(|err| {
1251 io::Error::new(
1252 err.kind(),
1253 format!(
1254 "{} when getting real_size for {}",
1255 err,
1256 self.fullname_lossy()
1257 ),
1258 )
1259 })
1260 }
1261
1262 /// Indicates whether this header will be followed by additional
1263 /// sparse-header records.
1264 ///
1265 /// Note that this is handled internally by this library, and is likely only
1266 /// interesting if a `raw` iterator is being used.
1267 pub fn is_extended(&self) -> bool {
1268 self.isextended[0] == 1
1269 }
1270
1271 /// Views this as a normal `Header`
1272 pub fn as_header(&self) -> &Header {
1273 unsafe { cast(self) }
1274 }
1275
1276 /// Views this as a normal `Header`
1277 pub fn as_header_mut(&mut self) -> &mut Header {
1278 unsafe { cast_mut(self) }
1279 }
1280}
1281
1282impl fmt::Debug for GnuHeader {
1283 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1284 let mut f: DebugStruct<'_, '_> = f.debug_struct(name:"GnuHeader");
1285 self.as_header().debug_fields(&mut f);
1286 if let Ok(atime: u64) = self.atime() {
1287 f.field(name:"atime", &atime);
1288 }
1289 if let Ok(ctime: u64) = self.ctime() {
1290 f.field(name:"ctime", &ctime);
1291 }
1292 f&mut DebugStruct<'_, '_>.field("is_extended", &self.is_extended())
1293 .field(name:"sparse", &DebugSparseHeaders(&self.sparse))
1294 .finish()
1295 }
1296}
1297
1298struct DebugSparseHeaders<'a>(&'a [GnuSparseHeader]);
1299
1300impl<'a> fmt::Debug for DebugSparseHeaders<'a> {
1301 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1302 let mut f: DebugList<'_, '_> = f.debug_list();
1303 for header: &GnuSparseHeader in self.0 {
1304 if !header.is_empty() {
1305 f.entry(header);
1306 }
1307 }
1308 f.finish()
1309 }
1310}
1311
1312impl GnuSparseHeader {
1313 /// Returns true if block is empty
1314 pub fn is_empty(&self) -> bool {
1315 self.offset[0] == 0 || self.numbytes[0] == 0
1316 }
1317
1318 /// Offset of the block from the start of the file
1319 ///
1320 /// Returns `Err` for a malformed `offset` field.
1321 pub fn offset(&self) -> io::Result<u64> {
1322 octal_from(&self.offset).map_err(|err| {
1323 io::Error::new(
1324 err.kind(),
1325 format!("{} when getting offset from sparse header", err),
1326 )
1327 })
1328 }
1329
1330 /// Length of the block
1331 ///
1332 /// Returns `Err` for a malformed `numbytes` field.
1333 pub fn length(&self) -> io::Result<u64> {
1334 octal_from(&self.numbytes).map_err(|err| {
1335 io::Error::new(
1336 err.kind(),
1337 format!("{} when getting length from sparse header", err),
1338 )
1339 })
1340 }
1341}
1342
1343impl fmt::Debug for GnuSparseHeader {
1344 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1345 let mut f: DebugStruct<'_, '_> = f.debug_struct(name:"GnuSparseHeader");
1346 if let Ok(offset: u64) = self.offset() {
1347 f.field(name:"offset", &offset);
1348 }
1349 if let Ok(length: u64) = self.length() {
1350 f.field(name:"length", &length);
1351 }
1352 f.finish()
1353 }
1354}
1355
1356impl GnuExtSparseHeader {
1357 /// Crates a new zero'd out sparse header entry.
1358 pub fn new() -> GnuExtSparseHeader {
1359 unsafe { mem::zeroed() }
1360 }
1361
1362 /// Returns a view into this header as a byte array.
1363 pub fn as_bytes(&self) -> &[u8; 512] {
1364 debug_assert_eq!(mem::size_of_val(self), 512);
1365 unsafe { mem::transmute(self) }
1366 }
1367
1368 /// Returns a view into this header as a byte array.
1369 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
1370 debug_assert_eq!(mem::size_of_val(self), 512);
1371 unsafe { mem::transmute(self) }
1372 }
1373
1374 /// Returns a slice of the underlying sparse headers.
1375 ///
1376 /// Some headers may represent empty chunks of both the offset and numbytes
1377 /// fields are 0.
1378 pub fn sparse(&self) -> &[GnuSparseHeader; 21] {
1379 &self.sparse
1380 }
1381
1382 /// Indicates if another sparse header should be following this one.
1383 pub fn is_extended(&self) -> bool {
1384 self.isextended[0] == 1
1385 }
1386}
1387
1388impl Default for GnuExtSparseHeader {
1389 fn default() -> Self {
1390 Self::new()
1391 }
1392}
1393
1394fn octal_from(slice: &[u8]) -> io::Result<u64> {
1395 let trun: &[u8] = truncate(slice);
1396 let num: &str = match str::from_utf8(trun) {
1397 Ok(n: &str) => n,
1398 Err(_) => {
1399 return Err(other(&format!(
1400 "numeric field did not have utf-8 text: {}",
1401 String::from_utf8_lossy(trun)
1402 )));
1403 }
1404 };
1405 match u64::from_str_radix(src:num.trim(), radix:8) {
1406 Ok(n: u64) => Ok(n),
1407 Err(_) => Err(other(&format!("numeric field was not a number: {}", num))),
1408 }
1409}
1410
1411fn octal_into<T: fmt::Octal>(dst: &mut [u8], val: T) {
1412 let o: String = format!("{:o}", val);
1413 let value: impl Iterator = o.bytes().rev().chain(repeat(elt:b'0'));
1414 for (slot: &mut u8, value: u8) in dst.iter_mut().rev().skip(1).zip(value) {
1415 *slot = value;
1416 }
1417}
1418
1419// Wrapper to figure out if we should fill the header field using tar's numeric
1420// extension (binary) or not (octal).
1421fn num_field_wrapper_into(dst: &mut [u8], src: u64) {
1422 if src >= 8589934592 || (src >= 2097152 && dst.len() == 8) {
1423 numeric_extended_into(dst, src);
1424 } else {
1425 octal_into(dst, val:src);
1426 }
1427}
1428
1429// Wrapper to figure out if we should read the header field in binary (numeric
1430// extension) or octal (standard encoding).
1431fn num_field_wrapper_from(src: &[u8]) -> io::Result<u64> {
1432 if src[0] & 0x80 != 0 {
1433 Ok(numeric_extended_from(src))
1434 } else {
1435 octal_from(slice:src)
1436 }
1437}
1438
1439// When writing numeric fields with is the extended form, the high bit of the
1440// first byte is set to 1 and the remainder of the field is treated as binary
1441// instead of octal ascii.
1442// This handles writing u64 to 8 (uid, gid) or 12 (size, *time) bytes array.
1443fn numeric_extended_into(dst: &mut [u8], src: u64) {
1444 let len: usize = dst.len();
1445 for (slot: &mut u8, val: u8) in dst.iter_mut().zip(
1446 repeatimpl Iterator(elt:0)
1447 .take(len - 8) // to zero init extra bytes
1448 .chain((0..8).rev().map(|x: i32| ((src >> (8 * x)) & 0xff) as u8)),
1449 ) {
1450 *slot = val;
1451 }
1452 dst[0] |= 0x80;
1453}
1454
1455fn numeric_extended_from(src: &[u8]) -> u64 {
1456 let mut dst: u64 = 0;
1457 let mut b_to_skip: usize = 1;
1458 if src.len() == 8 {
1459 // read first byte without extension flag bit
1460 dst = (src[0] ^ 0x80) as u64;
1461 } else {
1462 // only read last 8 bytes
1463 b_to_skip = src.len() - 8;
1464 }
1465 for byte: &u8 in src.iter().skip(b_to_skip) {
1466 dst <<= 8;
1467 dst |= *byte as u64;
1468 }
1469 dst
1470}
1471
1472fn truncate(slice: &[u8]) -> &[u8] {
1473 match slice.iter().position(|i: &u8| *i == 0) {
1474 Some(i: usize) => &slice[..i],
1475 None => slice,
1476 }
1477}
1478
1479/// Copies `bytes` into the `slot` provided, returning an error if the `bytes`
1480/// array is too long or if it contains any nul bytes.
1481fn copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()> {
1482 if bytes.len() > slot.len() {
1483 Err(other(msg:"provided value is too long"))
1484 } else if bytes.iter().any(|b: &u8| *b == 0) {
1485 Err(other(msg:"provided value contains a nul byte"))
1486 } else {
1487 for (slot: &mut u8, val: &u8) in slot.iter_mut().zip(bytes.iter().chain(Some(&0))) {
1488 *slot = *val;
1489 }
1490 Ok(())
1491 }
1492}
1493
1494/// Copies `path` into the `slot` provided
1495///
1496/// Returns an error if:
1497///
1498/// * the path is too long to fit
1499/// * a nul byte was found
1500/// * an invalid path component is encountered (e.g. a root path or parent dir)
1501/// * the path itself is empty
1502fn copy_path_into(mut slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()> {
1503 let mut emitted = false;
1504 let mut needs_slash = false;
1505 for component in path.components() {
1506 let bytes = path2bytes(Path::new(component.as_os_str()))?;
1507 match (component, is_link_name) {
1508 (Component::Prefix(..), false) | (Component::RootDir, false) => {
1509 return Err(other("paths in archives must be relative"));
1510 }
1511 (Component::ParentDir, false) => {
1512 return Err(other("paths in archives must not have `..`"));
1513 }
1514 // Allow "./" as the path
1515 (Component::CurDir, false) if path.components().count() == 1 => {}
1516 (Component::CurDir, false) => continue,
1517 (Component::Normal(_), _) | (_, true) => {}
1518 };
1519 if needs_slash {
1520 copy(&mut slot, b"/")?;
1521 }
1522 if bytes.contains(&b'/') {
1523 if let Component::Normal(..) = component {
1524 return Err(other("path component in archive cannot contain `/`"));
1525 }
1526 }
1527 copy(&mut slot, &*bytes)?;
1528 if &*bytes != b"/" {
1529 needs_slash = true;
1530 }
1531 emitted = true;
1532 }
1533 if !emitted {
1534 return Err(other("paths in archives must have at least one component"));
1535 }
1536 if ends_with_slash(path) {
1537 copy(&mut slot, &[b'/'])?;
1538 }
1539 return Ok(());
1540
1541 fn copy(slot: &mut &mut [u8], bytes: &[u8]) -> io::Result<()> {
1542 copy_into(*slot, bytes)?;
1543 let tmp = mem::replace(slot, &mut []);
1544 *slot = &mut tmp[bytes.len()..];
1545 Ok(())
1546 }
1547}
1548
1549#[cfg(target_arch = "wasm32")]
1550fn ends_with_slash(p: &Path) -> bool {
1551 p.to_string_lossy().ends_with('/')
1552}
1553
1554#[cfg(windows)]
1555fn ends_with_slash(p: &Path) -> bool {
1556 let last = p.as_os_str().encode_wide().last();
1557 last == Some(b'/' as u16) || last == Some(b'\\' as u16)
1558}
1559
1560#[cfg(unix)]
1561fn ends_with_slash(p: &Path) -> bool {
1562 p.as_os_str().as_bytes().ends_with(&[b'/'])
1563}
1564
1565#[cfg(any(windows, target_arch = "wasm32"))]
1566pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1567 p.as_os_str()
1568 .to_str()
1569 .map(|s| s.as_bytes())
1570 .ok_or_else(|| other(&format!("path {} was not valid Unicode", p.display())))
1571 .map(|bytes| {
1572 if bytes.contains(&b'\\') {
1573 // Normalize to Unix-style path separators
1574 let mut bytes = bytes.to_owned();
1575 for b in &mut bytes {
1576 if *b == b'\\' {
1577 *b = b'/';
1578 }
1579 }
1580 Cow::Owned(bytes)
1581 } else {
1582 Cow::Borrowed(bytes)
1583 }
1584 })
1585}
1586
1587#[cfg(unix)]
1588/// On unix this will never fail
1589pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1590 Ok(p.as_os_str().as_bytes()).map(op:Cow::Borrowed)
1591}
1592
1593#[cfg(windows)]
1594/// On windows we cannot accept non-Unicode bytes because it
1595/// is impossible to convert it to UTF-16.
1596pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1597 return match bytes {
1598 Cow::Borrowed(bytes) => {
1599 let s = str::from_utf8(bytes).map_err(|_| not_unicode(bytes))?;
1600 Ok(Cow::Borrowed(Path::new(s)))
1601 }
1602 Cow::Owned(bytes) => {
1603 let s = String::from_utf8(bytes).map_err(|uerr| not_unicode(&uerr.into_bytes()))?;
1604 Ok(Cow::Owned(PathBuf::from(s)))
1605 }
1606 };
1607
1608 fn not_unicode(v: &[u8]) -> io::Error {
1609 other(&format!(
1610 "only Unicode paths are supported on Windows: {}",
1611 String::from_utf8_lossy(v)
1612 ))
1613 }
1614}
1615
1616#[cfg(unix)]
1617/// On unix this operation can never fail.
1618pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1619 use std::ffi::{OsStr, OsString};
1620
1621 Ok(match bytes {
1622 Cow::Borrowed(bytes: &[u8]) => Cow::Borrowed(Path::new(OsStr::from_bytes(slice:bytes))),
1623 Cow::Owned(bytes: Vec) => Cow::Owned(PathBuf::from(OsString::from_vec(bytes))),
1624 })
1625}
1626
1627#[cfg(target_arch = "wasm32")]
1628pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1629 Ok(match bytes {
1630 Cow::Borrowed(bytes) => {
1631 Cow::Borrowed({ Path::new(str::from_utf8(bytes).map_err(invalid_utf8)?) })
1632 }
1633 Cow::Owned(bytes) => {
1634 Cow::Owned({ PathBuf::from(String::from_utf8(bytes).map_err(invalid_utf8)?) })
1635 }
1636 })
1637}
1638
1639#[cfg(target_arch = "wasm32")]
1640fn invalid_utf8<T>(_: T) -> io::Error {
1641 io::Error::new(io::ErrorKind::InvalidData, "Invalid utf-8")
1642}
1643