1use super::*;
4
5use std::fs::File;
6use std::io::Error;
7use std::os::unix::io::AsRawFd;
8
9use errno::errno;
10use libc::{c_int, lseek, off_t, EINVAL, ENXIO, SEEK_END};
11
12cfg_if::cfg_if! {
13 if #[cfg(target_os = "macos")]{
15 const SEEK_HOLE: c_int = 3;
16 const SEEK_DATA: c_int = 4;
17 } else {
18 use libc::{SEEK_DATA, SEEK_HOLE};
19 }
20}
21
22impl SparseFile for File {
23 fn scan_chunks(&mut self) -> Result<Vec<Segment>, ScanError> {
24 let mut tags: Vec<Segment> = Vec::new();
26 let fd = self.as_raw_fd();
28 let end = safe_lseek(fd, 0, SEEK_END)?.unwrap_or(0);
30
31 if end == 0 {
32 return Ok(vec![]);
33 }
34
35 let mut last_seek = safe_lseek(fd, 0, SEEK_HOLE)?.unwrap_or(end);
39 let mut last_type = SegmentType::Hole;
40 if last_seek > 0 {
41 tags.push(Segment {
42 segment_type: SegmentType::Data,
43 range: 0..last_seek,
44 })
45 }
46
47 while last_seek < end {
48 let seek_type = match last_type {
49 SegmentType::Hole => SEEK_DATA,
50 SegmentType::Data => SEEK_HOLE,
51 };
52
53 let next_seek = safe_lseek(fd, last_seek, seek_type)?.unwrap_or(end);
54 tags.push(Segment {
55 segment_type: last_type,
56 range: last_seek..next_seek,
57 });
58 last_seek = next_seek;
59 last_type = last_type.opposite();
60 }
61 Ok(tags)
62 }
63
64 #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd",))]
65 fn drill_hole(&self, start: u64, end: u64) -> Result<(), ScanError> {
66 unsafe {
67 use libc::{fallocate, FALLOC_FL_KEEP_SIZE, FALLOC_FL_PUNCH_HOLE};
68 use std::io::Error;
69 use std::os::unix::io::AsRawFd;
70
71 if fallocate(
72 self.as_raw_fd(),
73 FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
74 start as libc::off_t,
75 (end - start) as libc::off_t,
76 ) < 0
77 {
78 return Err(Error::last_os_error().into());
79 }
80 }
81 Ok(())
82 }
83
84 #[cfg(target_os = "macos")]
85 fn drill_hole(&self, start: u64, end: u64) -> Result<(), ScanError> {
86 use libc::fcntl;
87 use std::os::unix::io::AsRawFd;
88
89 #[repr(C)]
90 struct fpunchhole_t {
91 fp_flags: c_int, reserved: c_int, fp_offset: u64, fp_length: u64, }
96
97 const F_PUNCHHOLE: c_int = 99;
99
100 let hole = fpunchhole_t {
101 fp_flags: 0,
102 reserved: 0,
103 fp_offset: start,
104 fp_length: (end - start),
105 };
106
107 unsafe {
109 let ret = fcntl(self.as_raw_fd(), F_PUNCHHOLE, &hole);
110 if ret < 0 {
111 return Err(Error::last_os_error().into());
112 }
113 }
114 Ok(())
115 }
116}
117
118fn safe_lseek(fd: c_int, offset: u64, seek_type: c_int) -> Result<Option<u64>, ScanError> {
119 unsafe {
120 let new_offset = lseek(fd, offset as off_t, seek_type);
121 if new_offset < 0 {
123 let errno = errno().into();
125 match errno {
126 EINVAL => Err(ScanError::UnsupportedFileSystem),
129 ENXIO => Ok(None),
134 _ => Err(Error::last_os_error().into()),
137 }
138 } else {
139 Ok(Some(new_offset as u64))
140 }
141 }
142}