]>
Commit | Line | Data |
---|---|---|
416331ca XL |
1 | // copies from linuxx |
2 | use crate::ffi::{CString, CStr, OsString, OsStr}; | |
3 | use crate::sys::vxworks::ext::ffi::OsStrExt; | |
4 | use crate::fmt; | |
5 | use crate::io::{self, Error, ErrorKind, SeekFrom, IoSlice, IoSliceMut}; | |
6 | use crate::mem; | |
7 | use crate::path::{Path, PathBuf}; | |
8 | use crate::ptr; | |
9 | use crate::sync::Arc; | |
10 | use crate::sys::fd::FileDesc; | |
11 | use crate::sys::time::SystemTime; | |
12 | use crate::sys::{cvt, cvt_r}; | |
13 | use crate::sys_common::{AsInner, FromInner}; | |
14 | use libc::{self, c_int, mode_t, stat64, off_t}; | |
15 | use libc::{ftruncate, lseek, dirent, readdir_r as readdir64_r, open}; | |
16 | use crate::sys::vxworks::ext::ffi::OsStringExt; | |
17 | pub struct File(FileDesc); | |
18 | ||
19 | #[derive(Clone)] | |
20 | pub struct FileAttr { | |
21 | stat: stat64, | |
22 | } | |
23 | ||
24 | // all DirEntry's will have a reference to this struct | |
25 | struct InnerReadDir { | |
26 | dirp: Dir, | |
27 | root: PathBuf, | |
28 | } | |
29 | ||
30 | #[derive(Clone)] | |
31 | pub struct ReadDir { | |
32 | inner: Arc<InnerReadDir>, | |
33 | end_of_stream: bool, | |
34 | } | |
35 | ||
36 | struct Dir(*mut libc::DIR); | |
37 | ||
38 | unsafe impl Send for Dir {} | |
39 | unsafe impl Sync for Dir {} | |
40 | ||
41 | pub struct DirEntry { | |
42 | entry: dirent, | |
43 | dir: ReadDir, | |
44 | } | |
45 | ||
46 | #[derive(Clone, Debug)] | |
47 | pub struct OpenOptions { | |
48 | // generic | |
49 | read: bool, | |
50 | write: bool, | |
51 | append: bool, | |
52 | truncate: bool, | |
53 | create: bool, | |
54 | create_new: bool, | |
55 | // system-specific | |
56 | custom_flags: i32, | |
57 | mode: mode_t, | |
58 | } | |
59 | ||
60 | #[derive(Clone, PartialEq, Eq, Debug)] | |
61 | pub struct FilePermissions { mode: mode_t } | |
62 | ||
63 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] | |
64 | pub struct FileType { mode: mode_t } | |
65 | ||
66 | #[derive(Debug)] | |
67 | pub struct DirBuilder { mode: mode_t } | |
68 | ||
69 | impl FileAttr { | |
70 | pub fn size(&self) -> u64 { self.stat.st_size as u64 } | |
71 | pub fn perm(&self) -> FilePermissions { | |
72 | FilePermissions { mode: (self.stat.st_mode as mode_t) } | |
73 | } | |
74 | ||
75 | pub fn file_type(&self) -> FileType { | |
76 | FileType { mode: self.stat.st_mode as mode_t } | |
77 | } | |
78 | ||
79 | pub fn modified(&self) -> io::Result<SystemTime> { | |
80 | Ok(SystemTime::from(libc::timespec { | |
81 | tv_sec: self.stat.st_mtime as libc::time_t, | |
82 | tv_nsec: 0, // hack 2.0; | |
83 | })) | |
84 | } | |
85 | ||
86 | pub fn accessed(&self) -> io::Result<SystemTime> { | |
87 | Ok(SystemTime::from(libc::timespec { | |
88 | tv_sec: self.stat.st_atime as libc::time_t, | |
89 | tv_nsec: 0, // hack - a proper fix would be better | |
90 | })) | |
91 | } | |
92 | ||
93 | pub fn created(&self) -> io::Result<SystemTime> { | |
94 | Err(io::Error::new(io::ErrorKind::Other, | |
95 | "creation time is not available on this platform currently")) | |
96 | } | |
97 | ||
98 | } | |
99 | ||
100 | impl AsInner<stat64> for FileAttr { | |
101 | fn as_inner(&self) -> &stat64 { &self.stat } | |
102 | } | |
103 | ||
104 | impl FilePermissions { | |
105 | pub fn readonly(&self) -> bool { | |
106 | // check if any class (owner, group, others) has write permission | |
107 | self.mode & 0o222 == 0 | |
108 | } | |
109 | ||
110 | pub fn set_readonly(&mut self, readonly: bool) { | |
111 | if readonly { | |
112 | // remove write permission for all classes; equivalent to `chmod a-w <file>` | |
113 | self.mode &= !0o222; | |
114 | } else { | |
115 | // add write permission for all classes; equivalent to `chmod a+w <file>` | |
116 | self.mode |= 0o222; | |
117 | } | |
118 | } | |
119 | pub fn mode(&self) -> u32 { self.mode as u32 } | |
120 | } | |
121 | ||
122 | impl FileType { | |
123 | pub fn is_dir(&self) -> bool { self.is(libc::S_IFDIR) } | |
124 | pub fn is_file(&self) -> bool { self.is(libc::S_IFREG) } | |
125 | pub fn is_symlink(&self) -> bool { self.is(libc::S_IFLNK) } | |
126 | ||
127 | pub fn is(&self, mode: mode_t) -> bool { self.mode & libc::S_IFMT == mode } | |
128 | } | |
129 | ||
130 | impl FromInner<u32> for FilePermissions { | |
131 | fn from_inner(mode: u32) -> FilePermissions { | |
132 | FilePermissions { mode: mode as mode_t } | |
133 | } | |
134 | } | |
135 | ||
136 | impl fmt::Debug for ReadDir { | |
137 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
138 | // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. | |
139 | // Thus the result will be e g 'ReadDir("/home")' | |
140 | fmt::Debug::fmt(&*self.inner.root, f) | |
141 | } | |
142 | } | |
143 | ||
144 | impl Iterator for ReadDir { | |
145 | type Item = io::Result<DirEntry>; | |
146 | fn next(&mut self) -> Option<io::Result<DirEntry>> { | |
147 | if self.end_of_stream { | |
148 | return None; | |
149 | } | |
150 | ||
151 | unsafe { | |
152 | let mut ret = DirEntry { | |
153 | entry: mem::zeroed(), | |
154 | dir: self.clone(), | |
155 | }; | |
156 | let mut entry_ptr = ptr::null_mut(); | |
157 | loop { | |
158 | if readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr) != 0 { | |
159 | if entry_ptr.is_null() { | |
160 | // We encountered an error (which will be returned in this iteration), but | |
161 | // we also reached the end of the directory stream. The `end_of_stream` | |
162 | // flag is enabled to make sure that we return `None` in the next iteration | |
163 | // (instead of looping forever) | |
164 | self.end_of_stream = true; | |
165 | } | |
166 | return Some(Err(Error::last_os_error())) | |
167 | } | |
168 | if entry_ptr.is_null() { | |
169 | return None | |
170 | } | |
171 | if ret.name_bytes() != b"." && ret.name_bytes() != b".." { | |
172 | return Some(Ok(ret)) | |
173 | } | |
174 | } | |
175 | } | |
176 | } | |
177 | } | |
178 | ||
179 | impl Drop for Dir { | |
180 | fn drop(&mut self) { | |
181 | let r = unsafe { libc::closedir(self.0) }; | |
182 | debug_assert_eq!(r, 0); | |
183 | } | |
184 | } | |
185 | ||
186 | impl DirEntry { | |
187 | pub fn path(&self) -> PathBuf { | |
188 | use crate::sys::vxworks::ext::ffi::OsStrExt; | |
189 | self.dir.inner.root.join(OsStr::from_bytes(self.name_bytes())) | |
190 | } | |
191 | ||
192 | pub fn file_name(&self) -> OsString { | |
193 | OsStr::from_bytes(self.name_bytes()).to_os_string() | |
194 | } | |
195 | ||
196 | ||
197 | pub fn metadata(&self) -> io::Result<FileAttr> { | |
198 | lstat(&self.path()) | |
199 | } | |
200 | ||
201 | pub fn file_type(&self) -> io::Result<FileType> { | |
202 | lstat(&self.path()).map(|m| m.file_type()) | |
203 | ||
204 | } | |
205 | ||
206 | pub fn ino(&self) -> u64 { | |
207 | self.entry.d_ino as u64 | |
208 | } | |
209 | ||
210 | fn name_bytes(&self) -> &[u8] { | |
211 | unsafe { | |
212 | //&*self.name | |
213 | CStr::from_ptr(self.entry.d_name.as_ptr()).to_bytes() | |
214 | } | |
215 | } | |
216 | } | |
217 | ||
218 | impl OpenOptions { | |
219 | pub fn new() -> OpenOptions { | |
220 | OpenOptions { | |
221 | // generic | |
222 | read: false, | |
223 | write: false, | |
224 | append: false, | |
225 | truncate: false, | |
226 | create: false, | |
227 | create_new: false, | |
228 | // system-specific | |
229 | custom_flags: 0, | |
230 | mode: 0o666, | |
231 | } | |
232 | } | |
233 | ||
234 | pub fn read(&mut self, read: bool) { self.read = read; } | |
235 | pub fn write(&mut self, write: bool) { self.write = write; } | |
236 | pub fn append(&mut self, append: bool) { self.append = append; } | |
237 | pub fn truncate(&mut self, truncate: bool) { self.truncate = truncate; } | |
238 | pub fn create(&mut self, create: bool) { self.create = create; } | |
239 | pub fn create_new(&mut self, create_new: bool) { self.create_new = create_new; } | |
240 | pub fn mode(&mut self, mode: u32) { self.mode = mode as mode_t; } | |
241 | ||
242 | fn get_access_mode(&self) -> io::Result<c_int> { | |
243 | match (self.read, self.write, self.append) { | |
244 | (true, false, false) => Ok(libc::O_RDONLY), | |
245 | (false, true, false) => Ok(libc::O_WRONLY), | |
246 | (true, true, false) => Ok(libc::O_RDWR), | |
247 | (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND), | |
248 | (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND), | |
249 | (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)), | |
250 | } | |
251 | } | |
252 | ||
253 | fn get_creation_mode(&self) -> io::Result<c_int> { | |
254 | match (self.write, self.append) { | |
255 | (true, false) => {} | |
256 | (false, false) => | |
257 | if self.truncate || self.create || self.create_new { | |
258 | return Err(Error::from_raw_os_error(libc::EINVAL)); | |
259 | }, | |
260 | (_, true) => | |
261 | if self.truncate && !self.create_new { | |
262 | return Err(Error::from_raw_os_error(libc::EINVAL)); | |
263 | }, | |
264 | } | |
265 | ||
266 | Ok(match (self.create, self.truncate, self.create_new) { | |
267 | (false, false, false) => 0, | |
268 | (true, false, false) => libc::O_CREAT, | |
269 | (false, true, false) => libc::O_TRUNC, | |
270 | (true, true, false) => libc::O_CREAT | libc::O_TRUNC, | |
271 | (_, _, true) => libc::O_CREAT | libc::O_EXCL, | |
272 | }) | |
273 | } | |
274 | } | |
275 | ||
276 | impl File { | |
277 | pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> { | |
278 | let path = cstr(path)?; | |
279 | File::open_c(&path, opts) | |
280 | } | |
281 | ||
282 | pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> { | |
283 | let flags = libc::O_CLOEXEC | | |
284 | opts.get_access_mode()? | | |
285 | opts.get_creation_mode()? | | |
286 | (opts.custom_flags as c_int & !libc::O_ACCMODE); | |
287 | let fd = cvt_r(|| unsafe { | |
288 | open(path.as_ptr(), flags, opts.mode as c_int) | |
289 | })?; | |
290 | Ok(File(FileDesc::new(fd))) | |
291 | } | |
292 | ||
293 | pub fn file_attr(&self) -> io::Result<FileAttr> { | |
294 | let mut stat: stat64 = unsafe { mem::zeroed() }; | |
295 | cvt(unsafe { | |
296 | ::libc::fstat(self.0.raw(), &mut stat) | |
297 | })?; | |
298 | Ok(FileAttr { stat: stat }) | |
299 | } | |
300 | ||
301 | pub fn fsync(&self) -> io::Result<()> { | |
302 | cvt_r(|| unsafe { libc::fsync(self.0.raw()) })?; | |
303 | Ok(()) | |
304 | } | |
305 | ||
306 | pub fn datasync(&self) -> io::Result<()> { | |
307 | cvt_r(|| unsafe { os_datasync(self.0.raw()) })?; | |
308 | return Ok(()); | |
309 | unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) } //not supported | |
310 | } | |
311 | ||
312 | pub fn truncate(&self, size: u64) -> io::Result<()> { | |
313 | return cvt_r(|| unsafe { | |
314 | ftruncate(self.0.raw(), size as off_t) | |
315 | }).map(|_| ()); | |
316 | } | |
317 | ||
318 | pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { | |
319 | self.0.read(buf) | |
320 | } | |
321 | ||
322 | pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { | |
323 | self.0.read_vectored(bufs) | |
324 | } | |
325 | ||
326 | pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> { | |
327 | self.0.read_at(buf, offset) | |
328 | } | |
329 | ||
330 | pub fn write(&self, buf: &[u8]) -> io::Result<usize> { | |
331 | self.0.write(buf) | |
332 | } | |
333 | ||
334 | pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { | |
335 | self.0.write_vectored(bufs) | |
336 | } | |
337 | ||
338 | pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { | |
339 | self.0.write_at(buf, offset) | |
340 | } | |
341 | ||
342 | pub fn flush(&self) -> io::Result<()> { Ok(()) } | |
343 | ||
344 | pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> { | |
345 | let (whence, pos) = match pos { | |
346 | // Casting to `i64` is fine, too large values will end up as | |
347 | // negative which will cause an error in `"lseek64"`. | |
348 | SeekFrom::Start(off) => (libc::SEEK_SET, off as i64), | |
349 | SeekFrom::End(off) => (libc::SEEK_END, off), | |
350 | SeekFrom::Current(off) => (libc::SEEK_CUR, off), | |
351 | }; | |
352 | let n = cvt(unsafe { lseek(self.0.raw(), pos, whence) })?; | |
353 | Ok(n as u64) | |
354 | } | |
355 | ||
356 | pub fn duplicate(&self) -> io::Result<File> { | |
357 | self.0.duplicate().map(File) | |
358 | } | |
359 | ||
360 | pub fn fd(&self) -> &FileDesc { &self.0 } | |
361 | ||
362 | pub fn into_fd(self) -> FileDesc { self.0 } | |
363 | ||
364 | pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { | |
365 | cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?; | |
366 | Ok(()) | |
367 | } | |
368 | ||
369 | pub fn diverge(&self) -> ! { | |
370 | panic!() | |
371 | } | |
372 | } | |
373 | ||
374 | impl DirBuilder { | |
375 | pub fn new() -> DirBuilder { | |
376 | DirBuilder { mode: 0o777 } | |
377 | } | |
378 | ||
379 | pub fn mkdir(&self, p: &Path) -> io::Result<()> { | |
380 | let p = cstr(p)?; | |
381 | cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) })?; | |
382 | Ok(()) | |
383 | } | |
384 | ||
385 | pub fn set_mode(&mut self, mode: u32) { | |
386 | self.mode = mode as mode_t; | |
387 | } | |
388 | } | |
389 | ||
390 | fn cstr(path: &Path) -> io::Result<CString> { | |
391 | use crate::sys::vxworks::ext::ffi::OsStrExt; | |
392 | Ok(CString::new(path.as_os_str().as_bytes())?) | |
393 | } | |
394 | ||
395 | impl FromInner<c_int> for File { | |
396 | fn from_inner(fd: c_int) -> File { | |
397 | File(FileDesc::new(fd)) | |
398 | } | |
399 | } | |
400 | ||
401 | impl fmt::Debug for File { | |
402 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
e74abb32 XL |
403 | fn get_path(fd: c_int) -> Option<PathBuf> { |
404 | let mut buf = vec![0;libc::PATH_MAX as usize]; | |
405 | let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) }; | |
406 | if n == -1 { | |
407 | return None; | |
408 | } | |
409 | let l = buf.iter().position(|&c| c == 0).unwrap(); | |
410 | buf.truncate(l as usize); | |
411 | Some(PathBuf::from(OsString::from_vec(buf))) | |
416331ca | 412 | } |
e74abb32 XL |
413 | fn get_mode(fd: c_int) -> Option<(bool, bool)> { |
414 | let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) }; | |
415 | if mode == -1 { | |
416 | return None; | |
417 | } | |
418 | match mode & libc::O_ACCMODE { | |
419 | libc::O_RDONLY => Some((true, false)), | |
420 | libc::O_RDWR => Some((true, true)), | |
421 | libc::O_WRONLY => Some((false, true)), | |
422 | _ => None | |
423 | } | |
416331ca XL |
424 | } |
425 | ||
426 | let fd = self.0.raw(); | |
427 | let mut b = f.debug_struct("File"); | |
428 | b.field("fd", &fd); | |
429 | if let Some(path) = get_path(fd) { | |
430 | b.field("path", &path); | |
431 | } | |
432 | if let Some((read, write)) = get_mode(fd) { | |
433 | b.field("read", &read).field("write", &write); | |
434 | } | |
435 | b.finish() | |
436 | } | |
437 | } | |
438 | ||
439 | pub fn readdir(p: &Path) -> io::Result<ReadDir> { | |
440 | let root = p.to_path_buf(); | |
441 | let p = cstr(p)?; | |
442 | unsafe { | |
443 | let ptr = libc::opendir(p.as_ptr()); | |
444 | if ptr.is_null() { | |
445 | Err(Error::last_os_error()) | |
446 | } else { | |
447 | let inner = InnerReadDir { dirp: Dir(ptr), root }; | |
448 | Ok(ReadDir{ | |
449 | inner: Arc::new(inner), | |
450 | end_of_stream: false, | |
451 | }) | |
452 | } | |
453 | } | |
454 | } | |
455 | ||
456 | pub fn unlink(p: &Path) -> io::Result<()> { | |
457 | let p = cstr(p)?; | |
458 | cvt(unsafe { libc::unlink(p.as_ptr()) })?; | |
459 | Ok(()) | |
460 | } | |
461 | ||
462 | pub fn rename(old: &Path, new: &Path) -> io::Result<()> { | |
463 | let old = cstr(old)?; | |
464 | let new = cstr(new)?; | |
465 | cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })?; | |
466 | Ok(()) | |
467 | } | |
468 | ||
469 | pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { | |
470 | let p = cstr(p)?; | |
471 | cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) })?; | |
472 | Ok(()) | |
473 | } | |
474 | ||
475 | pub fn rmdir(p: &Path) -> io::Result<()> { | |
476 | let p = cstr(p)?; | |
477 | cvt(unsafe { libc::rmdir(p.as_ptr()) })?; | |
478 | Ok(()) | |
479 | } | |
480 | ||
481 | pub fn remove_dir_all(path: &Path) -> io::Result<()> { | |
482 | let filetype = lstat(path)?.file_type(); | |
483 | if filetype.is_symlink() { | |
484 | unlink(path) | |
485 | } else { | |
486 | remove_dir_all_recursive(path) | |
487 | } | |
488 | } | |
489 | ||
490 | fn remove_dir_all_recursive(path: &Path) -> io::Result<()> { | |
491 | for child in readdir(path)? { | |
492 | let child = child?; | |
493 | if child.file_type()?.is_dir() { | |
494 | remove_dir_all_recursive(&child.path())?; | |
495 | } else { | |
496 | unlink(&child.path())?; | |
497 | } | |
498 | } | |
499 | rmdir(path) | |
500 | } | |
501 | ||
502 | pub fn readlink(p: &Path) -> io::Result<PathBuf> { | |
503 | let c_path = cstr(p)?; | |
504 | let p = c_path.as_ptr(); | |
505 | ||
506 | let mut buf = Vec::with_capacity(256); | |
507 | ||
508 | loop { | |
509 | let buf_read = cvt(unsafe { | |
510 | libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) | |
511 | })? as usize; | |
512 | ||
513 | unsafe { buf.set_len(buf_read); } | |
514 | ||
515 | if buf_read != buf.capacity() { | |
516 | buf.shrink_to_fit(); | |
517 | ||
518 | return Ok(PathBuf::from(OsString::from_vec(buf))); | |
519 | } | |
520 | ||
521 | // Trigger the internal buffer resizing logic of `Vec` by requiring | |
522 | // more space than the current capacity. The length is guaranteed to be | |
523 | // the same as the capacity due to the if statement above. | |
524 | buf.reserve(1); | |
525 | } | |
526 | } | |
527 | ||
528 | pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { | |
529 | let src = cstr(src)?; | |
530 | let dst = cstr(dst)?; | |
531 | cvt(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })?; | |
532 | Ok(()) | |
533 | } | |
534 | ||
535 | pub fn link(src: &Path, dst: &Path) -> io::Result<()> { | |
536 | let src = cstr(src)?; | |
537 | let dst = cstr(dst)?; | |
538 | cvt(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })?; | |
539 | Ok(()) | |
540 | } | |
541 | ||
542 | pub fn stat(p: &Path) -> io::Result<FileAttr> { | |
543 | let p = cstr(p)?; | |
544 | let mut stat: stat64 = unsafe { mem::zeroed() }; | |
545 | cvt(unsafe { | |
e1599b0c | 546 | libc::stat(p.as_ptr(), &mut stat as *mut _ as *mut _) |
416331ca XL |
547 | })?; |
548 | Ok(FileAttr { stat }) | |
549 | } | |
550 | ||
551 | pub fn lstat(p: &Path) -> io::Result<FileAttr> { | |
552 | let p = cstr(p)?; | |
553 | let mut stat: stat64 = unsafe { mem::zeroed() }; | |
554 | cvt(unsafe { | |
555 | ::libc::lstat(p.as_ptr(), &mut stat as *mut _ as *mut _) | |
556 | })?; | |
557 | Ok(FileAttr { stat }) | |
558 | } | |
559 | ||
560 | pub fn canonicalize(p: &Path) -> io::Result<PathBuf> { | |
561 | use crate::sys::vxworks::ext::ffi::OsStrExt; | |
562 | let path = CString::new(p.as_os_str().as_bytes())?; | |
563 | let buf; | |
564 | unsafe { | |
565 | let r = libc::realpath(path.as_ptr(), ptr::null_mut()); | |
566 | if r.is_null() { | |
567 | return Err(io::Error::last_os_error()) | |
568 | } | |
569 | buf = CStr::from_ptr(r).to_bytes().to_vec(); | |
570 | libc::free(r as *mut _); | |
571 | } | |
572 | Ok(PathBuf::from(OsString::from_vec(buf))) | |
573 | } | |
574 | ||
575 | pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { | |
576 | use crate::fs::File; | |
577 | if !from.is_file() { | |
578 | return Err(Error::new(ErrorKind::InvalidInput, | |
579 | "the source path is not an existing regular file")) | |
580 | } | |
581 | ||
582 | let mut reader = File::open(from)?; | |
583 | let mut writer = File::create(to)?; | |
584 | let perm = reader.metadata()?.permissions(); | |
585 | ||
586 | let ret = io::copy(&mut reader, &mut writer)?; | |
587 | writer.set_permissions(perm)?; | |
588 | Ok(ret) | |
589 | } |