]>
Commit | Line | Data |
---|---|---|
4fa71e05 DM |
1 | //! *catar* format encoder. |
2 | //! | |
3 | //! This module contain the code to generate *catar* archive files. | |
4 | ||
fb8365b7 | 5 | use failure::*; |
4f6892eb | 6 | use endian_trait::Endian; |
fb8365b7 DM |
7 | |
8 | use super::format_definition::*; | |
95bd5dfe | 9 | use super::binary_search_tree::*; |
fb8365b7 DM |
10 | |
11 | use std::io::Write; | |
12 | use std::os::unix::io::AsRawFd; | |
13 | use std::os::unix::ffi::OsStrExt; | |
45281d49 | 14 | use std::os::unix::io::RawFd; |
d2b03f23 | 15 | use std::path::{Path, PathBuf}; |
45281d49 | 16 | |
a0cc09b5 | 17 | use std::ffi::CStr; |
248c17af | 18 | |
2e4ae0e2 | 19 | use nix::NixPath; |
0ff55999 DM |
20 | use nix::fcntl::OFlag; |
21 | use nix::sys::stat::Mode; | |
d2b03f23 | 22 | use nix::errno::Errno; |
3192ae96 | 23 | use nix::sys::stat::FileStat; |
fb8365b7 | 24 | |
389e5625 DM |
25 | /// The format requires to build sorted directory lookup tables in |
26 | /// memory, so we restrict the number of allowed entries to limit | |
27 | /// maximum memory usage. | |
28 | pub const MAX_DIRECTORY_ENTRIES: usize = 256*1024; | |
2e4ae0e2 | 29 | |
5e7a09be | 30 | pub struct CaTarEncoder<'a, W: Write> { |
d2b03f23 | 31 | current_path: PathBuf, // used for error reporting |
5e7a09be | 32 | writer: &'a mut W, |
248c17af | 33 | writer_pos: usize, |
9f49fe1d | 34 | _size: usize, |
248c17af | 35 | file_copy_buffer: Vec<u8>, |
79c281fd DM |
36 | all_file_systems: bool, |
37 | root_st_dev: u64, | |
219ef0e6 | 38 | verbose: bool, |
fb8365b7 DM |
39 | } |
40 | ||
5e7a09be | 41 | impl <'a, W: Write> CaTarEncoder<'a, W> { |
a0cc09b5 | 42 | |
4c0fd487 DM |
43 | pub fn encode( |
44 | path: PathBuf, | |
45 | dir: &mut nix::dir::Dir, | |
219ef0e6 | 46 | writer: &'a mut W, |
eed6db39 | 47 | all_file_systems: bool, |
219ef0e6 | 48 | verbose: bool, |
4c0fd487 | 49 | ) -> Result<(), Error> { |
248c17af | 50 | |
389e5625 DM |
51 | const FILE_COPY_BUFFER_SIZE: usize = 1024*1024; |
52 | ||
248c17af DM |
53 | let mut file_copy_buffer = Vec::with_capacity(FILE_COPY_BUFFER_SIZE); |
54 | unsafe { file_copy_buffer.set_len(FILE_COPY_BUFFER_SIZE); } | |
55 | ||
fb8365b7 DM |
56 | |
57 | // todo: use scandirat?? | |
4f6892eb | 58 | |
13d98013 DM |
59 | let dir_fd = dir.as_raw_fd(); |
60 | let stat = match nix::sys::stat::fstat(dir_fd) { | |
4f6892eb | 61 | Ok(stat) => stat, |
79c281fd | 62 | Err(err) => bail!("fstat {:?} failed - {}", path, err), |
4f6892eb DM |
63 | }; |
64 | ||
65 | if (stat.st_mode & libc::S_IFMT) != libc::S_IFDIR { | |
79c281fd | 66 | bail!("got unexpected file type {:?} (not a directory)", path); |
4f6892eb DM |
67 | } |
68 | ||
13d98013 | 69 | let magic = detect_fs_type(dir_fd)?; |
1c4804cf | 70 | |
a8a04956 DM |
71 | if is_virtual_file_system(magic) { |
72 | bail!("backup virtual file systems is disabled!"); | |
73 | } | |
74 | ||
79c281fd DM |
75 | let mut me = Self { |
76 | current_path: path, | |
77 | writer: writer, | |
78 | writer_pos: 0, | |
79 | _size: 0, | |
80 | file_copy_buffer, | |
81 | all_file_systems, | |
82 | root_st_dev: stat.st_dev, | |
83 | verbose, | |
84 | }; | |
4c0fd487 | 85 | |
219ef0e6 DM |
86 | if verbose { println!("{:?}", me.current_path); } |
87 | ||
1c4804cf | 88 | me.encode_dir(dir, &stat, magic)?; |
fb8365b7 DM |
89 | |
90 | Ok(()) | |
91 | } | |
92 | ||
248c17af | 93 | fn write(&mut self, buf: &[u8]) -> Result<(), Error> { |
c7fa4872 | 94 | self.writer.write_all(buf)?; |
248c17af DM |
95 | self.writer_pos += buf.len(); |
96 | Ok(()) | |
97 | } | |
98 | ||
8c1dfa6c | 99 | fn write_item<T: Endian>(&mut self, item: T) -> Result<(), Error> { |
4f6892eb | 100 | |
8c1dfa6c | 101 | let data = item.to_le(); |
4f6892eb DM |
102 | |
103 | let buffer = unsafe { std::slice::from_raw_parts( | |
104 | &data as *const T as *const u8, | |
105 | std::mem::size_of::<T>() | |
106 | )}; | |
107 | ||
108 | self.write(buffer)?; | |
109 | ||
110 | Ok(()) | |
111 | } | |
112 | ||
248c17af | 113 | fn flush_copy_buffer(&mut self, size: usize) -> Result<(), Error> { |
c7fa4872 | 114 | self.writer.write_all(&self.file_copy_buffer[..size])?; |
248c17af DM |
115 | self.writer_pos += size; |
116 | Ok(()) | |
117 | } | |
118 | ||
3192ae96 DM |
119 | fn write_header(&mut self, htype: u64, size: u64) -> Result<(), Error> { |
120 | ||
4f6892eb | 121 | let size = size + (std::mem::size_of::<CaFormatHeader>() as u64); |
8c1dfa6c | 122 | self.write_item(CaFormatHeader { size, htype })?; |
248c17af DM |
123 | |
124 | Ok(()) | |
125 | } | |
126 | ||
127 | fn write_filename(&mut self, name: &CStr) -> Result<(), Error> { | |
128 | ||
129 | let buffer = name.to_bytes_with_nul(); | |
130 | self.write_header(CA_FORMAT_FILENAME, buffer.len() as u64)?; | |
131 | self.write(buffer)?; | |
3192ae96 DM |
132 | |
133 | Ok(()) | |
248c17af | 134 | } |
3192ae96 | 135 | |
4f6892eb | 136 | fn create_entry(&self, stat: &FileStat) -> Result<CaFormatEntry, Error> { |
3192ae96 | 137 | |
4f6892eb DM |
138 | let mode = if (stat.st_mode & libc::S_IFMT) == libc::S_IFLNK { |
139 | (libc::S_IFLNK | 0o777) as u64 | |
140 | } else { | |
141 | (stat.st_mode & (libc::S_IFMT | 0o7777)) as u64 | |
142 | }; | |
3192ae96 | 143 | |
4f6892eb DM |
144 | let mtime = stat.st_mtime * 1_000_000_000 + stat.st_mtime_nsec; |
145 | if mtime < 0 { | |
146 | bail!("got strange mtime ({}) from fstat for {:?}.", mtime, self.current_path); | |
147 | } | |
3192ae96 | 148 | |
3192ae96 | 149 | |
4f6892eb | 150 | let entry = CaFormatEntry { |
b41d1aab | 151 | feature_flags: CA_FORMAT_DEFAULT, // fixme: ?? |
4f6892eb DM |
152 | mode: mode, |
153 | flags: 0, | |
154 | uid: stat.st_uid as u64, | |
155 | gid: stat.st_gid as u64, | |
156 | mtime: mtime as u64, | |
157 | }; | |
3192ae96 | 158 | |
4f6892eb DM |
159 | Ok(entry) |
160 | } | |
3192ae96 | 161 | |
4f6892eb | 162 | fn read_chattr(&self, fd: RawFd, entry: &mut CaFormatEntry) -> Result<(), Error> { |
3192ae96 | 163 | |
5c76c2f3 DM |
164 | let mut attr: usize = 0; |
165 | ||
166 | let res = unsafe { read_attr_fd(fd, &mut attr)}; | |
167 | if let Err(err) = res { | |
168 | if let nix::Error::Sys(errno) = err { | |
169 | if errno_is_unsupported(errno) { return Ok(()) }; | |
170 | } | |
171 | bail!("read_attr_fd failed for {:?} - {}", self.current_path, err); | |
172 | } | |
173 | ||
174 | let flags = ca_feature_flags_from_chattr(attr as u32); | |
175 | entry.flags = entry.flags | flags; | |
176 | ||
177 | Ok(()) | |
178 | } | |
179 | ||
d7bfac86 DM |
180 | fn read_fat_attr(&self, fd: RawFd, magic: i64, entry: &mut CaFormatEntry) -> Result<(), Error> { |
181 | ||
182 | if magic != MSDOS_SUPER_MAGIC && magic != FUSE_SUPER_MAGIC { return Ok(()); } | |
5c76c2f3 DM |
183 | |
184 | let mut attr: u32 = 0; | |
185 | ||
186 | let res = unsafe { read_fat_attr_fd(fd, &mut attr)}; | |
187 | if let Err(err) = res { | |
188 | if let nix::Error::Sys(errno) = err { | |
189 | if errno_is_unsupported(errno) { return Ok(()) }; | |
190 | } | |
191 | bail!("read_fat_attr_fd failed for {:?} - {}", self.current_path, err); | |
4f6892eb DM |
192 | } |
193 | ||
5c76c2f3 DM |
194 | let flags = ca_feature_flags_from_fat_attr(attr); |
195 | entry.flags = entry.flags | flags; | |
196 | ||
4f6892eb DM |
197 | Ok(()) |
198 | } | |
3192ae96 | 199 | |
8c1dfa6c | 200 | fn write_entry(&mut self, entry: CaFormatEntry) -> Result<(), Error> { |
4f6892eb DM |
201 | |
202 | self.write_header(CA_FORMAT_ENTRY, std::mem::size_of::<CaFormatEntry>() as u64)?; | |
203 | self.write_item(entry)?; | |
3192ae96 DM |
204 | |
205 | Ok(()) | |
206 | } | |
d2b03f23 | 207 | |
1b0dc9f6 DM |
208 | fn write_goodbye_table(&mut self, goodbye_offset: usize, goodbye_items: &mut [CaFormatGoodbyeItem]) -> Result<(), Error> { |
209 | ||
210 | goodbye_items.sort_unstable_by(|a, b| a.hash.cmp(&b.hash)); | |
985567fb DM |
211 | |
212 | let item_count = goodbye_items.len(); | |
213 | ||
214 | let goodbye_table_size = (item_count + 1)*std::mem::size_of::<CaFormatGoodbyeItem>(); | |
215 | ||
216 | self.write_header(CA_FORMAT_GOODBYE, goodbye_table_size as u64)?; | |
217 | ||
9409255a DM |
218 | if self.file_copy_buffer.len() < goodbye_table_size { |
219 | let need = goodbye_table_size - self.file_copy_buffer.len(); | |
389e5625 DM |
220 | self.file_copy_buffer.reserve(need); |
221 | unsafe { self.file_copy_buffer.set_len(self.file_copy_buffer.capacity()); } | |
985567fb DM |
222 | } |
223 | ||
224 | let buffer = &mut self.file_copy_buffer; | |
985567fb DM |
225 | |
226 | copy_binary_search_tree(item_count, |s, d| { | |
227 | let item = &goodbye_items[s]; | |
228 | let offset = d*std::mem::size_of::<CaFormatGoodbyeItem>(); | |
229 | let dest = crate::tools::map_struct_mut::<CaFormatGoodbyeItem>(&mut buffer[offset..]).unwrap(); | |
230 | dest.offset = u64::to_le(item.offset); | |
231 | dest.size = u64::to_le(item.size); | |
232 | dest.hash = u64::to_le(item.hash); | |
233 | }); | |
234 | ||
235 | // append CaFormatGoodbyeTail as last item | |
236 | let offset = item_count*std::mem::size_of::<CaFormatGoodbyeItem>(); | |
237 | let dest = crate::tools::map_struct_mut::<CaFormatGoodbyeItem>(&mut buffer[offset..]).unwrap(); | |
238 | dest.offset = u64::to_le(goodbye_offset as u64); | |
239 | dest.size = u64::to_le((goodbye_table_size + std::mem::size_of::<CaFormatHeader>()) as u64); | |
240 | dest.hash = u64::to_le(CA_FORMAT_GOODBYE_TAIL_MARKER); | |
241 | ||
242 | self.flush_copy_buffer(goodbye_table_size)?; | |
243 | ||
244 | Ok(()) | |
245 | } | |
246 | ||
1c4804cf | 247 | fn encode_dir(&mut self, dir: &mut nix::dir::Dir, dir_stat: &FileStat, magic: i64) -> Result<(), Error> { |
fb8365b7 | 248 | |
0433db19 | 249 | //println!("encode_dir: {:?} start {}", self.current_path, self.writer_pos); |
fb8365b7 DM |
250 | |
251 | let mut name_list = vec![]; | |
252 | ||
253 | let rawfd = dir.as_raw_fd(); | |
254 | ||
4f6892eb | 255 | let dir_start_pos = self.writer_pos; |
fb8365b7 | 256 | |
4f6892eb | 257 | let mut dir_entry = self.create_entry(&dir_stat)?; |
2e4ae0e2 | 258 | |
4f6892eb | 259 | self.read_chattr(rawfd, &mut dir_entry)?; |
d7bfac86 | 260 | self.read_fat_attr(rawfd, magic, &mut dir_entry)?; |
3192ae96 | 261 | |
8c1dfa6c | 262 | self.write_entry(dir_entry)?; |
248c17af | 263 | |
389e5625 DM |
264 | let mut dir_count = 0; |
265 | ||
79c281fd | 266 | let include_children; |
4c0fd487 DM |
267 | if is_virtual_file_system(magic) { |
268 | include_children = false; | |
269 | } else { | |
79c281fd | 270 | include_children = (self.root_st_dev == dir_stat.st_dev) || self.all_file_systems; |
4c0fd487 DM |
271 | } |
272 | ||
273 | if include_children { | |
13d98013 DM |
274 | for entry in dir.iter() { |
275 | dir_count += 1; | |
276 | if dir_count > MAX_DIRECTORY_ENTRIES { | |
277 | bail!("too many directory items in {:?} (> {})", | |
278 | self.current_path, MAX_DIRECTORY_ENTRIES); | |
279 | } | |
389e5625 | 280 | |
13d98013 DM |
281 | let entry = match entry { |
282 | Ok(entry) => entry, | |
283 | Err(err) => bail!("readir {:?} failed - {}", self.current_path, err), | |
284 | }; | |
285 | let filename = entry.file_name().to_owned(); | |
fb8365b7 | 286 | |
13d98013 DM |
287 | let name = filename.to_bytes_with_nul(); |
288 | let name_len = name.len(); | |
289 | if name_len == 2 && name[0] == b'.' && name[1] == 0u8 { continue; } | |
290 | if name_len == 3 && name[0] == b'.' && name[1] == b'.' && name[2] == 0u8 { continue; } | |
fb8365b7 | 291 | |
13d98013 DM |
292 | name_list.push(filename); |
293 | } | |
65092b1e DM |
294 | } else { |
295 | eprintln!("skip mount point: {:?}", self.current_path); | |
fb8365b7 DM |
296 | } |
297 | ||
4f6892eb | 298 | name_list.sort_unstable_by(|a, b| a.cmp(&b)); |
fb8365b7 | 299 | |
985567fb | 300 | let mut goodbye_items = vec![]; |
a0cc09b5 | 301 | |
4f6892eb | 302 | for filename in &name_list { |
45281d49 | 303 | self.current_path.push(std::ffi::OsStr::from_bytes(filename.as_bytes())); |
fb8365b7 | 304 | |
219ef0e6 DM |
305 | if self.verbose { println!("{:?}", self.current_path); } |
306 | ||
4f6892eb DM |
307 | let stat = match nix::sys::stat::fstatat(rawfd, filename.as_ref(), nix::fcntl::AtFlags::AT_SYMLINK_NOFOLLOW) { |
308 | Ok(stat) => stat, | |
309 | Err(nix::Error::Sys(Errno::ENOENT)) => { | |
310 | self.report_vanished_file(&self.current_path)?; | |
311 | continue; | |
312 | } | |
313 | Err(err) => bail!("fstat {:?} failed - {}", self.current_path, err), | |
314 | }; | |
315 | ||
a0cc09b5 DM |
316 | let start_pos = self.writer_pos; |
317 | ||
a7e37131 DM |
318 | let ifmt = stat.st_mode & libc::S_IFMT; |
319 | ||
320 | if ifmt == libc::S_IFDIR { | |
a0cc09b5 | 321 | |
1c4804cf DM |
322 | let mut dir = match nix::dir::Dir::openat(rawfd, filename.as_ref(), OFlag::O_DIRECTORY|OFlag::O_NOFOLLOW, Mode::empty()) { |
323 | Ok(dir) => dir, | |
324 | Err(nix::Error::Sys(Errno::ENOENT)) => { | |
325 | self.report_vanished_file(&self.current_path)?; | |
326 | continue; // fixme!! | |
327 | }, | |
d2b03f23 | 328 | Err(err) => bail!("open dir {:?} failed - {}", self.current_path, err), |
1c4804cf DM |
329 | }; |
330 | ||
331 | let child_magic = if dir_stat.st_dev != stat.st_dev { | |
13d98013 | 332 | detect_fs_type(dir.as_raw_fd())? |
1c4804cf DM |
333 | } else { |
334 | magic | |
335 | }; | |
336 | ||
337 | self.write_filename(&filename)?; | |
338 | self.encode_dir(&mut dir, &stat, child_magic)?; | |
3192ae96 | 339 | |
a7e37131 | 340 | } else if ifmt == libc::S_IFREG { |
1c4804cf DM |
341 | let filefd = match nix::fcntl::openat(rawfd, filename.as_ref(), OFlag::O_NOFOLLOW, Mode::empty()) { |
342 | Ok(filefd) => filefd, | |
343 | Err(nix::Error::Sys(Errno::ENOENT)) => { | |
344 | self.report_vanished_file(&self.current_path)?; | |
345 | continue; | |
346 | }, | |
d2b03f23 | 347 | Err(err) => bail!("open file {:?} failed - {}", self.current_path, err), |
1c4804cf DM |
348 | }; |
349 | ||
13d98013 DM |
350 | let child_magic = if dir_stat.st_dev != stat.st_dev { |
351 | detect_fs_type(filefd)? | |
352 | } else { | |
353 | magic | |
354 | }; | |
355 | ||
1c4804cf | 356 | self.write_filename(&filename)?; |
13d98013 | 357 | let res = self.encode_file(filefd, &stat, child_magic); |
1c4804cf DM |
358 | let _ = nix::unistd::close(filefd); // ignore close errors |
359 | res?; | |
360 | ||
a7e37131 | 361 | } else if ifmt == libc::S_IFLNK { |
0ff55999 | 362 | let mut buffer = [0u8; libc::PATH_MAX as usize]; |
3192ae96 | 363 | |
3192ae96 | 364 | let res = filename.with_nix_path(|cstr| { |
d05f9321 | 365 | unsafe { libc::readlinkat(rawfd, cstr.as_ptr(), buffer.as_mut_ptr() as *mut libc::c_char, buffer.len()-1) } |
3192ae96 DM |
366 | })?; |
367 | ||
368 | match Errno::result(res) { | |
d05f9321 DM |
369 | Ok(len) => { |
370 | buffer[len as usize] = 0u8; // add Nul byte | |
1c4804cf | 371 | self.write_filename(&filename)?; |
d05f9321 DM |
372 | self.encode_symlink(&buffer[..((len+1) as usize)], &stat)? |
373 | } | |
1c4804cf DM |
374 | Err(nix::Error::Sys(Errno::ENOENT)) => { |
375 | self.report_vanished_file(&self.current_path)?; | |
376 | continue; | |
377 | } | |
d2b03f23 DM |
378 | Err(err) => bail!("readlink {:?} failed - {}", self.current_path, err), |
379 | } | |
a7e37131 | 380 | } else if (ifmt == libc::S_IFBLK) || (ifmt == libc::S_IFCHR) { |
1c4804cf | 381 | self.write_filename(&filename)?; |
a7e37131 | 382 | self.encode_device(&stat)?; |
490683ec | 383 | } else if (ifmt == libc::S_IFIFO) || (ifmt == libc::S_IFSOCK) { |
1c4804cf DM |
384 | self.write_filename(&filename)?; |
385 | self.encode_special(&stat)?; | |
45281d49 DM |
386 | } else { |
387 | bail!("unsupported file type (mode {:o} {:?})", stat.st_mode, self.current_path); | |
fb8365b7 | 388 | } |
45281d49 | 389 | |
a0cc09b5 DM |
390 | let end_pos = self.writer_pos; |
391 | ||
985567fb | 392 | goodbye_items.push(CaFormatGoodbyeItem { |
a0cc09b5 DM |
393 | offset: start_pos as u64, |
394 | size: (end_pos - start_pos) as u64, | |
0866748d | 395 | hash: compute_goodbye_hash(filename.to_bytes()), |
a0cc09b5 DM |
396 | }); |
397 | ||
45281d49 | 398 | self.current_path.pop(); |
248c17af DM |
399 | } |
400 | ||
0433db19 | 401 | //println!("encode_dir: {:?} end {}", self.current_path, self.writer_pos); |
45281d49 | 402 | |
985567fb DM |
403 | // fixup goodby item offsets |
404 | let goodbye_start = self.writer_pos as u64; | |
405 | for item in &mut goodbye_items { | |
406 | item.offset = goodbye_start - item.offset; | |
a0cc09b5 DM |
407 | } |
408 | ||
985567fb | 409 | let goodbye_offset = self.writer_pos - dir_start_pos; |
a0cc09b5 | 410 | |
1b0dc9f6 | 411 | self.write_goodbye_table(goodbye_offset, &mut goodbye_items)?; |
a0cc09b5 | 412 | |
0433db19 | 413 | //println!("encode_dir: {:?} end1 {}", self.current_path, self.writer_pos); |
45281d49 DM |
414 | Ok(()) |
415 | } | |
416 | ||
13d98013 | 417 | fn encode_file(&mut self, filefd: RawFd, stat: &FileStat, magic: i64) -> Result<(), Error> { |
45281d49 | 418 | |
0433db19 | 419 | //println!("encode_file: {:?}", self.current_path); |
45281d49 | 420 | |
4f6892eb | 421 | let mut entry = self.create_entry(&stat)?; |
2e4ae0e2 | 422 | |
4f6892eb | 423 | self.read_chattr(filefd, &mut entry)?; |
d7bfac86 | 424 | self.read_fat_attr(filefd, magic, &mut entry)?; |
2e4ae0e2 | 425 | |
8c1dfa6c | 426 | self.write_entry(entry)?; |
2e4ae0e2 | 427 | |
79c281fd | 428 | let include_payload; |
13d98013 | 429 | if is_virtual_file_system(magic) { |
4c0fd487 DM |
430 | include_payload = false; |
431 | } else { | |
79c281fd | 432 | include_payload = (stat.st_dev == self.root_st_dev) || self.all_file_systems; |
4c0fd487 DM |
433 | } |
434 | ||
435 | if !include_payload { | |
65092b1e | 436 | eprintln!("skip content: {:?}", self.current_path); |
13d98013 DM |
437 | self.write_header(CA_FORMAT_PAYLOAD, 0)?; |
438 | return Ok(()); | |
439 | } | |
440 | ||
2e4ae0e2 DM |
441 | let size = stat.st_size as u64; |
442 | ||
443 | self.write_header(CA_FORMAT_PAYLOAD, size)?; | |
444 | ||
445 | let mut pos: u64 = 0; | |
446 | loop { | |
447 | let n = match nix::unistd::read(filefd, &mut self.file_copy_buffer) { | |
448 | Ok(n) => n, | |
449 | Err(nix::Error::Sys(Errno::EINTR)) => continue /* try again */, | |
450 | Err(err) => bail!("read {:?} failed - {}", self.current_path, err), | |
451 | }; | |
452 | if n == 0 { // EOF | |
453 | if pos != size { | |
454 | // Note:: casync format cannot handle that | |
455 | bail!("detected shrinked file {:?} ({} < {})", self.current_path, pos, size); | |
456 | } | |
457 | break; | |
458 | } | |
459 | ||
460 | let mut next = pos + (n as u64); | |
461 | ||
462 | if next > size { next = size; } | |
463 | ||
464 | let count = (next - pos) as usize; | |
465 | ||
248c17af | 466 | self.flush_copy_buffer(count)?; |
2e4ae0e2 | 467 | |
f0f3029e | 468 | pos = next; |
2e4ae0e2 DM |
469 | |
470 | if pos >= size { break; } | |
471 | } | |
3192ae96 | 472 | |
45281d49 DM |
473 | Ok(()) |
474 | } | |
475 | ||
a7e37131 DM |
476 | fn encode_device(&mut self, stat: &FileStat) -> Result<(), Error> { |
477 | ||
1c4804cf | 478 | let entry = self.create_entry(&stat)?; |
a7e37131 DM |
479 | |
480 | self.write_entry(entry)?; | |
481 | ||
482 | let major = unsafe { libc::major(stat.st_rdev) } as u64; | |
483 | let minor = unsafe { libc::minor(stat.st_rdev) } as u64; | |
484 | ||
6b64d25d | 485 | //println!("encode_device: {:?} {} {} {}", self.current_path, stat.st_rdev, major, minor); |
a7e37131 DM |
486 | |
487 | self.write_header(CA_FORMAT_DEVICE, std::mem::size_of::<CaFormatDevice>() as u64)?; | |
488 | self.write_item(CaFormatDevice { major, minor })?; | |
489 | ||
490 | Ok(()) | |
491 | } | |
492 | ||
1c4804cf DM |
493 | // FIFO or Socket |
494 | fn encode_special(&mut self, stat: &FileStat) -> Result<(), Error> { | |
495 | ||
496 | let entry = self.create_entry(&stat)?; | |
497 | ||
498 | self.write_entry(entry)?; | |
499 | ||
500 | Ok(()) | |
501 | } | |
502 | ||
a0cc09b5 | 503 | fn encode_symlink(&mut self, target: &[u8], stat: &FileStat) -> Result<(), Error> { |
45281d49 | 504 | |
0433db19 | 505 | //println!("encode_symlink: {:?} -> {:?}", self.current_path, target); |
fb8365b7 | 506 | |
4f6892eb | 507 | let entry = self.create_entry(&stat)?; |
8c1dfa6c | 508 | self.write_entry(entry)?; |
3192ae96 | 509 | |
2e4ae0e2 | 510 | self.write_header(CA_FORMAT_SYMLINK, target.len() as u64)?; |
248c17af | 511 | self.write(target)?; |
3192ae96 | 512 | |
fb8365b7 DM |
513 | Ok(()) |
514 | } | |
d2b03f23 DM |
515 | |
516 | // the report_XXX method may raise and error - depending on encoder configuration | |
3192ae96 | 517 | |
d2b03f23 DM |
518 | fn report_vanished_file(&self, path: &Path) -> Result<(), Error> { |
519 | ||
520 | eprintln!("WARNING: detected vanished file {:?}", path); | |
521 | ||
522 | Ok(()) | |
523 | } | |
a0cc09b5 | 524 | } |
4f6892eb DM |
525 | |
526 | fn errno_is_unsupported(errno: Errno) -> bool { | |
527 | ||
528 | match errno { | |
529 | Errno::ENOTTY | Errno::ENOSYS | Errno::EBADF | Errno::EOPNOTSUPP | Errno::EINVAL => { | |
530 | true | |
531 | } | |
532 | _ => false, | |
533 | } | |
534 | } | |
535 | ||
13d98013 | 536 | fn detect_fs_type(fd: RawFd) -> Result<i64, Error> { |
1c4804cf | 537 | let mut fs_stat: libc::statfs = unsafe { std::mem::uninitialized() }; |
13d98013 DM |
538 | let res = unsafe { libc::fstatfs(fd, &mut fs_stat) }; |
539 | Errno::result(res)?; | |
1c4804cf DM |
540 | |
541 | Ok(fs_stat.f_type) | |
542 | } | |
543 | ||
4f6892eb | 544 | use nix::{convert_ioctl_res, request_code_read, ioc}; |
8c1dfa6c | 545 | |
4f6892eb DM |
546 | // /usr/include/linux/fs.h: #define FS_IOC_GETFLAGS _IOR('f', 1, long) |
547 | /// read Linux file system attributes (see man chattr) | |
548 | nix::ioctl_read!(read_attr_fd, b'f', 1, usize); | |
549 | ||
5c76c2f3 DM |
550 | // /usr/include/linux/msdos_fs.h: #define FAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, __u32) |
551 | // read FAT file system attributes | |
552 | nix::ioctl_read!(read_fat_attr_fd, b'r', 0x10, u32); | |
80881f60 DM |
553 | |
554 | ||
555 | // from /usr/include/linux/magic.h | |
556 | // and from casync util.h | |
9f49fe1d DM |
557 | pub const BINFMTFS_MAGIC: i64 = 0x42494e4d; |
558 | pub const CGROUP2_SUPER_MAGIC: i64 = 0x63677270; | |
559 | pub const CGROUP_SUPER_MAGIC: i64 = 0x0027e0eb; | |
560 | pub const CONFIGFS_MAGIC: i64 = 0x62656570; | |
561 | pub const DEBUGFS_MAGIC: i64 = 0x64626720; | |
562 | pub const DEVPTS_SUPER_MAGIC: i64 = 0x00001cd1; | |
563 | pub const EFIVARFS_MAGIC: i64 = 0xde5e81e4; | |
564 | pub const FUSE_CTL_SUPER_MAGIC: i64 = 0x65735543; | |
565 | pub const HUGETLBFS_MAGIC: i64 = 0x958458f6; | |
566 | pub const MQUEUE_MAGIC: i64 = 0x19800202; | |
567 | pub const NFSD_MAGIC: i64 = 0x6e667364; | |
568 | pub const PROC_SUPER_MAGIC: i64 = 0x00009fa0; | |
569 | pub const PSTOREFS_MAGIC: i64 = 0x6165676C; | |
570 | pub const RPCAUTH_GSSMAGIC: i64 = 0x67596969; | |
571 | pub const SECURITYFS_MAGIC: i64 = 0x73636673; | |
572 | pub const SELINUX_MAGIC: i64 = 0xf97cff8c; | |
573 | pub const SMACK_MAGIC: i64 = 0x43415d53; | |
574 | pub const RAMFS_MAGIC: i64 = 0x858458f6; | |
575 | pub const TMPFS_MAGIC: i64 = 0x01021994; | |
576 | pub const SYSFS_MAGIC: i64 = 0x62656572; | |
577 | pub const MSDOS_SUPER_MAGIC: i64 = 0x00004d44; | |
578 | pub const FUSE_SUPER_MAGIC: i64 = 0x65735546; | |
d7bfac86 | 579 | |
80881f60 DM |
580 | |
581 | #[inline(always)] | |
9f49fe1d | 582 | pub fn is_temporary_file_system(magic: i64) -> bool { |
80881f60 DM |
583 | magic == RAMFS_MAGIC || magic == TMPFS_MAGIC |
584 | } | |
585 | ||
9f49fe1d | 586 | pub fn is_virtual_file_system(magic: i64) -> bool { |
80881f60 DM |
587 | |
588 | match magic { | |
589 | BINFMTFS_MAGIC | | |
590 | CGROUP2_SUPER_MAGIC | | |
591 | CGROUP_SUPER_MAGIC | | |
592 | CONFIGFS_MAGIC | | |
593 | DEBUGFS_MAGIC | | |
594 | DEVPTS_SUPER_MAGIC | | |
595 | EFIVARFS_MAGIC | | |
596 | FUSE_CTL_SUPER_MAGIC | | |
597 | HUGETLBFS_MAGIC | | |
598 | MQUEUE_MAGIC | | |
599 | NFSD_MAGIC | | |
600 | PROC_SUPER_MAGIC | | |
601 | PSTOREFS_MAGIC | | |
602 | RPCAUTH_GSSMAGIC | | |
603 | SECURITYFS_MAGIC | | |
604 | SELINUX_MAGIC | | |
605 | SMACK_MAGIC | | |
606 | SYSFS_MAGIC => true, | |
607 | _ => false | |
608 | } | |
609 | } |