1 //! *pxar* format decoder.
3 //! This module contain the code to decode *pxar* archive files.
6 use endian_trait
::Endian
;
8 use super::format_definition
::*;
10 use std
::io
::{Read, Write}
;
11 use std
::path
::{Path, PathBuf}
;
13 use std
::os
::unix
::io
::AsRawFd
;
14 use std
::os
::unix
::io
::RawFd
;
15 use std
::os
::unix
::io
::FromRawFd
;
16 use std
::os
::unix
::ffi
::{OsStringExt}
;
17 use std
::ffi
::{OsStr, OsString}
;
19 use nix
::fcntl
::OFlag
;
20 use nix
::sys
::stat
::Mode
;
21 use nix
::errno
::Errno
;
24 use crate::tools
::io
::ops
::*;
25 use crate::tools
::vec
;
26 use crate::tools
::xattr
;
28 // This one need Read, but works without Seek
29 pub struct SequentialDecoder
<'a
, R
: Read
> {
35 const HEADER_SIZE
: u64 = std
::mem
::size_of
::<CaFormatHeader
>() as u64;
37 impl <'a
, R
: Read
> SequentialDecoder
<'a
, R
> {
39 pub fn new(reader
: &'a
mut R
, feature_flags
: u64) -> Self {
40 let skip_buffer
= vec
::undefined(64*1024);
49 pub (crate) fn get_reader_mut(&mut self) -> & mut R
{
53 pub (crate) fn read_item
<T
: Endian
>(&mut self) -> Result
<T
, Error
> {
55 let mut result
: T
= unsafe { std::mem::uninitialized() }
;
57 let buffer
= unsafe { std
::slice
::from_raw_parts_mut(
58 &mut result
as *mut T
as *mut u8,
59 std
::mem
::size_of
::<T
>()
62 self.reader
.read_exact(buffer
)?
;
67 fn read_link(&mut self, size
: u64) -> Result
<PathBuf
, Error
> {
68 if size
< (HEADER_SIZE
+ 2) {
69 bail
!("dectected short link target.");
71 let target_len
= size
- HEADER_SIZE
;
73 if target_len
> (libc
::PATH_MAX
as u64) {
74 bail
!("link target too long ({}).", target_len
);
77 let mut buffer
= self.reader
.read_exact_allocated(target_len
as usize)?
;
79 let last_byte
= buffer
.pop().unwrap();
81 bail
!("link target not nul terminated.");
84 Ok(PathBuf
::from(std
::ffi
::OsString
::from_vec(buffer
)))
87 fn read_hardlink(&mut self, size
: u64) -> Result
<(PathBuf
, u64), Error
> {
88 if size
< (HEADER_SIZE
+ 8 + 2) {
89 bail
!("dectected short hardlink header.");
91 let offset
: u64 = self.read_item()?
;
92 let target
= self.read_link(size
- 8)?
;
94 for c
in target
.components() {
96 std
::path
::Component
::Normal(_
) => { /* OK */ }
,
98 bail
!("hardlink target contains invalid component {:?}", c
);
106 pub (crate) fn read_filename(&mut self, size
: u64) -> Result
<OsString
, Error
> {
107 if size
< (HEADER_SIZE
+ 2) {
108 bail
!("dectected short filename");
110 let name_len
= size
- HEADER_SIZE
;
112 if name_len
> ((libc
::FILENAME_MAX
as u64) + 1) {
113 bail
!("filename too long ({}).", name_len
);
116 let mut buffer
= self.reader
.read_exact_allocated(name_len
as usize)?
;
118 let last_byte
= buffer
.pop().unwrap();
119 if last_byte
!= 0u8 {
120 bail
!("filename entry not nul terminated.");
123 if (buffer
.len() == 1 && buffer
[0] == b'
.'
) || (buffer
.len() == 2 && buffer
[0] == b'
.'
&& buffer
[1] == b'
.'
) {
124 bail
!("found invalid filename with slashes.");
127 if buffer
.iter().find(|b
| (**b
== b'
/'
)).is_some() {
128 bail
!("found invalid filename with slashes.");
131 let name
= std
::ffi
::OsString
::from_vec(buffer
);
133 bail
!("found empty filename.");
139 fn has_features(&self, feature_flags
: u64) -> bool
{
140 (self.feature_flags
& feature_flags
) == feature_flags
143 fn read_xattr(&mut self, size
: usize) -> Result
<CaFormatXAttr
, Error
> {
144 let buffer
= self.reader
.read_exact_allocated(size
)?
;
146 let separator
= buffer
.iter().position(|c
| *c
== b'
\0'
)
147 .ok_or_else(|| format_err
!("no value found in xattr"))?
;
149 let (name
, value
) = buffer
.split_at(separator
);
150 if !xattr
::is_valid_xattr_name(name
) ||
151 xattr
::is_security_capability(name
)
153 bail
!("incorrect xattr name - {}.", String
::from_utf8_lossy(name
));
158 value
: value
[1..].to_vec(),
162 fn read_fcaps(&mut self, size
: usize) -> Result
<CaFormatFCaps
, Error
> {
163 let buffer
= self.reader
.read_exact_allocated(size
)?
;
165 Ok(CaFormatFCaps { data: buffer }
)
168 fn restore_attributes(&mut self, entry
: &CaFormatEntry
, fd
: RawFd
) -> Result
<CaFormatHeader
, Error
> {
169 let mut xattrs
: Vec
<CaFormatXAttr
> = Vec
::new();
170 let mut fcaps
: Option
<CaFormatFCaps
> = None
;
172 let mut head
: CaFormatHeader
= self.read_item()?
;
173 let mut size
= (head
.size
- HEADER_SIZE
) as usize;
177 if self.has_features(CA_FORMAT_WITH_XATTRS
) {
178 xattrs
.push(self.read_xattr(size
)?
);
180 self.skip_bytes(size
)?
;
184 if self.has_features(CA_FORMAT_WITH_FCAPS
) {
185 fcaps
= Some(self.read_fcaps(size
)?
);
187 self.skip_bytes(size
)?
;
192 head
= self.read_item()?
;
193 size
= (head
.size
- HEADER_SIZE
) as usize;
195 self.restore_xattrs_fcaps_fd(fd
, xattrs
, fcaps
)?
;
200 fn restore_xattrs_fcaps_fd(&mut self, fd
: RawFd
, xattrs
: Vec
<CaFormatXAttr
>, fcaps
: Option
<CaFormatFCaps
>) -> Result
<(), Error
> {
201 for xattr
in xattrs
{
202 if let Err(err
) = xattr
::fsetxattr(fd
, xattr
) {
203 bail
!("fsetxattr failed with error: {}", err
);
206 if let Some(fcaps
) = fcaps
{
207 if let Err(err
) = xattr
::fsetxattr_fcaps(fd
, fcaps
) {
208 bail
!("fsetxattr_fcaps failed with error: {}", err
);
215 fn restore_mode(&mut self, entry
: &CaFormatEntry
, fd
: RawFd
) -> Result
<(), Error
> {
217 let mode
= Mode
::from_bits_truncate((entry
.mode
as u32) & 0o7777);
219 nix
::sys
::stat
::fchmod(fd
, mode
)?
;
224 fn restore_mode_at(&mut self, entry
: &CaFormatEntry
, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
226 let mode
= Mode
::from_bits_truncate((entry
.mode
as u32) & 0o7777);
228 // NOTE: we want :FchmodatFlags::NoFollowSymlink, but fchmodat does not support that
229 // on linux (see man fchmodat). Fortunately, we can simply avoid calling this on symlinks.
230 nix
::sys
::stat
::fchmodat(Some(dirfd
), filename
, mode
, nix
::sys
::stat
::FchmodatFlags
::FollowSymlink
)?
;
235 fn restore_ugid(&mut self, entry
: &CaFormatEntry
, fd
: RawFd
) -> Result
<(), Error
> {
237 let uid
= entry
.uid
as u32;
238 let gid
= entry
.gid
as u32;
240 let res
= unsafe { libc::fchown(fd, uid, gid) }
;
246 fn restore_ugid_at(&mut self, entry
: &CaFormatEntry
, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
248 let uid
= entry
.uid
as u32;
249 let gid
= entry
.gid
as u32;
251 let res
= filename
.with_nix_path(|cstr
| unsafe {
252 libc
::fchownat(dirfd
, cstr
.as_ptr(), uid
, gid
, libc
::AT_SYMLINK_NOFOLLOW
)
259 fn restore_mtime(&mut self, entry
: &CaFormatEntry
, fd
: RawFd
) -> Result
<(), Error
> {
261 let times
= nsec_to_update_timespec(entry
.mtime
);
263 let res
= unsafe { libc::futimens(fd, ×[0]) }
;
269 fn restore_mtime_at(&mut self, entry
: &CaFormatEntry
, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
271 let times
= nsec_to_update_timespec(entry
.mtime
);
273 let res
= filename
.with_nix_path(|cstr
| unsafe {
274 libc
::utimensat(dirfd
, cstr
.as_ptr(), ×
[0], libc
::AT_SYMLINK_NOFOLLOW
)
281 fn restore_device_at(&mut self, entry
: &CaFormatEntry
, dirfd
: RawFd
, filename
: &OsStr
, device
: &CaFormatDevice
) -> Result
<(), Error
> {
283 let rdev
= nix
::sys
::stat
::makedev(device
.major
, device
.minor
);
284 let mode
= ((entry
.mode
as u32) & libc
::S_IFMT
) | 0o0600;
285 let res
= filename
.with_nix_path(|cstr
| unsafe {
286 libc
::mknodat(dirfd
, cstr
.as_ptr(), mode
, rdev
)
293 fn restore_socket_at(&mut self, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
295 let mode
= libc
::S_IFSOCK
| 0o0600;
296 let res
= filename
.with_nix_path(|cstr
| unsafe {
297 libc
::mknodat(dirfd
, cstr
.as_ptr(), mode
, 0)
304 fn restore_fifo_at(&mut self, dirfd
: RawFd
, filename
: &OsStr
) -> Result
<(), Error
> {
306 let mode
= libc
::S_IFIFO
| 0o0600;
307 let res
= filename
.with_nix_path(|cstr
| unsafe {
308 libc
::mkfifoat(dirfd
, cstr
.as_ptr(), mode
)
315 fn skip_bytes(&mut self, count
: usize) -> Result
<(), Error
> {
318 let todo
= count
- done
;
319 let n
= if todo
> self.skip_buffer
.len() { self.skip_buffer.len() }
else { todo }
;
320 let data
= &mut self.skip_buffer
[..n
];
321 self.reader
.read_exact(data
)?
;
327 /// Restore an archive into the specified directory.
329 /// The directory is created if it does not exist.
334 ) -> Result
<(), Error
>
335 where F
: Fn(&Path
) -> Result
<(), Error
>
338 let _
= std
::fs
::create_dir(path
);
340 let dir
= match nix
::dir
::Dir
::open(path
, nix
::fcntl
::OFlag
::O_DIRECTORY
, nix
::sys
::stat
::Mode
::empty()) {
342 Err(err
) => bail
!("unable to open target directory {:?} - {}", path
, err
),
345 let mut relative_path
= PathBuf
::new();
346 self.restore_sequential(path
, &mut relative_path
, &OsString
::new(), &dir
, callback
)
349 fn restore_sequential
<F
>(
352 relative_path
: &mut PathBuf
,
353 filename
: &OsStr
, // repeats path last relative_path component
354 parent
: &nix
::dir
::Dir
,
356 ) -> Result
<(), Error
>
357 where F
: Fn(&Path
) -> Result
<(), Error
>
360 let parent_fd
= parent
.as_raw_fd();
362 let full_path
= base_path
.join(&relative_path
);
364 (callback
)(&full_path
)?
;
366 let head
: CaFormatHeader
= self.read_item()?
;
368 if head
.htype
== PXAR_FORMAT_HARDLINK
{
369 let (target
, _offset
) = self.read_hardlink(head
.size
)?
;
370 let target_path
= base_path
.join(&target
);
371 //println!("HARDLINK: {} {:?} -> {:?}", offset, full_path, target_path);
372 hardlink(&target_path
, &full_path
)?
;
376 check_ca_header
::<CaFormatEntry
>(&head
, CA_FORMAT_ENTRY
)?
;
377 let entry
: CaFormatEntry
= self.read_item()?
;
380 let mode
= entry
.mode
as u32; //fixme: upper 32bits?
382 let ifmt
= mode
& libc
::S_IFMT
;
384 if ifmt
== libc
::S_IFDIR
{
386 if filename
.is_empty() {
387 dir
= nix
::dir
::Dir
::openat(parent_fd
, ".", OFlag
::O_DIRECTORY
, Mode
::empty())?
;
389 dir
= match dir_mkdirat(parent_fd
, filename
, true) {
391 Err(err
) => bail
!("unable to open directory {:?} - {}", full_path
, err
),
395 self.restore_ugid(&entry
, dir
.as_raw_fd())?
;
396 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
397 let mut head
= match self.restore_attributes(&entry
, dir
.as_raw_fd()) {
399 Err(err
) => bail
!("Restoring of directory attributes failed - {}", err
),
402 while head
.htype
== CA_FORMAT_FILENAME
{
403 let name
= self.read_filename(head
.size
)?
;
404 relative_path
.push(&name
);
405 self.restore_sequential(base_path
, relative_path
, &name
, &dir
, callback
)?
;
408 head
= self.read_item()?
;
411 if head
.htype
!= CA_FORMAT_GOODBYE
{
412 bail
!("got unknown header type inside directory entry {:016x}", head
.htype
);
415 //println!("Skip Goodbye");
416 if head
.size
< HEADER_SIZE { bail!("detected short goodbye table"); }
418 self.skip_bytes((head
.size
- HEADER_SIZE
) as usize)?
;
420 self.restore_mode(&entry
, dir
.as_raw_fd())?
;
421 self.restore_mtime(&entry
, dir
.as_raw_fd())?
;
426 if filename
.is_empty() {
427 bail
!("got empty file name at {:?}", full_path
)
430 if ifmt
== libc
::S_IFLNK
{
431 // fixme: create symlink
432 //fixme: restore permission, acls, xattr, ...
434 let head
: CaFormatHeader
= self.read_item()?
;
436 CA_FORMAT_SYMLINK
=> {
437 let target
= self.read_link(head
.size
)?
;
438 //println!("TARGET: {:?}", target);
439 if let Err(err
) = symlinkat(&target
, parent_fd
, filename
) {
440 bail
!("create symlink {:?} failed - {}", full_path
, err
);
444 bail
!("got unknown header type inside symlink entry {:016x}", head
.htype
);
448 // self.restore_mode_at(&entry, parent_fd, filename)?; //not supported on symlinks
449 self.restore_ugid_at(&entry
, parent_fd
, filename
)?
;
450 self.restore_mtime_at(&entry
, parent_fd
, filename
)?
;
455 if ifmt
== libc
::S_IFSOCK
{
457 self.restore_socket_at(parent_fd
, filename
)?
;
459 self.restore_mode_at(&entry
, parent_fd
, filename
)?
;
460 self.restore_ugid_at(&entry
, parent_fd
, filename
)?
;
461 self.restore_mtime_at(&entry
, parent_fd
, filename
)?
;
466 if ifmt
== libc
::S_IFIFO
{
468 self.restore_fifo_at(parent_fd
, filename
)?
;
470 self.restore_mode_at(&entry
, parent_fd
, filename
)?
;
471 self.restore_ugid_at(&entry
, parent_fd
, filename
)?
;
472 self.restore_mtime_at(&entry
, parent_fd
, filename
)?
;
477 if (ifmt
== libc
::S_IFBLK
) || (ifmt
== libc
::S_IFCHR
) {
479 let head
: CaFormatHeader
= self.read_item()?
;
481 CA_FORMAT_DEVICE
=> {
482 let device
: CaFormatDevice
= self.read_item()?
;
483 self.restore_device_at(&entry
, parent_fd
, filename
, &device
)?
;
486 bail
!("got unknown header type inside device entry {:016x}", head
.htype
);
490 self.restore_mode_at(&entry
, parent_fd
, filename
)?
;
491 self.restore_ugid_at(&entry
, parent_fd
, filename
)?
;
492 self.restore_mtime_at(&entry
, parent_fd
, filename
)?
;
497 if ifmt
== libc
::S_IFREG
{
499 let mut read_buffer
: [u8; 64*1024] = unsafe { std::mem::uninitialized() }
;
501 let flags
= OFlag
::O_CREAT
|OFlag
::O_WRONLY
|OFlag
::O_EXCL
;
502 let open_mode
= Mode
::from_bits_truncate(0o0600 | mode
);
504 let mut file
= match file_openat(parent_fd
, filename
, flags
, open_mode
) {
506 Err(err
) => bail
!("open file {:?} failed - {}", full_path
, err
),
509 self.restore_ugid(&entry
, file
.as_raw_fd())?
;
510 // fcaps have to be restored after restore_ugid as chown clears security.capability xattr, see CVE-2015-1350
511 let head
= match self.restore_attributes(&entry
, file
.as_raw_fd()) {
513 Err(err
) => bail
!("Restoring of file attributes failed - {}", err
),
516 if head
.htype
!= CA_FORMAT_PAYLOAD
{
517 bail
!("got unknown header type for file entry {:016x}", head
.htype
);
520 if head
.size
< HEADER_SIZE
{
521 bail
!("detected short payload");
523 let need
= (head
.size
- HEADER_SIZE
) as usize;
524 //self.reader.seek(SeekFrom::Current(need as i64))?;
528 let todo
= need
- done
;
529 let n
= if todo
> read_buffer
.len() { read_buffer.len() }
else { todo }
;
530 let data
= &mut read_buffer
[..n
];
531 self.reader
.read_exact(data
)?
;
532 file
.write_all(data
)?
;
536 self.restore_mode(&entry
, file
.as_raw_fd())?
;
537 self.restore_mtime(&entry
, file
.as_raw_fd())?
;
545 /// List/Dump archive content.
547 /// Simply print the list of contained files. This dumps archive
548 /// format details when the verbose flag is set (useful for debug).
549 pub fn dump_entry
<W
: std
::io
::Write
>(
554 ) -> Result
<(), Error
> {
556 let print_head
= |head
: &CaFormatHeader
| {
557 println
!("Type: {:016x}", head
.htype
);
558 println
!("Size: {}", head
.size
);
561 let head
: CaFormatHeader
= self.read_item()?
;
563 println
!("Path: {:?}", path
);
566 println
!("{:?}", path
);
569 if head
.htype
== PXAR_FORMAT_HARDLINK
{
570 let (target
, offset
) = self.read_hardlink(head
.size
)?
;
572 println
!("Hardlink: {} {:?}", offset
, target
);
577 check_ca_header
::<CaFormatEntry
>(&head
, CA_FORMAT_ENTRY
)?
;
578 let entry
: CaFormatEntry
= self.read_item()?
;
581 println
!("Mode: {:08x} {:08x}", entry
.mode
, (entry
.mode
as u32) & libc
::S_IFDIR
);
583 // fixme: dump attributes (ACLs, ...)
585 let ifmt
= (entry
.mode
as u32) & libc
::S_IFMT
;
587 if ifmt
== libc
::S_IFDIR
{
589 let mut entry_count
= 0;
592 let head
: CaFormatHeader
= self.read_item()?
;
597 //TODO verify the correct order of occurrence
599 let size
= (head
.size
- HEADER_SIZE
) as usize;
600 let xattr
: CaFormatXAttr
= self.read_xattr(size
)?
;
602 println
!("XAttr: {:?}: {:?}", String
::from_utf8_lossy(&xattr
.name
), String
::from_utf8_lossy(&xattr
.value
));
606 let size
= (head
.size
- HEADER_SIZE
) as usize;
607 let fcaps
: CaFormatFCaps
= self.read_fcaps(size
)?
;
609 println
!("FCaps: {:?}", fcaps
);
612 CA_FORMAT_FILENAME
=> {
613 let name
= self.read_filename(head
.size
)?
;
614 if verbose { println!("Name: {:?}
", name); }
617 self.dump_entry(path, verbose, output)?;
620 CA_FORMAT_GOODBYE => {
621 let table_size = (head.size - HEADER_SIZE) as usize;
623 println!("Goodbye
: {:?}
", path);
624 self.dump_goodby_entries(entry_count, table_size)?;
626 self.skip_bytes(table_size)?;
631 panic!("got unexpected header
type inside directory
");
635 } else if (ifmt == libc::S_IFBLK) || (ifmt == libc::S_IFCHR) ||
636 (ifmt == libc::S_IFLNK) || (ifmt == libc::S_IFREG)
639 let head: CaFormatHeader = self.read_item()?;
646 CA_FORMAT_SYMLINK => {
647 let target = self.read_link(head.size)?;
649 println!("Symlink
: {:?}
", target);
653 CA_FORMAT_DEVICE => {
654 let device: CaFormatDevice = self.read_item()?;
656 println!("Device
: {}
, {}
", device.major, device.minor);
661 let size = (head.size - HEADER_SIZE) as usize;
662 let xattr: CaFormatXAttr = self.read_xattr(size)?;
664 println!("XAttr
: {:?}
: {:?}
", String::from_utf8_lossy(&xattr.name), String::from_utf8_lossy(&xattr.value));
668 let size = (head.size - HEADER_SIZE) as usize;
669 let fcaps: CaFormatFCaps = self.read_fcaps(size)?;
671 println!("FCaps
: {:?}
", fcaps);
674 CA_FORMAT_PAYLOAD => {
675 let payload_size = (head.size - HEADER_SIZE) as usize;
677 println!("Payload
: {}
", payload_size);
679 self.skip_bytes(payload_size)?;
683 panic!("got unexpected header
type inside non
-directory
");
687 } else if ifmt == libc::S_IFIFO {
691 } else if ifmt == libc::S_IFSOCK {
696 panic!("unknown st_mode
");
701 fn dump_goodby_entries(
705 ) -> Result<(), Error> {
707 const GOODBYE_ITEM_SIZE: usize = std::mem::size_of::<CaFormatGoodbyeItem>();
709 if table_size < GOODBYE_ITEM_SIZE {
710 bail!("Goodbye table to
small ({}
< {}
)", table_size, GOODBYE_ITEM_SIZE);
712 if (table_size % GOODBYE_ITEM_SIZE) != 0 {
713 bail!("Goodbye table with strange
size ({}
)", table_size);
716 let entries = table_size / GOODBYE_ITEM_SIZE;
718 if entry_count != (entries - 1) {
719 bail!("Goodbye table with wrong entry
count ({}
!= {}
)", entry_count, entries - 1);
725 let item: CaFormatGoodbyeItem = self.read_item()?;
727 if item.hash == CA_FORMAT_GOODBYE_TAIL_MARKER {
728 if count != entries {
729 bail!("unexpected goodbye tail marker
");
731 println!("Goodby tail mark
.");
734 println!("Goodby item
: offset {}
, size {}
, hash {:016x}
", item.offset, item.size, item.hash);
735 if count >= entries {
736 bail!("too many goodbye
items (no tail marker
)");
744 fn file_openat(parent: RawFd, filename: &OsStr, flags: OFlag, mode: Mode) -> Result<std::fs::File, Error> {
746 let fd = filename.with_nix_path(|cstr| {
747 nix::fcntl::openat(parent, cstr.as_ref(), flags, mode)
750 let file = unsafe { std::fs::File::from_raw_fd(fd) };
755 fn dir_mkdirat(parent: RawFd, filename: &OsStr, create_new: bool) -> Result<nix::dir::Dir, nix::Error> {
757 // call mkdirat first
758 let res = filename.with_nix_path(|cstr| unsafe {
759 libc::mkdirat(parent, cstr.as_ptr(), libc::S_IRWXU)
762 match Errno::result(res) {
765 if err == nix::Error::Sys(nix::errno::Errno::EEXIST) {
766 if create_new { return Err(err); }
773 let dir = nix::dir::Dir::openat(parent, filename, OFlag::O_DIRECTORY, Mode::empty())?;
778 fn hardlink(oldpath: &Path, newpath: &Path) -> Result<(), Error> {
779 oldpath.with_nix_path(|oldpath| {
780 newpath.with_nix_path(|newpath| {
781 let res = unsafe { libc::link(oldpath.as_ptr(), newpath.as_ptr()) };
788 fn symlinkat(target: &Path, parent: RawFd, linkname: &OsStr) -> Result<(), Error> {
790 target.with_nix_path(|target| {
791 linkname.with_nix_path(|linkname| {
792 let res = unsafe { libc::symlinkat(target.as_ptr(), parent, linkname.as_ptr()) };
799 fn nsec_to_update_timespec(mtime_nsec: u64) -> [libc::timespec; 2] {
802 const UTIME_OMIT: i64 = ((1 << 30) - 2);
803 const NANOS_PER_SEC: i64 = 1_000_000_000;
805 let sec = (mtime_nsec as i64) / NANOS_PER_SEC;
806 let nsec = (mtime_nsec as i64) % NANOS_PER_SEC;
808 let times: [libc::timespec; 2] = [
809 libc::timespec { tv_sec: 0, tv_nsec: UTIME_OMIT },
810 libc::timespec { tv_sec: sec, tv_nsec: nsec },