1use std::borrow::Cow;
2use std::env;
3use std::fs;
4use std::io::Result;
5use std::path::Path;
6use std::path::PathBuf;
7use std::time::SystemTime;
8
9use io::IsTerminal;
10
11use super::strip_unc_prefix;
12use super::RealSys;
13
14use crate::*;
15
16impl EnvCurrentDir for RealSys {
19 #[inline]
20 fn env_current_dir(&self) -> std::io::Result<PathBuf> {
21 env::current_dir()
22 }
23}
24
25impl BaseEnvSetCurrentDir for RealSys {
26 #[inline]
27 fn base_env_set_current_dir(&self, path: &Path) -> std::io::Result<()> {
28 env::set_current_dir(path)
29 }
30}
31
32impl BaseEnvVar for RealSys {
33 #[inline]
34 fn base_env_var_os(&self, key: &OsStr) -> Option<OsString> {
35 env::var_os(key)
36 }
37}
38
39impl BaseEnvSetVar for RealSys {
40 #[inline]
41 fn base_env_set_var(&self, key: &OsStr, value: &OsStr) {
42 env::set_var(key, value);
43 }
44}
45
46#[cfg(all(unix, feature = "libc"))]
47impl EnvUmask for RealSys {
48 fn env_umask(&self) -> std::io::Result<u32> {
49 use libc::mode_t;
50 use libc::umask;
51
52 unsafe {
54 let current_umask = umask(0o000 as mode_t);
57 umask(current_umask);
58 Ok(current_umask as u32)
59 }
60 }
61}
62
63#[cfg(not(unix))]
64impl EnvUmask for RealSys {
65 fn env_umask(&self) -> std::io::Result<u32> {
66 Err(std::io::Error::new(
67 ErrorKind::Unsupported,
68 "umask is not supported on this platform",
69 ))
70 }
71}
72
73#[cfg(all(unix, feature = "libc"))]
74impl EnvSetUmask for RealSys {
75 fn env_set_umask(&self, value: u32) -> std::io::Result<u32> {
76 unsafe {
78 use libc::mode_t;
79 use libc::umask;
80
81 let current_umask = umask(value as mode_t);
82 Ok(current_umask as u32)
83 }
84 }
85}
86
87#[cfg(not(unix))]
88impl EnvSetUmask for RealSys {
89 fn env_set_umask(&self, _umask: u32) -> std::io::Result<u32> {
90 Err(std::io::Error::new(
91 ErrorKind::Unsupported,
92 "umask is not supported on this platform",
93 ))
94 }
95}
96
97#[cfg(any(
98 all(target_os = "windows", feature = "winapi"),
99 all(unix, feature = "libc")
100))]
101impl EnvCacheDir for RealSys {
102 #[inline]
103 fn env_cache_dir(&self) -> Option<PathBuf> {
104 real_cache_dir_with_env(self)
105 }
106}
107
108#[cfg(any(
111 all(target_os = "windows", feature = "winapi"),
112 all(unix, feature = "libc")
113))]
114pub fn real_cache_dir_with_env(
115 env: &(impl EnvVar + EnvHomeDir),
116) -> Option<PathBuf> {
117 #[cfg(all(target_os = "windows", feature = "winapi"))]
118 {
119 let _ = env;
120 known_folder(&windows_sys::Win32::UI::Shell::FOLDERID_LocalAppData)
121 }
122 #[cfg(all(unix, feature = "libc"))]
123 {
124 if cfg!(target_os = "macos") {
125 env.env_home_dir().map(|h| h.join("Library/Caches"))
126 } else {
127 env
128 .env_var_path("XDG_CACHE_HOME")
129 .or_else(|| env.env_home_dir().map(|home| home.join(".cache")))
130 }
131 }
132}
133
134#[cfg(any(
135 all(target_os = "windows", feature = "winapi"),
136 all(unix, feature = "libc")
137))]
138impl EnvHomeDir for RealSys {
139 fn env_home_dir(&self) -> Option<PathBuf> {
140 real_home_dir_with_env(self)
141 }
142}
143
144#[cfg(any(
147 all(target_os = "windows", feature = "winapi"),
148 all(unix, feature = "libc")
149))]
150pub fn real_home_dir_with_env(env: &impl EnvVar) -> Option<PathBuf> {
151 #[cfg(all(target_os = "windows", feature = "winapi"))]
152 {
153 env.env_var_path("USERPROFILE").or_else(|| {
154 known_folder(&windows_sys::Win32::UI::Shell::FOLDERID_Profile)
155 })
156 }
157 #[cfg(all(unix, feature = "libc"))]
158 {
159 unsafe fn fallback() -> Option<std::ffi::OsString> {
161 let amt = match libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX) {
162 n if n < 0 => 512_usize,
163 n => n as usize,
164 };
165 let mut buf = Vec::with_capacity(amt);
166 let mut passwd: libc::passwd = std::mem::zeroed();
167 let mut result = std::ptr::null_mut();
168 match libc::getpwuid_r(
169 libc::getuid(),
170 &mut passwd,
171 buf.as_mut_ptr(),
172 buf.capacity(),
173 &mut result,
174 ) {
175 0 if !result.is_null() => {
176 let ptr = passwd.pw_dir as *const _;
177 let bytes = std::ffi::CStr::from_ptr(ptr).to_bytes().to_vec();
178 Some(std::os::unix::ffi::OsStringExt::from_vec(bytes))
179 }
180 _ => None,
181 }
182 }
183
184 env.env_var_path("HOME").or_else(|| {
185 unsafe { fallback().map(PathBuf::from) }
187 })
188 }
189}
190
191impl EnvTempDir for RealSys {
192 #[inline]
193 fn env_temp_dir(&self) -> std::io::Result<PathBuf> {
194 Ok(env::temp_dir())
195 }
196}
197
198impl BaseFsCanonicalize for RealSys {
201 #[inline]
202 fn base_fs_canonicalize(&self, path: &Path) -> Result<PathBuf> {
203 fs::canonicalize(path).map(strip_unc_prefix)
204 }
205}
206
207#[cfg(unix)]
208impl BaseFsChown for RealSys {
209 #[inline]
210 fn base_fs_chown(
211 &self,
212 path: &Path,
213 uid: Option<u32>,
214 gid: Option<u32>,
215 ) -> io::Result<()> {
216 std::os::unix::fs::chown(path, uid, gid)
217 }
218}
219
220#[cfg(not(unix))]
221impl BaseFsChown for RealSys {
222 #[inline]
223 fn base_fs_chown(
224 &self,
225 _path: &Path,
226 _uid: Option<u32>,
227 _gid: Option<u32>,
228 ) -> io::Result<()> {
229 Err(Error::new(
230 ErrorKind::Unsupported,
231 "chown is not supported on this platform",
232 ))
233 }
234}
235
236#[cfg(unix)]
237impl BaseFsSymlinkChown for RealSys {
238 #[inline]
239 fn base_fs_symlink_chown(
240 &self,
241 path: &Path,
242 uid: Option<u32>,
243 gid: Option<u32>,
244 ) -> io::Result<()> {
245 std::os::unix::fs::lchown(path, uid, gid)
246 }
247}
248
249#[cfg(not(unix))]
250impl BaseFsSymlinkChown for RealSys {
251 #[inline]
252 fn base_fs_symlink_chown(
253 &self,
254 _path: &Path,
255 _uid: Option<u32>,
256 _gid: Option<u32>,
257 ) -> io::Result<()> {
258 Err(Error::new(
259 ErrorKind::Unsupported,
260 "lchown is not supported on this platform",
261 ))
262 }
263}
264
265#[cfg(all(target_vendor = "apple", feature = "libc"))]
266impl BaseFsCloneFile for RealSys {
267 #[inline]
268 fn base_fs_clone_file(&self, from: &Path, to: &Path) -> std::io::Result<()> {
269 use std::os::unix::ffi::OsStrExt;
270 let from = std::ffi::CString::new(from.as_os_str().as_bytes())?;
271 let to = std::ffi::CString::new(to.as_os_str().as_bytes())?;
272 let ret = unsafe { libc::clonefile(from.as_ptr(), to.as_ptr(), 0) };
274 if ret != 0 {
275 return Err(std::io::Error::last_os_error());
276 }
277 Ok(())
278 }
279}
280
281#[cfg(not(all(target_vendor = "apple", feature = "libc")))]
282impl BaseFsCloneFile for RealSys {
283 fn base_fs_clone_file(&self, _from: &Path, _to: &Path) -> io::Result<()> {
284 Err(std::io::Error::new(
285 ErrorKind::Unsupported,
286 "clonefile is not supported on this platform or the libc feature in sys_traits is not enabled",
287 ))
288 }
289}
290
291impl BaseFsCopy for RealSys {
292 #[inline]
293 fn base_fs_copy(&self, from: &Path, to: &Path) -> std::io::Result<u64> {
294 fs::copy(from, to)
295 }
296}
297
298impl BaseFsCreateDir for RealSys {
299 fn base_fs_create_dir(
300 &self,
301 path: &Path,
302 options: &CreateDirOptions,
303 ) -> Result<()> {
304 let mut builder = fs::DirBuilder::new();
305 builder.recursive(options.recursive);
306 #[cfg(unix)]
307 {
308 use std::os::unix::fs::DirBuilderExt;
309 if let Some(mode) = options.mode {
310 builder.mode(mode);
311 }
312 }
313 builder.create(path)
314 }
315}
316
317impl BaseFsHardLink for RealSys {
318 #[inline]
319 fn base_fs_hard_link(&self, src: &Path, dst: &Path) -> Result<()> {
320 fs::hard_link(src, dst)
321 }
322}
323
324macro_rules! unix_metadata_prop {
325 ($id:ident, $type:ident) => {
326 #[inline]
327 fn $id(&self) -> Result<$type> {
328 #[cfg(unix)]
329 {
330 use std::os::unix::fs::MetadataExt;
331 Ok(self.0.$id())
332 }
333 #[cfg(not(unix))]
334 {
335 Err(Error::new(
336 ErrorKind::Unsupported,
337 concat!(stringify!($id), " is not supported on this platform"),
338 ))
339 }
340 }
341 };
342}
343
344macro_rules! unix_metadata_file_type_prop {
345 ($id:ident, $type:ident) => {
346 #[inline]
347 fn $id(&self) -> Result<$type> {
348 #[cfg(unix)]
349 {
350 use std::os::unix::fs::FileTypeExt;
351 Ok(self.0.file_type().$id())
352 }
353 #[cfg(not(unix))]
354 {
355 Err(Error::new(
356 ErrorKind::Unsupported,
357 concat!(stringify!($id), " is not supported on this platform"),
358 ))
359 }
360 }
361 };
362}
363
364#[derive(Debug, Clone)]
368pub struct RealFsMetadata(fs::Metadata);
369
370impl FsMetadataValue for RealFsMetadata {
371 #[inline]
372 fn file_type(&self) -> FileType {
373 self.0.file_type().into()
374 }
375
376 #[inline]
377 fn len(&self) -> u64 {
378 self.0.len()
379 }
380
381 #[inline]
382 fn accessed(&self) -> Result<SystemTime> {
383 self.0.accessed()
384 }
385
386 #[inline]
387 fn changed(&self) -> Result<SystemTime> {
388 #[cfg(unix)]
389 {
390 use std::os::unix::fs::MetadataExt;
391 let changed = self.0.ctime();
392 Ok(
393 SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(changed as u64),
394 )
395 }
396 #[cfg(not(unix))]
397 {
398 Err(Error::new(
399 ErrorKind::Unsupported,
400 "ctime is not supported on this platform",
401 ))
402 }
403 }
404
405 #[inline]
406 fn created(&self) -> Result<SystemTime> {
407 self.0.created()
408 }
409
410 #[inline]
411 fn modified(&self) -> Result<SystemTime> {
412 self.0.modified()
413 }
414
415 unix_metadata_prop!(dev, u64);
416 unix_metadata_prop!(ino, u64);
417 unix_metadata_prop!(mode, u32);
418 unix_metadata_prop!(nlink, u64);
419 unix_metadata_prop!(uid, u32);
420 unix_metadata_prop!(gid, u32);
421 unix_metadata_prop!(rdev, u64);
422 unix_metadata_prop!(blksize, u64);
423 unix_metadata_prop!(blocks, u64);
424 unix_metadata_file_type_prop!(is_block_device, bool);
425 unix_metadata_file_type_prop!(is_char_device, bool);
426 unix_metadata_file_type_prop!(is_fifo, bool);
427 unix_metadata_file_type_prop!(is_socket, bool);
428
429 fn file_attributes(&self) -> io::Result<u32> {
430 #[cfg(windows)]
431 {
432 use std::os::windows::prelude::MetadataExt;
433 Ok(self.0.file_attributes())
434 }
435 #[cfg(not(windows))]
436 {
437 Err(Error::new(
438 ErrorKind::Unsupported,
439 "file_attributes is not supported on this platform",
440 ))
441 }
442 }
443}
444
445impl BaseFsMetadata for RealSys {
446 type Metadata = RealFsMetadata;
447
448 #[inline]
449 fn base_fs_metadata(&self, path: &Path) -> Result<Self::Metadata> {
450 fs::metadata(path).map(RealFsMetadata)
451 }
452
453 #[inline]
454 fn base_fs_symlink_metadata(&self, path: &Path) -> Result<Self::Metadata> {
455 fs::symlink_metadata(path).map(RealFsMetadata)
456 }
457
458 #[cfg(any(all(unix, feature = "libc"), all(windows, feature = "winapi")))]
459 #[inline]
460 fn base_fs_exists_no_err(&self, path: &Path) -> bool {
461 #[cfg(unix)]
462 {
463 use libc::access;
464 use libc::F_OK;
465 use std::os::unix::ffi::OsStrExt;
466
467 let Ok(c_path) = std::ffi::CString::new(path.as_os_str().as_bytes())
468 else {
469 return false;
470 };
471
472 unsafe { access(c_path.as_ptr(), F_OK) == 0 }
474 }
475
476 #[cfg(windows)]
477 {
478 use std::os::windows::ffi::OsStrExt;
479 use windows_sys::Win32::Storage::FileSystem::GetFileAttributesW;
480 use windows_sys::Win32::Storage::FileSystem::INVALID_FILE_ATTRIBUTES;
481
482 let wide_path: Vec<u16> = path
483 .as_os_str()
484 .encode_wide()
485 .chain(std::iter::once(0))
486 .collect();
487
488 unsafe {
490 GetFileAttributesW(wide_path.as_ptr()) != INVALID_FILE_ATTRIBUTES
491 }
492 }
493 }
494}
495
496impl BaseFsOpen for RealSys {
497 type File = RealFsFile;
498
499 fn base_fs_open(
500 &self,
501 path: &Path,
502 options: &OpenOptions,
503 ) -> std::io::Result<Self::File> {
504 let mut builder = fs::OpenOptions::new();
505 if let Some(mode) = options.mode {
506 #[cfg(unix)]
507 {
508 use std::os::unix::fs::OpenOptionsExt;
509 builder.mode(mode);
510 }
511 #[cfg(not(unix))]
512 let _ = mode;
513 }
514 if let Some(flags) = options.custom_flags {
515 #[cfg(unix)]
516 {
517 use std::os::unix::fs::OpenOptionsExt;
518 builder.custom_flags(flags);
519 }
520 #[cfg(windows)]
521 {
522 use std::os::windows::fs::OpenOptionsExt;
523 builder.custom_flags(flags);
524 }
525 #[cfg(all(not(windows), not(unix)))]
526 let _ = flags;
527 }
528 if let Some(value) = options.access_mode {
529 #[cfg(windows)]
530 {
531 use std::os::windows::fs::OpenOptionsExt;
532 builder.access_mode(value);
533 }
534 #[cfg(not(windows))]
535 let _ = value;
536 }
537 if let Some(value) = options.share_mode {
538 #[cfg(windows)]
539 {
540 use std::os::windows::fs::OpenOptionsExt;
541 builder.share_mode(value);
542 }
543 #[cfg(not(windows))]
544 let _ = value;
545 }
546 if let Some(value) = options.attributes {
547 #[cfg(windows)]
548 {
549 use std::os::windows::fs::OpenOptionsExt;
550 builder.attributes(value);
551 }
552 #[cfg(not(windows))]
553 let _ = value;
554 }
555 if let Some(value) = options.security_qos_flags {
556 #[cfg(windows)]
557 {
558 use std::os::windows::fs::OpenOptionsExt;
559 builder.security_qos_flags(value);
560 }
561 #[cfg(not(windows))]
562 let _ = value;
563 }
564 builder
565 .read(options.read)
566 .write(options.write)
567 .create(options.create)
568 .truncate(options.truncate)
569 .append(options.append)
570 .create_new(options.create_new)
571 .open(path)
572 .map(RealFsFile)
573 }
574}
575
576impl BaseFsRead for RealSys {
577 #[inline]
578 fn base_fs_read(&self, path: &Path) -> Result<Cow<'static, [u8]>> {
579 fs::read(path).map(Cow::Owned)
580 }
581}
582
583#[derive(Debug)]
584pub struct RealFsDirEntry(fs::DirEntry);
585
586impl FsDirEntry for RealFsDirEntry {
587 type Metadata = RealFsMetadata;
588
589 #[inline]
590 fn file_name(&self) -> Cow<OsStr> {
591 Cow::Owned(self.0.file_name())
592 }
593
594 #[inline]
595 fn file_type(&self) -> std::io::Result<FileType> {
596 self.0.file_type().map(FileType::from)
597 }
598
599 #[inline]
600 fn metadata(&self) -> std::io::Result<Self::Metadata> {
601 self.0.metadata().map(RealFsMetadata)
602 }
603
604 #[inline]
605 fn path(&self) -> Cow<Path> {
606 Cow::Owned(self.0.path())
607 }
608}
609
610impl BaseFsReadDir for RealSys {
611 type ReadDirEntry = RealFsDirEntry;
612
613 #[inline]
614 fn base_fs_read_dir(
615 &self,
616 path: &Path,
617 ) -> std::io::Result<
618 Box<dyn Iterator<Item = std::io::Result<Self::ReadDirEntry>> + '_>,
619 > {
620 let iterator = fs::read_dir(path)?;
621 Ok(Box::new(iterator.map(|result| result.map(RealFsDirEntry))))
622 }
623}
624
625impl BaseFsReadLink for RealSys {
626 fn base_fs_read_link(&self, path: &Path) -> io::Result<PathBuf> {
627 fs::read_link(path)
628 }
629}
630
631impl BaseFsRemoveDir for RealSys {
632 #[inline]
633 fn base_fs_remove_dir(&self, path: &Path) -> std::io::Result<()> {
634 fs::remove_dir(path)
635 }
636}
637
638impl BaseFsRemoveDirAll for RealSys {
639 #[inline]
640 fn base_fs_remove_dir_all(&self, path: &Path) -> std::io::Result<()> {
641 fs::remove_dir_all(path)
642 }
643}
644
645impl BaseFsRemoveFile for RealSys {
646 #[inline]
647 fn base_fs_remove_file(&self, path: &Path) -> std::io::Result<()> {
648 fs::remove_file(path)
649 }
650}
651
652impl BaseFsRename for RealSys {
653 #[inline]
654 fn base_fs_rename(&self, from: &Path, to: &Path) -> std::io::Result<()> {
655 fs::rename(from, to)
656 }
657}
658
659#[cfg(feature = "filetime")]
660impl BaseFsSetFileTimes for RealSys {
661 #[inline]
662 fn base_fs_set_file_times(
663 &self,
664 path: &Path,
665 atime: SystemTime,
666 mtime: SystemTime,
667 ) -> Result<()> {
668 let atime = filetime::FileTime::from_system_time(atime);
669 let mtime = filetime::FileTime::from_system_time(mtime);
670 filetime::set_file_times(path, atime, mtime)
671 }
672}
673
674#[cfg(feature = "filetime")]
675impl BaseFsSetSymlinkFileTimes for RealSys {
676 #[inline]
677 fn base_fs_set_symlink_file_times(
678 &self,
679 path: &Path,
680 atime: SystemTime,
681 mtime: SystemTime,
682 ) -> Result<()> {
683 let atime = filetime::FileTime::from_system_time(atime);
684 let mtime = filetime::FileTime::from_system_time(mtime);
685 filetime::set_symlink_file_times(path, atime, mtime)
686 }
687}
688
689#[cfg(unix)]
690impl BaseFsSetPermissions for RealSys {
691 #[inline]
692 fn base_fs_set_permissions(
693 &self,
694 path: &Path,
695 mode: u32,
696 ) -> std::io::Result<()> {
697 use std::os::unix::fs::PermissionsExt;
698 let permissions = fs::Permissions::from_mode(mode);
699 fs::set_permissions(path, permissions)
700 }
701}
702
703#[cfg(not(unix))]
704impl BaseFsSetPermissions for RealSys {
705 fn base_fs_set_permissions(
706 &self,
707 _path: &Path,
708 _mode: u32,
709 ) -> std::io::Result<()> {
710 Err(std::io::Error::new(
711 ErrorKind::Unsupported,
712 "cannot set path permissions on this platform",
713 ))
714 }
715}
716
717impl BaseFsSymlinkDir for RealSys {
718 fn base_fs_symlink_dir(
719 &self,
720 original: &Path,
721 link: &Path,
722 ) -> std::io::Result<()> {
723 #[cfg(windows)]
724 {
725 std::os::windows::fs::symlink_dir(original, link)
726 }
727 #[cfg(not(windows))]
728 {
729 std::os::unix::fs::symlink(original, link)
730 }
731 }
732}
733
734impl BaseFsSymlinkFile for RealSys {
735 fn base_fs_symlink_file(
736 &self,
737 original: &Path,
738 link: &Path,
739 ) -> std::io::Result<()> {
740 #[cfg(windows)]
741 {
742 std::os::windows::fs::symlink_file(original, link)
743 }
744 #[cfg(not(windows))]
745 {
746 std::os::unix::fs::symlink(original, link)
747 }
748 }
749}
750
751impl BaseFsWrite for RealSys {
752 #[inline]
753 fn base_fs_write(&self, path: &Path, data: &[u8]) -> std::io::Result<()> {
754 fs::write(path, data)
755 }
756}
757
758#[derive(Debug)]
764pub struct RealFsFile(fs::File);
765
766impl FsFile for RealFsFile {}
767
768impl FsFileAsRaw for RealFsFile {
769 #[cfg(windows)]
770 #[inline]
771 fn fs_file_as_raw_handle(&self) -> Option<std::os::windows::io::RawHandle> {
772 use std::os::windows::io::AsRawHandle;
773 Some(self.0.as_raw_handle())
774 }
775
776 #[cfg(unix)]
777 #[inline]
778 fn fs_file_as_raw_fd(&self) -> Option<std::os::fd::RawFd> {
779 use std::os::fd::AsRawFd;
780 Some(self.0.as_raw_fd())
781 }
782}
783
784impl FsFileIsTerminal for RealFsFile {
785 #[inline]
786 fn fs_file_is_terminal(&self) -> bool {
787 self.0.is_terminal()
788 }
789}
790
791impl FsFileLock for RealFsFile {
792 fn fs_file_lock(&mut self, mode: FsFileLockMode) -> io::Result<()> {
793 lock_file(&self.0, mode, false)
794 }
795
796 fn fs_file_try_lock(&mut self, mode: FsFileLockMode) -> io::Result<()> {
797 lock_file(&self.0, mode, true)
798 }
799
800 fn fs_file_unlock(&mut self) -> io::Result<()> {
801 unlock_file(&self.0)
802 }
803}
804
805#[cfg(all(unix, feature = "libc"))]
806fn lock_file(
807 file: &fs::File,
808 mode: FsFileLockMode,
809 try_lock: bool,
810) -> Result<()> {
811 let operation = match mode {
812 FsFileLockMode::Shared => libc::LOCK_SH,
813 FsFileLockMode::Exclusive => libc::LOCK_EX,
814 } | if try_lock { libc::LOCK_NB } else { 0 };
815
816 flock(file, operation)
817}
818
819#[cfg(all(unix, feature = "libc"))]
820#[inline]
821fn unlock_file(file: &fs::File) -> Result<()> {
822 flock(file, libc::LOCK_UN)
823}
824
825#[cfg(all(unix, feature = "libc"))]
826fn flock(file: &fs::File, operation: i32) -> Result<()> {
827 use std::os::unix::io::AsRawFd;
828
829 unsafe {
831 let fd = file.as_raw_fd();
832 let result = libc::flock(fd, operation);
833 if result < 0 {
834 Err(Error::last_os_error())
835 } else {
836 Ok(())
837 }
838 }
839}
840
841#[cfg(all(windows, feature = "winapi"))]
842fn lock_file(
843 file: &fs::File,
844 mode: FsFileLockMode,
845 try_lock: bool,
846) -> Result<()> {
847 use std::os::windows::io::AsRawHandle;
848
849 use windows_sys::Win32::Foundation::FALSE;
850 use windows_sys::Win32::Storage::FileSystem::LockFileEx;
851 use windows_sys::Win32::Storage::FileSystem::LOCKFILE_EXCLUSIVE_LOCK;
852 use windows_sys::Win32::Storage::FileSystem::LOCKFILE_FAIL_IMMEDIATELY;
853
854 let flags = match mode {
855 FsFileLockMode::Shared => 0,
856 FsFileLockMode::Exclusive => LOCKFILE_EXCLUSIVE_LOCK,
857 } | if try_lock {
858 LOCKFILE_FAIL_IMMEDIATELY
859 } else {
860 0
861 };
862
863 unsafe {
865 let mut overlapped = std::mem::zeroed();
866 let success =
867 LockFileEx(file.as_raw_handle(), flags, 0, !0, !0, &mut overlapped);
868 if success == FALSE {
869 Err(Error::last_os_error())
870 } else {
871 Ok(())
872 }
873 }
874}
875
876#[cfg(all(windows, feature = "winapi"))]
877fn unlock_file(file: &fs::File) -> Result<()> {
878 use std::os::windows::io::AsRawHandle;
879
880 use windows_sys::Win32::Foundation::FALSE;
881 use windows_sys::Win32::Storage::FileSystem::UnlockFile;
882
883 unsafe {
885 let success = UnlockFile(file.as_raw_handle(), 0, 0, !0, !0);
886 if success == FALSE {
887 Err(Error::last_os_error())
888 } else {
889 Ok(())
890 }
891 }
892}
893
894#[cfg(not(any(
895 all(unix, feature = "libc"),
896 all(windows, feature = "winapi")
897)))]
898fn lock_file(
899 _file: &fs::File,
900 _mode: FsFileLockMode,
901 _try_lock: bool,
902) -> Result<()> {
903 Err(Error::new(
904 ErrorKind::Unsupported,
905 "file locking is not supported on this platform or the libc/winapi feature is not enabled",
906 ))
907}
908
909#[cfg(not(any(
910 all(unix, feature = "libc"),
911 all(windows, feature = "winapi")
912)))]
913fn unlock_file(_file: &fs::File) -> Result<()> {
914 Err(Error::new(
915 ErrorKind::Unsupported,
916 "file locking is not supported on this platform or the libc/winapi feature is not enabled",
917 ))
918}
919
920impl FsFileSetLen for RealFsFile {
921 #[inline]
922 fn fs_file_set_len(&mut self, size: u64) -> std::io::Result<()> {
923 self.0.set_len(size)
924 }
925}
926
927impl FsFileSetPermissions for RealFsFile {
928 #[inline]
929 fn fs_file_set_permissions(&mut self, mode: u32) -> Result<()> {
930 #[cfg(unix)]
931 {
932 use std::os::unix::fs::PermissionsExt;
933 let permissions = fs::Permissions::from_mode(mode);
934 self.0.set_permissions(permissions)
935 }
936 #[cfg(not(unix))]
937 {
938 let _ = mode;
939 Ok(())
940 }
941 }
942}
943
944impl FsFileSetTimes for RealFsFile {
945 fn fs_file_set_times(&mut self, times: FsFileTimes) -> io::Result<()> {
946 let mut std_times = std::fs::FileTimes::new();
947 if let Some(atime) = times.accessed {
948 std_times = std_times.set_accessed(atime);
949 }
950 if let Some(mtime) = times.modified {
951 std_times = std_times.set_modified(mtime);
952 }
953 self.0.set_times(std_times)
954 }
955}
956
957impl FsFileSyncAll for RealFsFile {
958 #[inline]
959 fn fs_file_sync_all(&mut self) -> io::Result<()> {
960 self.0.sync_all()
961 }
962}
963
964impl FsFileSyncData for RealFsFile {
965 #[inline]
966 fn fs_file_sync_data(&mut self) -> io::Result<()> {
967 self.0.sync_data()
968 }
969}
970
971impl std::io::Seek for RealFsFile {
972 #[inline]
973 fn seek(&mut self, pos: std::io::SeekFrom) -> Result<u64> {
974 self.0.seek(pos)
975 }
976}
977
978impl std::io::Write for RealFsFile {
979 #[inline]
980 fn write(&mut self, buf: &[u8]) -> Result<usize> {
981 self.0.write(buf)
982 }
983
984 #[inline]
985 fn flush(&mut self) -> Result<()> {
986 self.0.flush()
987 }
988}
989
990impl std::io::Read for RealFsFile {
991 #[inline]
992 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
993 self.0.read(buf)
994 }
995}
996
997impl SystemTimeNow for RealSys {
1000 #[inline]
1001 fn sys_time_now(&self) -> SystemTime {
1002 SystemTime::now()
1003 }
1004}
1005
1006#[cfg(feature = "getrandom")]
1007impl crate::SystemRandom for RealSys {
1008 #[inline]
1009 fn sys_random(&self, buf: &mut [u8]) -> Result<()> {
1010 getrandom::getrandom(buf)
1011 .map_err(|err| Error::new(ErrorKind::Other, err.to_string()))
1012 }
1013}
1014
1015impl crate::ThreadSleep for RealSys {
1016 #[inline]
1017 fn thread_sleep(&self, duration: std::time::Duration) {
1018 std::thread::sleep(duration);
1019 }
1020}
1021
1022#[cfg(all(windows, feature = "winapi"))]
1023fn known_folder(folder_id: *const windows_sys::core::GUID) -> Option<PathBuf> {
1024 use std::ffi::c_void;
1025 use std::os::windows::ffi::OsStringExt;
1026 use windows_sys::Win32::Foundation::S_OK;
1027 use windows_sys::Win32::Globalization::lstrlenW;
1028 use windows_sys::Win32::System::Com::CoTaskMemFree;
1029 use windows_sys::Win32::UI::Shell::SHGetKnownFolderPath;
1030
1031 unsafe {
1033 let mut path_ptr = std::ptr::null_mut();
1034 let result =
1035 SHGetKnownFolderPath(folder_id, 0, std::ptr::null_mut(), &mut path_ptr);
1036 if result != S_OK {
1037 return None;
1038 }
1039 let len = lstrlenW(path_ptr) as usize;
1040 let path = std::slice::from_raw_parts(path_ptr, len);
1041 let ostr: OsString = OsStringExt::from_wide(path);
1042 CoTaskMemFree(path_ptr as *mut c_void);
1043 Some(PathBuf::from(ostr))
1044 }
1045}
1046
1047#[cfg(test)]
1048mod test {
1049 use super::*;
1050
1051 #[cfg(any(feature = "winapi", feature = "libc"))]
1052 #[test]
1053 fn test_known_folders() {
1054 assert!(RealSys.env_cache_dir().is_some());
1055 assert!(RealSys.env_home_dir().is_some());
1056 }
1057
1058 #[cfg(all(unix, feature = "libc"))]
1059 #[test]
1060 fn test_umask() {
1061 let original_umask = RealSys.env_umask().unwrap();
1062 assert_eq!(RealSys.env_set_umask(0o777).unwrap(), original_umask);
1063 assert_eq!(RealSys.env_set_umask(original_umask).unwrap(), 0o777);
1064 }
1065
1066 #[cfg(target_os = "windows")]
1067 #[test]
1068 fn test_umask() {
1069 let err = RealSys.env_umask().unwrap_err();
1070 assert_eq!(err.kind(), ErrorKind::Unsupported);
1071 let err = RealSys.env_set_umask(0o000).unwrap_err();
1072 assert_eq!(err.kind(), ErrorKind::Unsupported);
1073 }
1074
1075 #[test]
1076 fn test_general() {
1077 assert!(RealSys.sys_time_now().elapsed().is_ok());
1078 }
1079
1080 #[cfg(any(feature = "winapi", feature = "libc"))]
1081 #[test]
1082 fn lock_file() {
1083 let sys = RealSys;
1084 let mut file = sys.fs_open("Cargo.toml", &OpenOptions::new_read()).unwrap();
1085 file.fs_file_lock(FsFileLockMode::Shared).unwrap();
1086 file.fs_file_unlock().unwrap();
1087 file.fs_file_try_lock(FsFileLockMode::Shared).unwrap();
1088 file.fs_file_unlock().unwrap();
1089 file.fs_file_lock(FsFileLockMode::Exclusive).unwrap();
1090 file.fs_file_unlock().unwrap();
1091 file.fs_file_try_lock(FsFileLockMode::Exclusive).unwrap();
1092 file.fs_file_unlock().unwrap();
1093 }
1094
1095 #[test]
1096 fn test_exists_no_err() {
1097 assert!(RealSys.fs_exists_no_err("Cargo.toml"));
1098 assert!(!RealSys.fs_exists_no_err("Cargo2.toml"));
1099 }
1100
1101 #[test]
1102 fn test_clone_file() {
1103 let temp_dir = tempfile::tempdir().unwrap();
1104 let path = temp_dir.path();
1105 RealSys.fs_write(path.join("file.txt"), "data").unwrap();
1106 let result =
1107 RealSys.fs_clone_file(path.join("file.txt"), path.join("cloned.txt"));
1108 if cfg!(target_vendor = "apple") {
1109 assert!(result.is_ok());
1110 assert_eq!(
1111 RealSys.fs_read_to_string(path.join("cloned.txt")).unwrap(),
1112 "data"
1113 );
1114 } else {
1115 assert_eq!(result.unwrap_err().kind(), ErrorKind::Unsupported);
1116 }
1117 }
1118}