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