libarchive2/
extract.rs

1//! Archive extraction functionality
2
3use crate::entry::EntryMut;
4use crate::error::{Error, Result};
5use std::ops::{BitOr, BitOrAssign};
6
7/// Flags for controlling extraction behavior
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct ExtractFlags(i32);
10
11impl ExtractFlags {
12    /// No special extraction flags
13    pub const NONE: ExtractFlags = ExtractFlags(0);
14
15    /// Set owner/group on extracted files
16    pub const OWNER: ExtractFlags = ExtractFlags(0x0001);
17
18    /// Restore file permissions
19    pub const PERM: ExtractFlags = ExtractFlags(0x0002);
20
21    /// Restore modification time
22    pub const TIME: ExtractFlags = ExtractFlags(0x0004);
23
24    /// Don't overwrite existing files
25    pub const NO_OVERWRITE: ExtractFlags = ExtractFlags(0x0008);
26
27    /// Unlink file before creating
28    pub const UNLINK: ExtractFlags = ExtractFlags(0x0010);
29
30    /// Restore ACLs (Access Control Lists)
31    pub const ACL: ExtractFlags = ExtractFlags(0x0020);
32
33    /// Restore file flags (e.g., immutable, append-only)
34    pub const FFLAGS: ExtractFlags = ExtractFlags(0x0040);
35
36    /// Restore extended attributes
37    pub const XATTR: ExtractFlags = ExtractFlags(0x0080);
38
39    /// Guard against symlink attacks
40    pub const SECURE_SYMLINKS: ExtractFlags = ExtractFlags(0x0100);
41
42    /// Reject entries with '..' in path
43    pub const SECURE_NODOTDOT: ExtractFlags = ExtractFlags(0x0200);
44
45    /// Don't create parent directories automatically
46    pub const NO_AUTODIR: ExtractFlags = ExtractFlags(0x0400);
47
48    /// Don't overwrite newer files
49    pub const NO_OVERWRITE_NEWER: ExtractFlags = ExtractFlags(0x0800);
50
51    /// Write sparse files with holes
52    pub const SPARSE: ExtractFlags = ExtractFlags(0x1000);
53
54    /// Restore Mac OS metadata
55    pub const MAC_METADATA: ExtractFlags = ExtractFlags(0x2000);
56
57    /// Don't use HFS+ compression
58    pub const NO_HFS_COMPRESSION: ExtractFlags = ExtractFlags(0x4000);
59
60    /// Force HFS+ compression
61    pub const HFS_COMPRESSION_FORCED: ExtractFlags = ExtractFlags(0x8000);
62
63    /// Reject absolute paths
64    pub const SECURE_NOABSOLUTEPATHS: ExtractFlags = ExtractFlags(0x10000);
65
66    /// Clear no-change flags when unlinking
67    pub const CLEAR_NOCHANGE_FFLAGS: ExtractFlags = ExtractFlags(0x20000);
68
69    /// Use safe writes (rename after extraction)
70    pub const SAFE_WRITES: ExtractFlags = ExtractFlags(0x40000);
71
72    /// Get the raw integer value of the flags
73    pub fn bits(&self) -> i32 {
74        self.0
75    }
76}
77
78impl BitOr for ExtractFlags {
79    type Output = Self;
80
81    fn bitor(self, rhs: Self) -> Self::Output {
82        ExtractFlags(self.0 | rhs.0)
83    }
84}
85
86impl BitOrAssign for ExtractFlags {
87    fn bitor_assign(&mut self, rhs: Self) {
88        self.0 |= rhs.0;
89    }
90}
91
92/// Archive writer for extracting entries to disk
93///
94/// This provides the `archive_write_disk` API for writing archive entries
95/// directly to the filesystem.
96///
97/// # Thread Safety
98///
99/// `WriteDisk` is `Send` but not `Sync`. You can transfer ownership between threads,
100/// but cannot share references across threads.
101pub struct WriteDisk {
102    archive: *mut libarchive2_sys::archive,
103}
104
105// SAFETY: WriteDisk can be sent between threads because the archive pointer
106// is owned exclusively by this instance and libarchive write_disk objects
107// can be used from different threads (just not concurrently).
108unsafe impl Send for WriteDisk {}
109
110// Note: WriteDisk is NOT Sync because libarchive archives are not thread-safe
111// for concurrent access.
112
113impl WriteDisk {
114    /// Create a new disk writer
115    pub fn new() -> Result<Self> {
116        unsafe {
117            let archive = libarchive2_sys::archive_write_disk_new();
118            if archive.is_null() {
119                return Err(Error::NullPointer);
120            }
121            Ok(WriteDisk { archive })
122        }
123    }
124
125    /// Set extraction options
126    pub fn set_options(&mut self, flags: ExtractFlags) -> Result<()> {
127        unsafe {
128            Error::from_return_code(
129                libarchive2_sys::archive_write_disk_set_options(self.archive, flags.bits()),
130                self.archive,
131            )?;
132        }
133        Ok(())
134    }
135
136    /// Use standard lookup functions for user/group names
137    ///
138    /// This enables looking up uid/gid from uname/gname using system calls
139    pub fn set_standard_lookup(&mut self) -> Result<()> {
140        unsafe {
141            Error::from_return_code(
142                libarchive2_sys::archive_write_disk_set_standard_lookup(self.archive),
143                self.archive,
144            )?;
145        }
146        Ok(())
147    }
148
149    /// Write an entry header to disk
150    ///
151    /// This creates the file/directory/etc on disk
152    pub fn write_header(&mut self, entry: &EntryMut) -> Result<()> {
153        // Set locale to UTF-8 on Windows to handle non-ASCII filenames correctly
154        let _guard = crate::locale::WindowsUTF8LocaleGuard::new();
155
156        unsafe {
157            Error::from_return_code(
158                libarchive2_sys::archive_write_header(self.archive, entry.entry),
159                self.archive,
160            )?;
161        }
162        Ok(())
163    }
164
165    /// Write data for the current entry
166    pub fn write_data(&mut self, data: &[u8]) -> Result<usize> {
167        unsafe {
168            let ret = libarchive2_sys::archive_write_data(
169                self.archive,
170                data.as_ptr() as *const std::os::raw::c_void,
171                data.len(),
172            );
173
174            if ret < 0 {
175                Err(Error::from_archive(self.archive))
176            } else {
177                Ok(ret as usize)
178            }
179        }
180    }
181
182    /// Finish writing the current entry
183    pub fn finish_entry(&mut self) -> Result<()> {
184        unsafe {
185            Error::from_return_code(
186                libarchive2_sys::archive_write_finish_entry(self.archive),
187                self.archive,
188            )?;
189        }
190        Ok(())
191    }
192
193    /// Close and free the disk writer
194    pub fn close(mut self) -> Result<()> {
195        unsafe {
196            if !self.archive.is_null() {
197                Error::from_return_code(
198                    libarchive2_sys::archive_write_close(self.archive),
199                    self.archive,
200                )?;
201                libarchive2_sys::archive_write_free(self.archive);
202                self.archive = std::ptr::null_mut();
203            }
204        }
205        Ok(())
206    }
207}
208
209impl Drop for WriteDisk {
210    fn drop(&mut self) {
211        unsafe {
212            if !self.archive.is_null() {
213                libarchive2_sys::archive_write_close(self.archive);
214                libarchive2_sys::archive_write_free(self.archive);
215            }
216        }
217    }
218}
219
220// Note: Default implementation removed because disk writer creation can fail.
221// Use WriteDisk::new() instead.