1use log::{debug, warn};
19use rustix::fs::{fsync, ftruncate};
20use rustix::io::{pread, pwrite};
21use std::cmp;
22use std::fs::{File, FileTimes};
23use std::io::{ErrorKind, Read, Write};
24use std::os::unix::fs::{fchown, MetadataExt};
25use std::path::Path;
26use xattr::FileExt;
27
28use crate::errors::{Result, Error};
29use crate::{Extent, XATTR_SUPPORTED, copy_sparse, probably_sparse, copy_file_bytes};
30
31fn copy_xattr(infd: &File, outfd: &File) -> Result<()> {
32 if XATTR_SUPPORTED {
34 debug!("Starting xattr copy...");
35 for attr in infd.list_xattr()? {
36 if let Some(val) = infd.get_xattr(&attr)? {
37 debug!("Copy xattr {attr:?}");
38 outfd.set_xattr(attr, val.as_slice())?;
39 }
40 }
41 }
42 Ok(())
43}
44
45pub fn copy_permissions(infd: &File, outfd: &File) -> Result<()> {
49 let xr = copy_xattr(infd, outfd);
50 if let Err(e) = xr {
51 warn!("Failed to copy xattrs from {infd:?}: {e}");
55 }
56
57 let inmeta = infd.metadata()?;
60
61 debug!("Performing permissions copy");
62 outfd.set_permissions(inmeta.permissions())?;
63
64 Ok(())
65}
66
67pub fn copy_timestamps(infd: &File, outfd: &File) -> Result<()> {
69 let inmeta = infd.metadata()?;
70
71 debug!("Performing timestamp copy");
72 let ftime = FileTimes::new()
73 .set_accessed(inmeta.accessed()?)
74 .set_modified(inmeta.modified()?);
75 outfd.set_times(ftime)?;
76
77 Ok(())
78}
79
80pub fn copy_owner(infd: &File, outfd: &File) -> Result<()> {
81 let inmeta = infd.metadata()?;
82 fchown(outfd, Some(inmeta.uid()), Some(inmeta.gid()))?;
83
84 Ok(())
85}
86
87pub(crate) fn read_bytes(fd: &File, buf: &mut [u8], off: usize) -> Result<usize> {
88 Ok(pread(fd, buf, off as u64)?)
89}
90
91pub(crate) fn write_bytes(fd: &File, buf: &mut [u8], off: usize) -> Result<usize> {
92 Ok(pwrite(fd, buf, off as u64)?)
93}
94
95pub(crate) fn copy_range_uspace(reader: &File, writer: &File, nbytes: usize, off: usize) -> Result<usize> {
97 let mut buf = vec![0; nbytes];
99
100 let mut written: usize = 0;
101 while written < nbytes {
102 let next = cmp::min(nbytes - written, nbytes);
103 let noff = off + written;
104
105 let rlen = match read_bytes(reader, &mut buf[..next], noff) {
106 Ok(0) => return Err(Error::InvalidSource("Source file ended prematurely.")),
107 Ok(len) => len,
108 Err(e) => return Err(e),
109 };
110
111 let _wlen = match write_bytes(writer, &mut buf[..rlen], noff) {
112 Ok(len) if len < rlen => {
113 return Err(Error::InvalidSource("Failed write to file."))
114 }
115 Ok(len) => len,
116 Err(e) => return Err(e),
117 };
118
119 written += rlen;
120 }
121 Ok(written)
122}
123
124pub(crate) fn copy_bytes_uspace(mut reader: &File, mut writer: &File, nbytes: usize) -> Result<usize> {
126 let mut buf = vec![0; nbytes];
127
128 let mut written = 0;
129 while written < nbytes {
130 let next = cmp::min(nbytes - written, nbytes);
131 let len = match reader.read(&mut buf[..next]) {
132 Ok(0) => return Err(Error::InvalidSource("Source file ended prematurely.")),
133 Ok(len) => len,
134 Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
135 Err(e) => return Err(e.into())
136 };
137 writer.write_all(&buf[..len])?;
138 written += len;
139 }
140 Ok(written)
141}
142
143pub fn allocate_file(fd: &File, len: u64) -> Result<()> {
145 Ok(ftruncate(fd, len)?)
146}
147
148pub fn merge_extents(extents: Vec<Extent>) -> Result<Vec<Extent>> {
150 let mut merged: Vec<Extent> = vec![];
151
152 let mut prev: Option<Extent> = None;
153 for e in extents {
154 match prev {
155 Some(p) => {
156 if e.start == p.end + 1 {
157 prev = Some(Extent {
160 start: p.start,
161 end: e.end,
162 shared: p.shared & e.shared,
163 });
164 } else {
165 merged.push(p);
166 prev = Some(e);
167 }
168 }
169 None => prev = Some(e),
171 }
172 }
173 if let Some(p) = prev {
174 merged.push(p);
175 }
176
177 Ok(merged)
178}
179
180
181pub fn is_same_file(src: &Path, dest: &Path) -> Result<bool> {
183 let sstat = src.metadata()?;
184 let dstat = dest.metadata()?;
185 let same = (sstat.ino() == dstat.ino())
186 && (sstat.dev() == dstat.dev());
187
188 Ok(same)
189}
190
191pub fn copy_file(from: &Path, to: &Path) -> Result<u64> {
194 let infd = File::open(from)?;
195 let len = infd.metadata()?.len();
196
197 let outfd = File::create(to)?;
198 allocate_file(&outfd, len)?;
199
200 let total = if probably_sparse(&infd)? {
201 copy_sparse(&infd, &outfd)?
202 } else {
203 copy_file_bytes(&infd, &outfd, len)? as u64
204 };
205
206 Ok(total)
207}
208
209pub fn sync(fd: &File) -> Result<()> {
211 Ok(fsync(fd)?)
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217 use std::fs::read;
218 use std::ops::Range;
219 use tempfile::tempdir;
220
221 impl From<Range<u64>> for Extent {
222 fn from(r: Range<u64>) -> Self {
223 Extent {
224 start: r.start,
225 end: r.end,
226 shared: false,
227 }
228 }
229 }
230
231 #[test]
232 fn test_copy_bytes_uspace_large() {
233 let dir = tempdir().unwrap();
234 let from = dir.path().join("from.bin");
235 let to = dir.path().join("to.bin");
236 let size = 128 * 1024;
237 let data = "X".repeat(size);
238
239 {
240 let mut fd: File = File::create(&from).unwrap();
241 write!(fd, "{data}").unwrap();
242 }
243
244 {
245 let infd = File::open(&from).unwrap();
246 let outfd = File::create(&to).unwrap();
247 let written = copy_bytes_uspace(&infd, &outfd, size).unwrap();
248
249 assert_eq!(written, size);
250 }
251
252 assert_eq!(from.metadata().unwrap().len(), to.metadata().unwrap().len());
253
254 {
255 let from_data = read(&from).unwrap();
256 let to_data = read(&to).unwrap();
257 assert_eq!(from_data, to_data);
258 }
259 }
260
261 #[test]
262 fn test_copy_range_uspace_large() {
263 let dir = tempdir().unwrap();
264 let from = dir.path().join("from.bin");
265 let to = dir.path().join("to.bin");
266 let size = 128 * 1024;
267 let data = "X".repeat(size);
268
269 {
270 let mut fd: File = File::create(&from).unwrap();
271 write!(fd, "{data}").unwrap();
272 }
273
274 {
275 let infd = File::open(&from).unwrap();
276 let outfd = File::create(&to).unwrap();
277
278 let blocksize = size / 4;
279 let mut written = 0;
280
281 for off in (0..4).rev() {
282 written += copy_range_uspace(&infd, &outfd, blocksize, blocksize * off).unwrap();
283 }
284
285 assert_eq!(written, size);
286 }
287
288 assert_eq!(from.metadata().unwrap().len(), to.metadata().unwrap().len());
289
290 {
291 let from_data = read(&from).unwrap();
292 let to_data = read(&to).unwrap();
293 assert_eq!(from_data, to_data);
294 }
295 }
296
297 #[test]
298 fn test_extent_merge() -> Result<()> {
299 assert_eq!(merge_extents(vec!())?, vec!());
300 assert_eq!(merge_extents(
301 vec!((0..1).into()))?,
302 vec!((0..1).into()));
303
304 assert_eq!(merge_extents(
305 vec!((0..1).into(),
306 (10..20).into()))?,
307 vec!((0..1).into(),
308 (10..20).into()));
309 assert_eq!(merge_extents(
310 vec!((0..10).into(),
311 (11..20).into()))?,
312 vec!((0..20).into()));
313 assert_eq!(
314 merge_extents(
315 vec!((0..5).into(),
316 (11..20).into(),
317 (21..30).into(),
318 (40..50).into()))?,
319 vec!((0..5).into(),
320 (11..30).into(),
321 (40..50).into())
322 );
323 assert_eq!(
324 merge_extents(vec!((0..5).into(),
325 (11..20).into(),
326 (21..30).into(),
327 (40..50).into(),
328 (51..60).into()))?,
329 vec!((0..5).into(),
330 (11..30).into(),
331 (40..60).into())
332 );
333 assert_eq!(
334 merge_extents(
335 vec!((0..10).into(),
336 (11..20).into(),
337 (21..30).into(),
338 (31..50).into(),
339 (51..60).into()))?,
340 vec!((0..60).into())
341 );
342 Ok(())
343 }
344
345
346 #[test]
347 fn test_copy_file() -> Result<()> {
348 let dir = tempdir()?;
349 let from = dir.path().join("file.bin");
350 let len = 32 * 1024 * 1024;
351
352 {
353 let mut fd = File::create(&from)?;
354 let data = "X".repeat(len);
355 write!(fd, "{data}").unwrap();
356 }
357
358 assert_eq!(len, from.metadata()?.len() as usize);
359
360 let to = dir.path().join("sparse.copy.bin");
361 crate::copy_file(&from, &to)?;
362
363 assert_eq!(len, to.metadata()?.len() as usize);
364
365 Ok(())
366 }
367}