]> git.proxmox.com Git - rustc.git/blame - vendor/tar/src/header.rs
New upstream version 1.56.0~beta.4+dfsg1
[rustc.git] / vendor / tar / src / header.rs
CommitLineData
3dfed10e 1#[cfg(unix)]
a1dfa0c6
XL
2use std::os::unix::prelude::*;
3#[cfg(windows)]
4use std::os::windows::prelude::*;
7cac9316
XL
5
6use std::borrow::Cow;
7use std::fmt;
8use std::fs;
9use std::io;
ff7c6d11 10use std::iter;
a1dfa0c6 11use std::iter::repeat;
7cac9316 12use std::mem;
a1dfa0c6 13use std::path::{Component, Path, PathBuf};
7cac9316
XL
14use std::str;
15
74b04a01
XL
16use crate::other;
17use crate::EntryType;
7cac9316
XL
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)]
94222f64 29#[non_exhaustive]
7cac9316
XL
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,
7cac9316
XL
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] };
ff7c6d11
XL
143 unsafe {
144 let gnu = cast_mut::<_, GnuHeader>(&mut header);
7cac9316
XL
145 gnu.magic = *b"ustar ";
146 gnu.version = *b" \0";
147 }
ff7c6d11 148 header.set_mtime(0);
7cac9316
XL
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] };
ff7c6d11
XL
161 unsafe {
162 let gnu = cast_mut::<_, UstarHeader>(&mut header);
7cac9316
XL
163 gnu.magic = *b"ustar\0";
164 gnu.version = *b"00";
165 }
ff7c6d11 166 header.set_mtime(0);
7cac9316
XL
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 {
ff7c6d11
XL
177 let mut header = Header { bytes: [0; 512] };
178 header.set_mtime(0);
179 header
7cac9316
XL
180 }
181
182 fn is_ustar(&self) -> bool {
ff7c6d11 183 let ustar = unsafe { cast::<_, UstarHeader>(self) };
7cac9316
XL
184 ustar.magic[..] == b"ustar\0"[..] && ustar.version[..] == b"00"[..]
185 }
186
187 fn is_gnu(&self) -> bool {
ff7c6d11 188 let ustar = unsafe { cast::<_, UstarHeader>(self) };
7cac9316
XL
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 {
ff7c6d11 197 unsafe { cast(self) }
7cac9316
XL
198 }
199
200 /// Same as `as_old`, but the mutable version.
201 pub fn as_old_mut(&mut self) -> &mut OldHeader {
ff7c6d11 202 unsafe { cast_mut(self) }
7cac9316
XL
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> {
a1dfa0c6
XL
215 if self.is_ustar() {
216 Some(unsafe { cast(self) })
217 } else {
218 None
219 }
7cac9316
XL
220 }
221
222 /// Same as `as_ustar_mut`, but the mutable version.
223 pub fn as_ustar_mut(&mut self) -> Option<&mut UstarHeader> {
a1dfa0c6
XL
224 if self.is_ustar() {
225 Some(unsafe { cast_mut(self) })
226 } else {
227 None
228 }
7cac9316
XL
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> {
a1dfa0c6
XL
241 if self.is_gnu() {
242 Some(unsafe { cast(self) })
243 } else {
244 None
245 }
7cac9316
XL
246 }
247
248 /// Same as `as_gnu`, but the mutable version.
249 pub fn as_gnu_mut(&mut self) -> Option<&mut GnuHeader> {
a1dfa0c6
XL
250 if self.is_gnu() {
251 Some(unsafe { cast_mut(self) })
252 } else {
253 None
254 }
7cac9316
XL
255 }
256
83c7162d
XL
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>());
a1dfa0c6 263 unsafe { &*(bytes.as_ptr() as *const Header) }
83c7162d
XL
264 }
265
7cac9316
XL
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> {
a1dfa0c6
XL
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 })
7cac9316
XL
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() {
a1dfa0c6
XL
314 self.as_gnu()
315 .ok_or_else(|| other("sparse header was not a gnu header"))
316 .and_then(|h| h.real_size())
7cac9316
XL
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) {
a1dfa0c6 324 num_field_wrapper_into(&mut self.as_old_mut().size, size);
7cac9316
XL
325 }
326
327 /// Returns the raw path name stored in this header.
328 ///
3dfed10e 329 /// This method may fail if the pathname is not valid Unicode and this is
7cac9316
XL
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
83c7162d
XL
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
7cac9316
XL
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
3dfed10e
XL
363 /// path specified is not Unicode and this is a Windows platform. Will
364 /// strip out any "." path component, which signifies the current directory.
17df50a5
XL
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.
7cac9316
XL
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() {
a1dfa0c6 376 return ustar.set_path(path);
7cac9316 377 }
a1dfa0c6
XL
378 copy_path_into(&mut self.as_old_mut().name, path, false).map_err(|err| {
379 io::Error::new(
83c7162d 380 err.kind(),
a1dfa0c6
XL
381 format!("{} when setting path for {}", err, self.path_lossy()),
382 )
383 })
7cac9316
XL
384 }
385
386 /// Returns the link name stored in this header, if any is found.
387 ///
3dfed10e 388 /// This method may fail if the pathname is not valid Unicode and this is
7cac9316
XL
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
74b04a01 417 /// Sets the link name for this header.
7cac9316 418 ///
74b04a01
XL
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
3dfed10e
XL
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.
7cac9316
XL
423 pub fn set_link_name<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
424 self._set_link_name(p.as_ref())
425 }
426
427 fn _set_link_name(&mut self, path: &Path) -> io::Result<()> {
a1dfa0c6
XL
428 copy_path_into(&mut self.as_old_mut().linkname, path, true).map_err(|err| {
429 io::Error::new(
430 err.kind(),
431 format!("{} when setting link name for {}", err, self.path_lossy()),
432 )
433 })
7cac9316
XL
434 }
435
436 /// Returns the mode bits for this file
437 ///
438 /// May return an error if the field is corrupted.
439 pub fn mode(&self) -> io::Result<u32> {
a1dfa0c6
XL
440 octal_from(&self.as_old().mode)
441 .map(|u| u as u32)
442 .map_err(|err| {
443 io::Error::new(
444 err.kind(),
445 format!("{} when getting mode for {}", err, self.path_lossy()),
446 )
447 })
7cac9316
XL
448 }
449
450 /// Encodes the `mode` provided into this header.
451 pub fn set_mode(&mut self, mode: u32) {
452 octal_into(&mut self.as_old_mut().mode, mode);
453 }
454
455 /// Returns the value of the owner's user ID field
456 ///
457 /// May return an error if the field is corrupted.
a1dfa0c6
XL
458 pub fn uid(&self) -> io::Result<u64> {
459 num_field_wrapper_from(&self.as_old().uid)
460 .map(|u| u as u64)
461 .map_err(|err| {
462 io::Error::new(
463 err.kind(),
464 format!("{} when getting uid for {}", err, self.path_lossy()),
465 )
466 })
7cac9316
XL
467 }
468
469 /// Encodes the `uid` provided into this header.
a1dfa0c6
XL
470 pub fn set_uid(&mut self, uid: u64) {
471 num_field_wrapper_into(&mut self.as_old_mut().uid, uid);
7cac9316
XL
472 }
473
474 /// Returns the value of the group's user ID field
a1dfa0c6
XL
475 pub fn gid(&self) -> io::Result<u64> {
476 num_field_wrapper_from(&self.as_old().gid)
477 .map(|u| u as u64)
478 .map_err(|err| {
479 io::Error::new(
480 err.kind(),
481 format!("{} when getting gid for {}", err, self.path_lossy()),
482 )
483 })
7cac9316
XL
484 }
485
486 /// Encodes the `gid` provided into this header.
a1dfa0c6
XL
487 pub fn set_gid(&mut self, gid: u64) {
488 num_field_wrapper_into(&mut self.as_old_mut().gid, gid);
7cac9316
XL
489 }
490
491 /// Returns the last modification time in Unix time format
492 pub fn mtime(&self) -> io::Result<u64> {
a1dfa0c6
XL
493 num_field_wrapper_from(&self.as_old().mtime).map_err(|err| {
494 io::Error::new(
495 err.kind(),
496 format!("{} when getting mtime for {}", err, self.path_lossy()),
497 )
498 })
7cac9316
XL
499 }
500
501 /// Encodes the `mtime` provided into this header.
502 ///
503 /// Note that this time is typically a number of seconds passed since
504 /// January 1, 1970.
505 pub fn set_mtime(&mut self, mtime: u64) {
a1dfa0c6 506 num_field_wrapper_into(&mut self.as_old_mut().mtime, mtime);
7cac9316
XL
507 }
508
509 /// Return the user name of the owner of this file.
510 ///
511 /// A return value of `Ok(Some(..))` indicates that the user name was
512 /// present and was valid utf-8, `Ok(None)` indicates that the user name is
513 /// not present in this archive format, and `Err` indicates that the user
514 /// name was present but was not valid utf-8.
515 pub fn username(&self) -> Result<Option<&str>, str::Utf8Error> {
516 match self.username_bytes() {
517 Some(bytes) => str::from_utf8(bytes).map(Some),
518 None => Ok(None),
519 }
520 }
521
522 /// Returns the user name of the owner of this file, if present.
523 ///
524 /// A return value of `None` indicates that the user name is not present in
525 /// this header format.
526 pub fn username_bytes(&self) -> Option<&[u8]> {
527 if let Some(ustar) = self.as_ustar() {
528 Some(ustar.username_bytes())
529 } else if let Some(gnu) = self.as_gnu() {
530 Some(gnu.username_bytes())
531 } else {
532 None
533 }
534 }
535
536 /// Sets the username inside this header.
537 ///
538 /// This function will return an error if this header format cannot encode a
539 /// user name or the name is too long.
540 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
541 if let Some(ustar) = self.as_ustar_mut() {
a1dfa0c6 542 return ustar.set_username(name);
7cac9316
XL
543 }
544 if let Some(gnu) = self.as_gnu_mut() {
545 gnu.set_username(name)
546 } else {
547 Err(other("not a ustar or gnu archive, cannot set username"))
548 }
549 }
550
551 /// Return the group name of the owner of this file.
552 ///
553 /// A return value of `Ok(Some(..))` indicates that the group name was
554 /// present and was valid utf-8, `Ok(None)` indicates that the group name is
555 /// not present in this archive format, and `Err` indicates that the group
556 /// name was present but was not valid utf-8.
557 pub fn groupname(&self) -> Result<Option<&str>, str::Utf8Error> {
558 match self.groupname_bytes() {
559 Some(bytes) => str::from_utf8(bytes).map(Some),
560 None => Ok(None),
561 }
562 }
563
564 /// Returns the group name of the owner of this file, if present.
565 ///
566 /// A return value of `None` indicates that the group name is not present in
567 /// this header format.
568 pub fn groupname_bytes(&self) -> Option<&[u8]> {
569 if let Some(ustar) = self.as_ustar() {
570 Some(ustar.groupname_bytes())
571 } else if let Some(gnu) = self.as_gnu() {
572 Some(gnu.groupname_bytes())
573 } else {
574 None
575 }
576 }
577
578 /// Sets the group name inside this header.
579 ///
580 /// This function will return an error if this header format cannot encode a
581 /// group name or the name is too long.
582 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
583 if let Some(ustar) = self.as_ustar_mut() {
a1dfa0c6 584 return ustar.set_groupname(name);
7cac9316
XL
585 }
586 if let Some(gnu) = self.as_gnu_mut() {
587 gnu.set_groupname(name)
588 } else {
589 Err(other("not a ustar or gnu archive, cannot set groupname"))
590 }
591 }
592
593 /// Returns the device major number, if present.
594 ///
595 /// This field may not be present in all archives, and it may not be
596 /// correctly formed in all archives. `Ok(Some(..))` means it was present
597 /// and correctly decoded, `Ok(None)` indicates that this header format does
598 /// not include the device major number, and `Err` indicates that it was
599 /// present and failed to decode.
600 pub fn device_major(&self) -> io::Result<Option<u32>> {
601 if let Some(ustar) = self.as_ustar() {
602 ustar.device_major().map(Some)
603 } else if let Some(gnu) = self.as_gnu() {
604 gnu.device_major().map(Some)
605 } else {
606 Ok(None)
607 }
608 }
609
610 /// Encodes the value `major` into the dev_major field of this header.
611 ///
612 /// This function will return an error if this header format cannot encode a
613 /// major device number.
614 pub fn set_device_major(&mut self, major: u32) -> io::Result<()> {
615 if let Some(ustar) = self.as_ustar_mut() {
94222f64
XL
616 ustar.set_device_major(major);
617 Ok(())
618 } else if let Some(gnu) = self.as_gnu_mut() {
619 gnu.set_device_major(major);
620 Ok(())
7cac9316
XL
621 } else {
622 Err(other("not a ustar or gnu archive, cannot set dev_major"))
623 }
624 }
625
626 /// Returns the device minor number, if present.
627 ///
628 /// This field may not be present in all archives, and it may not be
629 /// correctly formed in all archives. `Ok(Some(..))` means it was present
630 /// and correctly decoded, `Ok(None)` indicates that this header format does
631 /// not include the device minor number, and `Err` indicates that it was
632 /// present and failed to decode.
633 pub fn device_minor(&self) -> io::Result<Option<u32>> {
634 if let Some(ustar) = self.as_ustar() {
635 ustar.device_minor().map(Some)
636 } else if let Some(gnu) = self.as_gnu() {
637 gnu.device_minor().map(Some)
638 } else {
639 Ok(None)
640 }
641 }
642
643 /// Encodes the value `minor` into the dev_minor field of this header.
644 ///
645 /// This function will return an error if this header format cannot encode a
646 /// minor device number.
647 pub fn set_device_minor(&mut self, minor: u32) -> io::Result<()> {
648 if let Some(ustar) = self.as_ustar_mut() {
94222f64
XL
649 ustar.set_device_minor(minor);
650 Ok(())
651 } else if let Some(gnu) = self.as_gnu_mut() {
652 gnu.set_device_minor(minor);
653 Ok(())
7cac9316
XL
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.
660 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.
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.
672 pub fn cksum(&self) -> io::Result<u32> {
a1dfa0c6
XL
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 })
7cac9316
XL
681 }
682
683 /// Sets the checksum field of this header based on the current fields in
684 /// this header.
685 pub fn set_cksum(&mut self) {
ff7c6d11 686 let cksum = self.calculate_cksum();
7cac9316
XL
687 octal_into(&mut self.as_old_mut().cksum, cksum);
688 }
689
ff7c6d11
XL
690 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();
a1dfa0c6
XL
696 self.bytes[0..offset]
697 .iter()
ff7c6d11
XL
698 .chain(iter::repeat(&b' ').take(len))
699 .chain(&self.bytes[offset + len..])
700 .fold(0, |a, b| a + (*b as u32))
701 }
702
7cac9316
XL
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
a1dfa0c6
XL
706 self.set_size(if meta.is_dir() || meta.file_type().is_symlink() {
707 0
708 } else {
709 meta.len()
710 });
7cac9316
XL
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
74b04a01
XL
721 #[cfg(target_arch = "wasm32")]
722 #[allow(unused_variables)]
723 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
724 unimplemented!();
725 }
726
3dfed10e 727 #[cfg(unix)]
7cac9316 728 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
7cac9316
XL
729 match mode {
730 HeaderMode::Complete => {
731 self.set_mtime(meta.mtime() as u64);
a1dfa0c6
XL
732 self.set_uid(meta.uid() as u64);
733 self.set_gid(meta.gid() as u64);
7cac9316 734 self.set_mode(meta.mode() as u32);
a1dfa0c6 735 }
7cac9316 736 HeaderMode::Deterministic => {
17df50a5
XL
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
7cac9316
XL
747 self.set_uid(0);
748 self.set_gid(0);
749
750 // Use a default umask value, but propagate the (user) execute bit.
a1dfa0c6 751 let fs_mode = if meta.is_dir() || (0o100 & meta.mode() == 0o100) {
7cac9316 752 0o755
a1dfa0c6 753 } else {
7cac9316 754 0o644
a1dfa0c6 755 };
7cac9316 756 self.set_mode(fs_mode);
a1dfa0c6 757 }
7cac9316
XL
758 }
759
760 // Note that if we are a GNU header we *could* set atime/ctime, except
761 // the `tar` utility doesn't do that by default and it causes problems
762 // with 7-zip [1].
763 //
764 // It's always possible to fill them out manually, so we just don't fill
765 // it out automatically here.
766 //
767 // [1]: https://github.com/alexcrichton/tar-rs/issues/70
768
769 // TODO: need to bind more file types
ff7c6d11
XL
770 self.set_entry_type(entry_type(meta.mode()));
771
ff7c6d11 772 fn entry_type(mode: u32) -> EntryType {
ff7c6d11
XL
773 match mode as libc::mode_t & libc::S_IFMT {
774 libc::S_IFREG => EntryType::file(),
775 libc::S_IFLNK => EntryType::symlink(),
776 libc::S_IFCHR => EntryType::character_special(),
777 libc::S_IFBLK => EntryType::block_special(),
778 libc::S_IFDIR => EntryType::dir(),
779 libc::S_IFIFO => EntryType::fifo(),
780 _ => EntryType::new(b' '),
781 }
782 }
7cac9316
XL
783 }
784
785 #[cfg(windows)]
786 fn fill_platform_from(&mut self, meta: &fs::Metadata, mode: HeaderMode) {
3dfed10e 787 // There's no concept of a file mode on Windows, so do a best approximation here.
7cac9316
XL
788 match mode {
789 HeaderMode::Complete => {
790 self.set_uid(0);
791 self.set_gid(0);
792 // The dates listed in tarballs are always seconds relative to
793 // January 1, 1970. On Windows, however, the timestamps are returned as
794 // dates relative to January 1, 1601 (in 100ns intervals), so we need to
795 // add in some offset for those dates.
796 let mtime = (meta.last_write_time() / (1_000_000_000 / 100)) - 11644473600;
797 self.set_mtime(mtime);
798 let fs_mode = {
799 const FILE_ATTRIBUTE_READONLY: u32 = 0x00000001;
800 let readonly = meta.file_attributes() & FILE_ATTRIBUTE_READONLY;
801 match (meta.is_dir(), readonly != 0) {
802 (true, false) => 0o755,
803 (true, true) => 0o555,
804 (false, false) => 0o644,
805 (false, true) => 0o444,
806 }
807 };
808 self.set_mode(fs_mode);
a1dfa0c6 809 }
7cac9316
XL
810 HeaderMode::Deterministic => {
811 self.set_uid(0);
812 self.set_gid(0);
17df50a5 813 self.set_mtime(123456789); // see above in unix
a1dfa0c6 814 let fs_mode = if meta.is_dir() { 0o755 } else { 0o644 };
7cac9316 815 self.set_mode(fs_mode);
a1dfa0c6 816 }
7cac9316
XL
817 }
818
819 let ft = meta.file_type();
820 self.set_entry_type(if ft.is_dir() {
821 EntryType::dir()
822 } else if ft.is_file() {
823 EntryType::file()
824 } else if ft.is_symlink() {
825 EntryType::symlink()
826 } else {
827 EntryType::new(b' ')
828 });
829 }
ff7c6d11
XL
830
831 fn debug_fields(&self, b: &mut fmt::DebugStruct) {
832 if let Ok(entry_size) = self.entry_size() {
833 b.field("entry_size", &entry_size);
834 }
835 if let Ok(size) = self.size() {
836 b.field("size", &size);
837 }
838 if let Ok(path) = self.path() {
839 b.field("path", &path);
840 }
841 if let Ok(link_name) = self.link_name() {
842 b.field("link_name", &link_name);
843 }
844 if let Ok(mode) = self.mode() {
845 b.field("mode", &DebugAsOctal(mode));
846 }
847 if let Ok(uid) = self.uid() {
848 b.field("uid", &uid);
849 }
850 if let Ok(gid) = self.gid() {
851 b.field("gid", &gid);
852 }
853 if let Ok(mtime) = self.mtime() {
854 b.field("mtime", &mtime);
855 }
856 if let Ok(username) = self.username() {
857 b.field("username", &username);
858 }
859 if let Ok(groupname) = self.groupname() {
860 b.field("groupname", &groupname);
861 }
862 if let Ok(device_major) = self.device_major() {
863 b.field("device_major", &device_major);
864 }
865 if let Ok(device_minor) = self.device_minor() {
866 b.field("device_minor", &device_minor);
867 }
868 if let Ok(cksum) = self.cksum() {
869 b.field("cksum", &cksum);
870 b.field("cksum_valid", &(cksum == self.calculate_cksum()));
871 }
872 }
873}
874
875struct DebugAsOctal<T>(T);
876
877impl<T: fmt::Octal> fmt::Debug for DebugAsOctal<T> {
878 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
879 fmt::Octal::fmt(&self.0, f)
880 }
881}
882
883unsafe fn cast<T, U>(a: &T) -> &U {
884 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
885 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
886 &*(a as *const T as *const U)
887}
888
889unsafe fn cast_mut<T, U>(a: &mut T) -> &mut U {
890 assert_eq!(mem::size_of_val(a), mem::size_of::<U>());
891 assert_eq!(mem::align_of_val(a), mem::align_of::<U>());
892 &mut *(a as *mut T as *mut U)
7cac9316
XL
893}
894
895impl Clone for Header {
896 fn clone(&self) -> Header {
897 Header { bytes: self.bytes }
898 }
899}
900
ff7c6d11
XL
901impl fmt::Debug for Header {
902 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
903 if let Some(me) = self.as_ustar() {
904 me.fmt(f)
905 } else if let Some(me) = self.as_gnu() {
906 me.fmt(f)
907 } else {
908 self.as_old().fmt(f)
909 }
910 }
911}
912
913impl OldHeader {
914 /// Views this as a normal `Header`
915 pub fn as_header(&self) -> &Header {
916 unsafe { cast(self) }
917 }
918
919 /// Views this as a normal `Header`
920 pub fn as_header_mut(&mut self) -> &mut Header {
921 unsafe { cast_mut(self) }
922 }
923}
924
925impl fmt::Debug for OldHeader {
926 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
927 let mut f = f.debug_struct("OldHeader");
928 self.as_header().debug_fields(&mut f);
929 f.finish()
930 }
931}
932
7cac9316
XL
933impl UstarHeader {
934 /// See `Header::path_bytes`
935 pub fn path_bytes(&self) -> Cow<[u8]> {
936 if self.prefix[0] == 0 && !self.name.contains(&b'\\') {
937 Cow::Borrowed(truncate(&self.name))
938 } else {
939 let mut bytes = Vec::new();
940 let prefix = truncate(&self.prefix);
94222f64 941 if !prefix.is_empty() {
7cac9316
XL
942 bytes.extend_from_slice(prefix);
943 bytes.push(b'/');
944 }
945 bytes.extend_from_slice(truncate(&self.name));
946 Cow::Owned(bytes)
947 }
948 }
949
83c7162d
XL
950 /// Gets the path in a "lossy" way, used for error reporting ONLY.
951 fn path_lossy(&self) -> String {
952 String::from_utf8_lossy(&self.path_bytes()).to_string()
953 }
954
7cac9316
XL
955 /// See `Header::set_path`
956 pub fn set_path<P: AsRef<Path>>(&mut self, p: P) -> io::Result<()> {
957 self._set_path(p.as_ref())
958 }
959
960 fn _set_path(&mut self, path: &Path) -> io::Result<()> {
961 // This can probably be optimized quite a bit more, but for now just do
962 // something that's relatively easy and readable.
963 //
964 // First up, if the path fits within `self.name` then we just shove it
965 // in there. If not then we try to split it between some existing path
966 // components where it can fit in name/prefix. To do that we peel off
967 // enough until the path fits in `prefix`, then we try to put both
968 // halves into their destination.
8faf50e0 969 let bytes = path2bytes(path)?;
7cac9316
XL
970 let (maxnamelen, maxprefixlen) = (self.name.len(), self.prefix.len());
971 if bytes.len() <= maxnamelen {
a1dfa0c6
XL
972 copy_path_into(&mut self.name, path, false).map_err(|err| {
973 io::Error::new(
974 err.kind(),
975 format!("{} when setting path for {}", err, self.path_lossy()),
976 )
977 })?;
7cac9316
XL
978 } else {
979 let mut prefix = path;
980 let mut prefixlen;
981 loop {
982 match prefix.parent() {
983 Some(parent) => prefix = parent,
a1dfa0c6
XL
984 None => {
985 return Err(other(&format!(
986 "path cannot be split to be inserted into archive: {}",
987 path.display()
74b04a01 988 )));
a1dfa0c6 989 }
7cac9316 990 }
8faf50e0 991 prefixlen = path2bytes(prefix)?.len();
7cac9316 992 if prefixlen <= maxprefixlen {
a1dfa0c6 993 break;
7cac9316
XL
994 }
995 }
a1dfa0c6
XL
996 copy_path_into(&mut self.prefix, prefix, false).map_err(|err| {
997 io::Error::new(
998 err.kind(),
999 format!("{} when setting path for {}", err, self.path_lossy()),
1000 )
1001 })?;
8faf50e0 1002 let path = bytes2path(Cow::Borrowed(&bytes[prefixlen + 1..]))?;
a1dfa0c6
XL
1003 copy_path_into(&mut self.name, &path, false).map_err(|err| {
1004 io::Error::new(
1005 err.kind(),
1006 format!("{} when setting path for {}", err, self.path_lossy()),
1007 )
1008 })?;
7cac9316
XL
1009 }
1010 Ok(())
1011 }
1012
1013 /// See `Header::username_bytes`
1014 pub fn username_bytes(&self) -> &[u8] {
1015 truncate(&self.uname)
1016 }
1017
1018 /// See `Header::set_username`
1019 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
a1dfa0c6
XL
1020 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1021 io::Error::new(
1022 err.kind(),
1023 format!("{} when setting username for {}", err, self.path_lossy()),
1024 )
1025 })
7cac9316
XL
1026 }
1027
1028 /// See `Header::groupname_bytes`
1029 pub fn groupname_bytes(&self) -> &[u8] {
1030 truncate(&self.gname)
1031 }
1032
1033 /// See `Header::set_groupname`
1034 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
a1dfa0c6
XL
1035 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1036 io::Error::new(
1037 err.kind(),
1038 format!("{} when setting groupname for {}", err, self.path_lossy()),
1039 )
1040 })
7cac9316
XL
1041 }
1042
1043 /// See `Header::device_major`
1044 pub fn device_major(&self) -> io::Result<u32> {
a1dfa0c6
XL
1045 octal_from(&self.dev_major)
1046 .map(|u| u as u32)
1047 .map_err(|err| {
1048 io::Error::new(
1049 err.kind(),
1050 format!(
1051 "{} when getting device_major for {}",
1052 err,
1053 self.path_lossy()
1054 ),
1055 )
1056 })
7cac9316
XL
1057 }
1058
1059 /// See `Header::set_device_major`
1060 pub fn set_device_major(&mut self, major: u32) {
1061 octal_into(&mut self.dev_major, major);
1062 }
1063
1064 /// See `Header::device_minor`
1065 pub fn device_minor(&self) -> io::Result<u32> {
a1dfa0c6
XL
1066 octal_from(&self.dev_minor)
1067 .map(|u| u as u32)
1068 .map_err(|err| {
1069 io::Error::new(
1070 err.kind(),
1071 format!(
1072 "{} when getting device_minor for {}",
1073 err,
1074 self.path_lossy()
1075 ),
1076 )
1077 })
7cac9316
XL
1078 }
1079
1080 /// See `Header::set_device_minor`
1081 pub fn set_device_minor(&mut self, minor: u32) {
1082 octal_into(&mut self.dev_minor, minor);
1083 }
ff7c6d11
XL
1084
1085 /// Views this as a normal `Header`
1086 pub fn as_header(&self) -> &Header {
1087 unsafe { cast(self) }
1088 }
1089
1090 /// Views this as a normal `Header`
1091 pub fn as_header_mut(&mut self) -> &mut Header {
1092 unsafe { cast_mut(self) }
1093 }
1094}
1095
1096impl fmt::Debug for UstarHeader {
1097 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1098 let mut f = f.debug_struct("UstarHeader");
1099 self.as_header().debug_fields(&mut f);
1100 f.finish()
1101 }
7cac9316
XL
1102}
1103
1104impl GnuHeader {
1105 /// See `Header::username_bytes`
1106 pub fn username_bytes(&self) -> &[u8] {
1107 truncate(&self.uname)
1108 }
1109
83c7162d
XL
1110 /// Gets the fullname (group:user) in a "lossy" way, used for error reporting ONLY.
1111 fn fullname_lossy(&self) -> String {
1112 format!(
1113 "{}:{}",
94222f64
XL
1114 String::from_utf8_lossy(self.groupname_bytes()),
1115 String::from_utf8_lossy(self.username_bytes()),
83c7162d
XL
1116 )
1117 }
1118
7cac9316
XL
1119 /// See `Header::set_username`
1120 pub fn set_username(&mut self, name: &str) -> io::Result<()> {
a1dfa0c6
XL
1121 copy_into(&mut self.uname, name.as_bytes()).map_err(|err| {
1122 io::Error::new(
1123 err.kind(),
1124 format!(
1125 "{} when setting username for {}",
1126 err,
1127 self.fullname_lossy()
1128 ),
1129 )
1130 })
7cac9316
XL
1131 }
1132
1133 /// See `Header::groupname_bytes`
1134 pub fn groupname_bytes(&self) -> &[u8] {
1135 truncate(&self.gname)
1136 }
1137
1138 /// See `Header::set_groupname`
1139 pub fn set_groupname(&mut self, name: &str) -> io::Result<()> {
a1dfa0c6
XL
1140 copy_into(&mut self.gname, name.as_bytes()).map_err(|err| {
1141 io::Error::new(
1142 err.kind(),
1143 format!(
1144 "{} when setting groupname for {}",
1145 err,
1146 self.fullname_lossy()
1147 ),
1148 )
1149 })
7cac9316
XL
1150 }
1151
1152 /// See `Header::device_major`
1153 pub fn device_major(&self) -> io::Result<u32> {
a1dfa0c6
XL
1154 octal_from(&self.dev_major)
1155 .map(|u| u as u32)
1156 .map_err(|err| {
1157 io::Error::new(
1158 err.kind(),
1159 format!(
1160 "{} when getting device_major for {}",
1161 err,
1162 self.fullname_lossy()
1163 ),
1164 )
1165 })
7cac9316
XL
1166 }
1167
1168 /// See `Header::set_device_major`
1169 pub fn set_device_major(&mut self, major: u32) {
1170 octal_into(&mut self.dev_major, major);
1171 }
1172
1173 /// See `Header::device_minor`
1174 pub fn device_minor(&self) -> io::Result<u32> {
a1dfa0c6
XL
1175 octal_from(&self.dev_minor)
1176 .map(|u| u as u32)
1177 .map_err(|err| {
1178 io::Error::new(
1179 err.kind(),
1180 format!(
1181 "{} when getting device_minor for {}",
1182 err,
1183 self.fullname_lossy()
1184 ),
1185 )
1186 })
7cac9316
XL
1187 }
1188
1189 /// See `Header::set_device_minor`
1190 pub fn set_device_minor(&mut self, minor: u32) {
1191 octal_into(&mut self.dev_minor, minor);
1192 }
1193
1194 /// Returns the last modification time in Unix time format
1195 pub fn atime(&self) -> io::Result<u64> {
a1dfa0c6
XL
1196 num_field_wrapper_from(&self.atime).map_err(|err| {
1197 io::Error::new(
1198 err.kind(),
1199 format!("{} when getting atime for {}", err, self.fullname_lossy()),
1200 )
1201 })
7cac9316
XL
1202 }
1203
1204 /// Encodes the `atime` provided into this header.
1205 ///
1206 /// Note that this time is typically a number of seconds passed since
1207 /// January 1, 1970.
1208 pub fn set_atime(&mut self, atime: u64) {
a1dfa0c6 1209 num_field_wrapper_into(&mut self.atime, atime);
7cac9316
XL
1210 }
1211
1212 /// Returns the last modification time in Unix time format
1213 pub fn ctime(&self) -> io::Result<u64> {
a1dfa0c6
XL
1214 num_field_wrapper_from(&self.ctime).map_err(|err| {
1215 io::Error::new(
1216 err.kind(),
1217 format!("{} when getting ctime for {}", err, self.fullname_lossy()),
1218 )
1219 })
7cac9316
XL
1220 }
1221
1222 /// Encodes the `ctime` provided into this header.
1223 ///
1224 /// Note that this time is typically a number of seconds passed since
1225 /// January 1, 1970.
1226 pub fn set_ctime(&mut self, ctime: u64) {
a1dfa0c6 1227 num_field_wrapper_into(&mut self.ctime, ctime);
7cac9316
XL
1228 }
1229
1230 /// Returns the "real size" of the file this header represents.
1231 ///
1232 /// This is applicable for sparse files where the returned size here is the
1233 /// size of the entire file after the sparse regions have been filled in.
1234 pub fn real_size(&self) -> io::Result<u64> {
a1dfa0c6
XL
1235 octal_from(&self.realsize).map_err(|err| {
1236 io::Error::new(
1237 err.kind(),
1238 format!(
1239 "{} when getting real_size for {}",
1240 err,
1241 self.fullname_lossy()
1242 ),
1243 )
1244 })
7cac9316
XL
1245 }
1246
1247 /// Indicates whether this header will be followed by additional
1248 /// sparse-header records.
1249 ///
1250 /// Note that this is handled internally by this library, and is likely only
1251 /// interesting if a `raw` iterator is being used.
1252 pub fn is_extended(&self) -> bool {
1253 self.isextended[0] == 1
1254 }
ff7c6d11
XL
1255
1256 /// Views this as a normal `Header`
1257 pub fn as_header(&self) -> &Header {
1258 unsafe { cast(self) }
1259 }
1260
1261 /// Views this as a normal `Header`
1262 pub fn as_header_mut(&mut self) -> &mut Header {
1263 unsafe { cast_mut(self) }
1264 }
1265}
1266
1267impl fmt::Debug for GnuHeader {
1268 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1269 let mut f = f.debug_struct("GnuHeader");
1270 self.as_header().debug_fields(&mut f);
1271 if let Ok(atime) = self.atime() {
1272 f.field("atime", &atime);
1273 }
1274 if let Ok(ctime) = self.ctime() {
1275 f.field("ctime", &ctime);
1276 }
1277 f.field("is_extended", &self.is_extended())
a1dfa0c6
XL
1278 .field("sparse", &DebugSparseHeaders(&self.sparse))
1279 .finish()
ff7c6d11
XL
1280 }
1281}
1282
1283struct DebugSparseHeaders<'a>(&'a [GnuSparseHeader]);
1284
1285impl<'a> fmt::Debug for DebugSparseHeaders<'a> {
1286 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1287 let mut f = f.debug_list();
1288 for header in self.0 {
1289 if !header.is_empty() {
1290 f.entry(header);
1291 }
1292 }
1293 f.finish()
1294 }
7cac9316
XL
1295}
1296
1297impl GnuSparseHeader {
1298 /// Returns true if block is empty
1299 pub fn is_empty(&self) -> bool {
1300 self.offset[0] == 0 || self.numbytes[0] == 0
1301 }
1302
1303 /// Offset of the block from the start of the file
1304 ///
1305 /// Returns `Err` for a malformed `offset` field.
1306 pub fn offset(&self) -> io::Result<u64> {
a1dfa0c6
XL
1307 octal_from(&self.offset).map_err(|err| {
1308 io::Error::new(
1309 err.kind(),
3dfed10e 1310 format!("{} when getting offset from sparse header", err),
a1dfa0c6
XL
1311 )
1312 })
7cac9316
XL
1313 }
1314
1315 /// Length of the block
1316 ///
1317 /// Returns `Err` for a malformed `numbytes` field.
1318 pub fn length(&self) -> io::Result<u64> {
a1dfa0c6
XL
1319 octal_from(&self.numbytes).map_err(|err| {
1320 io::Error::new(
1321 err.kind(),
1322 format!("{} when getting length from sparse header", err),
1323 )
1324 })
7cac9316
XL
1325 }
1326}
1327
ff7c6d11
XL
1328impl fmt::Debug for GnuSparseHeader {
1329 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1330 let mut f = f.debug_struct("GnuSparseHeader");
1331 if let Ok(offset) = self.offset() {
1332 f.field("offset", &offset);
1333 }
1334 if let Ok(length) = self.length() {
1335 f.field("length", &length);
1336 }
1337 f.finish()
1338 }
1339}
1340
7cac9316
XL
1341impl GnuExtSparseHeader {
1342 /// Crates a new zero'd out sparse header entry.
1343 pub fn new() -> GnuExtSparseHeader {
1344 unsafe { mem::zeroed() }
1345 }
1346
1347 /// Returns a view into this header as a byte array.
1348 pub fn as_bytes(&self) -> &[u8; 512] {
1349 debug_assert_eq!(mem::size_of_val(self), 512);
1350 unsafe { mem::transmute(self) }
1351 }
1352
1353 /// Returns a view into this header as a byte array.
1354 pub fn as_mut_bytes(&mut self) -> &mut [u8; 512] {
1355 debug_assert_eq!(mem::size_of_val(self), 512);
1356 unsafe { mem::transmute(self) }
1357 }
1358
1359 /// Returns a slice of the underlying sparse headers.
1360 ///
1361 /// Some headers may represent empty chunks of both the offset and numbytes
1362 /// fields are 0.
1363 pub fn sparse(&self) -> &[GnuSparseHeader; 21] {
1364 &self.sparse
1365 }
1366
1367 /// Indicates if another sparse header should be following this one.
1368 pub fn is_extended(&self) -> bool {
1369 self.isextended[0] == 1
1370 }
1371}
1372
1373impl Default for GnuExtSparseHeader {
1374 fn default() -> Self {
1375 Self::new()
1376 }
1377}
1378
1379fn octal_from(slice: &[u8]) -> io::Result<u64> {
a1dfa0c6
XL
1380 let trun = truncate(slice);
1381 let num = match str::from_utf8(trun) {
1382 Ok(n) => n,
1383 Err(_) => {
1384 return Err(other(&format!(
1385 "numeric field did not have utf-8 text: {}",
1386 String::from_utf8_lossy(trun)
74b04a01 1387 )));
ff7c6d11 1388 }
a1dfa0c6
XL
1389 };
1390 match u64::from_str_radix(num.trim(), 8) {
1391 Ok(n) => Ok(n),
1392 Err(_) => Err(other(&format!("numeric field was not a number: {}", num))),
7cac9316
XL
1393 }
1394}
1395
1396fn octal_into<T: fmt::Octal>(dst: &mut [u8], val: T) {
1397 let o = format!("{:o}", val);
1398 let value = o.bytes().rev().chain(repeat(b'0'));
1399 for (slot, value) in dst.iter_mut().rev().skip(1).zip(value) {
1400 *slot = value;
1401 }
1402}
1403
a1dfa0c6
XL
1404// Wrapper to figure out if we should fill the header field using tar's numeric
1405// extension (binary) or not (octal).
1406fn num_field_wrapper_into(dst: &mut [u8], src: u64) {
1407 if src >= 8589934592 || (src >= 2097152 && dst.len() == 8) {
1408 numeric_extended_into(dst, src);
1409 } else {
1410 octal_into(dst, src);
1411 }
1412}
1413
1414// Wrapper to figure out if we should read the header field in binary (numeric
1415// extension) or octal (standard encoding).
1416fn num_field_wrapper_from(src: &[u8]) -> io::Result<u64> {
1417 if src[0] & 0x80 != 0 {
1418 Ok(numeric_extended_from(src))
1419 } else {
1420 octal_from(src)
1421 }
1422}
1423
1424// When writing numeric fields with is the extended form, the high bit of the
1425// first byte is set to 1 and the remainder of the field is treated as binary
1426// instead of octal ascii.
1427// This handles writing u64 to 8 (uid, gid) or 12 (size, *time) bytes array.
1428fn numeric_extended_into(dst: &mut [u8], src: u64) {
1429 let len: usize = dst.len();
1430 for (slot, val) in dst.iter_mut().zip(
74b04a01
XL
1431 repeat(0)
1432 .take(len - 8) // to zero init extra bytes
a1dfa0c6
XL
1433 .chain((0..8).rev().map(|x| ((src >> (8 * x)) & 0xff) as u8)),
1434 ) {
1435 *slot = val;
1436 }
1437 dst[0] |= 0x80;
1438}
1439
1440fn numeric_extended_from(src: &[u8]) -> u64 {
1441 let mut dst: u64 = 0;
1442 let mut b_to_skip = 1;
1443 if src.len() == 8 {
1444 // read first byte without extension flag bit
1445 dst = (src[0] ^ 0x80) as u64;
1446 } else {
1447 // only read last 8 bytes
1448 b_to_skip = src.len() - 8;
1449 }
1450 for byte in src.iter().skip(b_to_skip) {
1451 dst <<= 8;
1452 dst |= *byte as u64;
1453 }
1454 dst
1455}
1456
7cac9316
XL
1457fn truncate(slice: &[u8]) -> &[u8] {
1458 match slice.iter().position(|i| *i == 0) {
1459 Some(i) => &slice[..i],
1460 None => slice,
1461 }
1462}
1463
1464/// Copies `bytes` into the `slot` provided, returning an error if the `bytes`
1465/// array is too long or if it contains any nul bytes.
1466fn copy_into(slot: &mut [u8], bytes: &[u8]) -> io::Result<()> {
1467 if bytes.len() > slot.len() {
1468 Err(other("provided value is too long"))
1469 } else if bytes.iter().any(|b| *b == 0) {
1470 Err(other("provided value contains a nul byte"))
1471 } else {
1472 for (slot, val) in slot.iter_mut().zip(bytes.iter().chain(Some(&0))) {
1473 *slot = *val;
1474 }
1475 Ok(())
1476 }
1477}
1478
1479/// Copies `path` into the `slot` provided
1480///
1481/// Returns an error if:
1482///
1483/// * the path is too long to fit
1484/// * a nul byte was found
1485/// * an invalid path component is encountered (e.g. a root path or parent dir)
1486/// * the path itself is empty
a1dfa0c6 1487fn copy_path_into(mut slot: &mut [u8], path: &Path, is_link_name: bool) -> io::Result<()> {
7cac9316 1488 let mut emitted = false;
ff7c6d11 1489 let mut needs_slash = false;
7cac9316 1490 for component in path.components() {
8faf50e0 1491 let bytes = path2bytes(Path::new(component.as_os_str()))?;
7cac9316 1492 match (component, is_link_name) {
a1dfa0c6 1493 (Component::Prefix(..), false) | (Component::RootDir, false) => {
74b04a01 1494 return Err(other("paths in archives must be relative"));
7cac9316
XL
1495 }
1496 (Component::ParentDir, false) => {
74b04a01 1497 return Err(other("paths in archives must not have `..`"));
7cac9316 1498 }
ff7c6d11 1499 // Allow "./" as the path
a1dfa0c6 1500 (Component::CurDir, false) if path.components().count() == 1 => {}
7cac9316 1501 (Component::CurDir, false) => continue,
a1dfa0c6 1502 (Component::Normal(_), _) | (_, true) => {}
7cac9316 1503 };
ff7c6d11 1504 if needs_slash {
8faf50e0 1505 copy(&mut slot, b"/")?;
7cac9316
XL
1506 }
1507 if bytes.contains(&b'/') {
1508 if let Component::Normal(..) = component {
a1dfa0c6 1509 return Err(other("path component in archive cannot contain `/`"));
7cac9316
XL
1510 }
1511 }
8faf50e0 1512 copy(&mut slot, &*bytes)?;
ff7c6d11
XL
1513 if &*bytes != b"/" {
1514 needs_slash = true;
1515 }
7cac9316
XL
1516 emitted = true;
1517 }
1518 if !emitted {
a1dfa0c6 1519 return Err(other("paths in archives must have at least one component"));
7cac9316
XL
1520 }
1521 if ends_with_slash(path) {
8faf50e0 1522 copy(&mut slot, &[b'/'])?;
7cac9316
XL
1523 }
1524 return Ok(());
1525
1526 fn copy(slot: &mut &mut [u8], bytes: &[u8]) -> io::Result<()> {
8faf50e0 1527 copy_into(*slot, bytes)?;
7cac9316
XL
1528 let tmp = mem::replace(slot, &mut []);
1529 *slot = &mut tmp[bytes.len()..];
1530 Ok(())
1531 }
1532}
1533
74b04a01
XL
1534#[cfg(target_arch = "wasm32")]
1535fn ends_with_slash(p: &Path) -> bool {
1536 p.to_string_lossy().ends_with('/')
1537}
1538
7cac9316
XL
1539#[cfg(windows)]
1540fn ends_with_slash(p: &Path) -> bool {
1541 let last = p.as_os_str().encode_wide().last();
1542 last == Some(b'/' as u16) || last == Some(b'\\' as u16)
1543}
1544
3dfed10e 1545#[cfg(unix)]
7cac9316
XL
1546fn ends_with_slash(p: &Path) -> bool {
1547 p.as_os_str().as_bytes().ends_with(&[b'/'])
1548}
1549
74b04a01 1550#[cfg(any(windows, target_arch = "wasm32"))]
7cac9316 1551pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
a1dfa0c6
XL
1552 p.as_os_str()
1553 .to_str()
1554 .map(|s| s.as_bytes())
3dfed10e 1555 .ok_or_else(|| other(&format!("path {} was not valid Unicode", p.display())))
a1dfa0c6
XL
1556 .map(|bytes| {
1557 if bytes.contains(&b'\\') {
1558 // Normalize to Unix-style path separators
1559 let mut bytes = bytes.to_owned();
1560 for b in &mut bytes {
1561 if *b == b'\\' {
1562 *b = b'/';
1563 }
7cac9316 1564 }
a1dfa0c6
XL
1565 Cow::Owned(bytes)
1566 } else {
1567 Cow::Borrowed(bytes)
7cac9316 1568 }
a1dfa0c6 1569 })
7cac9316
XL
1570}
1571
3dfed10e 1572#[cfg(unix)]
83c7162d 1573/// On unix this will never fail
7cac9316
XL
1574pub fn path2bytes(p: &Path) -> io::Result<Cow<[u8]>> {
1575 Ok(p.as_os_str().as_bytes()).map(Cow::Borrowed)
1576}
1577
1578#[cfg(windows)]
3dfed10e 1579/// On windows we cannot accept non-Unicode bytes because it
83c7162d 1580/// is impossible to convert it to UTF-16.
7cac9316
XL
1581pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1582 return match bytes {
1583 Cow::Borrowed(bytes) => {
3dfed10e 1584 let s = str::from_utf8(bytes).map_err(|_| not_unicode(bytes))?;
7cac9316
XL
1585 Ok(Cow::Borrowed(Path::new(s)))
1586 }
1587 Cow::Owned(bytes) => {
3dfed10e 1588 let s = String::from_utf8(bytes).map_err(|uerr| not_unicode(&uerr.into_bytes()))?;
7cac9316
XL
1589 Ok(Cow::Owned(PathBuf::from(s)))
1590 }
1591 };
1592
83c7162d
XL
1593 fn not_unicode(v: &[u8]) -> io::Error {
1594 other(&format!(
3dfed10e 1595 "only Unicode paths are supported on Windows: {}",
83c7162d
XL
1596 String::from_utf8_lossy(v)
1597 ))
7cac9316
XL
1598 }
1599}
1600
3dfed10e 1601#[cfg(unix)]
83c7162d 1602/// On unix this operation can never fail.
7cac9316
XL
1603pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1604 use std::ffi::{OsStr, OsString};
1605
1606 Ok(match bytes {
3dfed10e
XL
1607 Cow::Borrowed(bytes) => Cow::Borrowed(Path::new(OsStr::from_bytes(bytes))),
1608 Cow::Owned(bytes) => Cow::Owned(PathBuf::from(OsString::from_vec(bytes))),
7cac9316
XL
1609 })
1610}
74b04a01
XL
1611
1612#[cfg(target_arch = "wasm32")]
1613pub fn bytes2path(bytes: Cow<[u8]>) -> io::Result<Cow<Path>> {
1614 Ok(match bytes {
1615 Cow::Borrowed(bytes) => {
1616 Cow::Borrowed({ Path::new(str::from_utf8(bytes).map_err(invalid_utf8)?) })
1617 }
1618 Cow::Owned(bytes) => {
1619 Cow::Owned({ PathBuf::from(String::from_utf8(bytes).map_err(invalid_utf8)?) })
1620 }
1621 })
1622}
1623
1624#[cfg(target_arch = "wasm32")]
1625fn invalid_utf8<T>(_: T) -> io::Error {
3dfed10e 1626 io::Error::new(io::ErrorKind::InvalidData, "Invalid utf-8")
74b04a01 1627}