1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Write operations trait for archives.
use super::ArchiveRead;
use crate::BaleError;
use std::path::Path;
use std::time::SystemTime;
/// Write operations for archives.
///
/// This trait is only implemented for `Archive<MappedArchiveMut>`.
pub trait ArchiveWrite: ArchiveRead {
/// Adds an entry from raw data.
///
/// If an entry with the same path already exists, the new entry shadows it.
/// The old data remains in the archive (orphaned) until compact.
///
/// # Arguments
///
/// * `path` - Archive path for the entry
/// * `data` - File contents
/// * `mode` - Unix file permissions (e.g., 0o644)
///
/// # Errors
///
/// Returns an error if:
/// - The path exceeds the archive's path_size
/// - The data size exceeds 4GB (ZIP format limitation)
/// - The archive offset would exceed 4GB (ZIP format limitation)
/// - Writing to the archive fails
fn add_entry(&mut self, path: &str, data: &[u8], mode: u32) -> Result<(), BaleError>;
/// Adds an entry from raw data with a specific modification time.
///
/// If an entry with the same path already exists, the new entry shadows it.
/// The old data remains in the archive (orphaned) until compact.
///
/// # Arguments
///
/// * `path` - Archive path for the entry
/// * `data` - File contents
/// * `mode` - Unix file permissions (e.g., 0o644)
/// * `mtime` - Modification time (None uses current time)
///
/// # Errors
///
/// Returns an error if:
/// - The path exceeds the archive's path_size
/// - The data size exceeds 4GB (ZIP format limitation)
/// - The archive offset would exceed 4GB (ZIP format limitation)
/// - Writing to the archive fails
fn add_entry_with_mtime(
&mut self,
path: &str,
data: &[u8],
mode: u32,
mtime: Option<SystemTime>,
) -> Result<(), BaleError>;
/// Adds a file from the filesystem to the archive.
///
/// # Memory usage
///
/// This method reads the entire file into memory before writing to the
/// archive. For very large files, consider using [`add_entry()`](Self::add_entry)
/// with a streaming approach, or ensure sufficient memory is available.
///
/// # Arguments
///
/// * `src` - Path to the source file
/// * `archive_path` - Path within the archive
///
/// # Errors
///
/// Returns an error if:
/// - The source file cannot be read
/// - The archive path exceeds path_size
/// - The file size exceeds 4GB (ZIP format limitation)
/// - Writing to the archive fails
fn add_file(&mut self, src: impl AsRef<Path>, archive_path: &str) -> Result<(), BaleError>;
/// Deletes all entries matching a path.
///
/// Removes all matching entries from the Central Directory. If duplicate
/// entries exist (from shadowing), all are removed. The file data remains
/// in the archive (orphaned) until a compact operation.
///
/// Returns `true` if any entries were deleted, `false` if none matched.
fn delete(&mut self, path: &str) -> bool;
/// Flushes all changes to disk.
///
/// Rewrites the Central Directory and full trailer (ZIP64 EOCD, ZIP64 EOCD
/// Locator, EOCD, and BaleEocd). The CD starts at an aligned offset for
/// efficient mmap access. The file is truncated to the logical size.
///
/// If no changes have been made since the last sync, this is a no-op.
///
/// # Errors
///
/// Returns an error if writing or syncing fails.
fn sync(&mut self) -> Result<(), BaleError>;
/// Creates an explicit directory entry.
///
/// Directory entries have zero-length data and directory mode bits set.
/// The path should not have a trailing slash; it will be added internally
/// if needed for ZIP compatibility.
///
/// # Arguments
///
/// * `path` - Archive path for the directory
/// * `mode` - Unix directory permissions (e.g., 0o755). The directory type
/// bits (0o040000) will be added automatically if not present.
///
/// # Errors
///
/// Returns an error if:
/// - The path exceeds the archive's path_size
/// - Writing to the archive fails
fn add_folder(&mut self, path: impl AsRef<str>, mode: u32) -> Result<(), BaleError>;
/// Creates a symbolic link entry.
///
/// Symlink entries store the target path as their data, with symlink mode
/// bits set in external attributes.
///
/// # Arguments
///
/// * `path` - Archive path for the symlink
/// * `target` - The symlink target (what the link points to)
/// * `mode` - Unix permissions (e.g., 0o777). The symlink type bits
/// (0o120000) will be added automatically if not present.
///
/// # Errors
///
/// Returns an error if:
/// - The path exceeds the archive's path_size
/// - The target length exceeds 4GB
/// - Writing to the archive fails
fn add_symlink(
&mut self,
path: impl AsRef<str>,
target: impl AsRef<str>,
mode: u32,
) -> Result<(), BaleError>;
}