1#[cfg(feature = "fs_utf8")]
2use camino::Utf8Path;
3#[cfg(all(windows, feature = "async_std", feature = "fs_utf8"))]
4use cap_primitives::fs::stat;
5#[cfg(not(windows))]
6use cap_primitives::fs::symlink;
7use cap_primitives::fs::{
8 access, open_dir_nofollow, set_symlink_permissions, set_times, set_times_nofollow,
9 FollowSymlinks, Permissions,
10};
11#[cfg(windows)]
12use cap_primitives::fs::{symlink_dir, symlink_file};
13use io_lifetimes::AsFilelike;
14use std::io;
15use std::path::Path;
16#[cfg(feature = "async_std")]
17use {async_std::task::spawn_blocking, async_trait::async_trait};
18
19pub use cap_primitives::fs::{AccessType, SystemTimeSpec};
20
21pub trait DirExt {
23 fn set_atime<P: AsRef<Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()>;
29
30 fn set_mtime<P: AsRef<Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()>;
36
37 fn set_times<P: AsRef<Path>>(
43 &self,
44 path: P,
45 atime: Option<SystemTimeSpec>,
46 mtime: Option<SystemTimeSpec>,
47 ) -> io::Result<()>;
48
49 fn set_symlink_times<P: AsRef<Path>>(
56 &self,
57 path: P,
58 atime: Option<SystemTimeSpec>,
59 mtime: Option<SystemTimeSpec>,
60 ) -> io::Result<()>;
61
62 fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()>;
70
71 fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()>;
79
80 fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()>;
88
89 fn open_dir_nofollow<P: AsRef<Path>>(&self, path: P) -> io::Result<Self>
92 where
93 Self: Sized;
94
95 fn remove_file_or_symlink<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
101
102 fn access<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()>;
104
105 fn access_symlink<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()>;
108
109 fn set_symlink_permissions<P: AsRef<Path>>(&self, path: P, perm: Permissions)
112 -> io::Result<()>;
113}
114
115#[cfg(feature = "async_std")]
119#[async_trait]
120pub trait AsyncDirExt {
121 async fn set_atime<P: AsRef<async_std::path::Path> + Send>(
127 &self,
128 path: P,
129 atime: SystemTimeSpec,
130 ) -> io::Result<()>;
131
132 async fn set_mtime<P: AsRef<async_std::path::Path> + Send>(
138 &self,
139 path: P,
140 mtime: SystemTimeSpec,
141 ) -> io::Result<()>;
142
143 async fn set_times<P: AsRef<async_std::path::Path> + Send>(
149 &self,
150 path: P,
151 atime: Option<SystemTimeSpec>,
152 mtime: Option<SystemTimeSpec>,
153 ) -> io::Result<()>;
154
155 async fn set_symlink_times<P: AsRef<async_std::path::Path> + Send>(
162 &self,
163 path: P,
164 atime: Option<SystemTimeSpec>,
165 mtime: Option<SystemTimeSpec>,
166 ) -> io::Result<()>;
167
168 async fn symlink<
176 P: AsRef<async_std::path::Path> + Send,
177 Q: AsRef<async_std::path::Path> + Send,
178 >(
179 &self,
180 src: P,
181 dst: Q,
182 ) -> io::Result<()>;
183
184 async fn symlink_file<
192 P: AsRef<async_std::path::Path> + Send,
193 Q: AsRef<async_std::path::Path> + Send,
194 >(
195 &self,
196 src: P,
197 dst: Q,
198 ) -> io::Result<()>;
199
200 async fn symlink_dir<
208 P: AsRef<async_std::path::Path> + Send,
209 Q: AsRef<async_std::path::Path> + Send,
210 >(
211 &self,
212 src: P,
213 dst: Q,
214 ) -> io::Result<()>;
215
216 async fn open_dir_nofollow<P: AsRef<async_std::path::Path> + Send>(
219 &self,
220 path: P,
221 ) -> io::Result<Self>
222 where
223 Self: Sized;
224
225 async fn remove_file_or_symlink<P: AsRef<async_std::path::Path> + Send>(
231 &self,
232 path: P,
233 ) -> io::Result<()>;
234
235 async fn access<P: AsRef<async_std::path::Path> + Send>(
237 &self,
238 path: P,
239 type_: AccessType,
240 ) -> io::Result<()>;
241
242 async fn access_symlink<P: AsRef<async_std::path::Path> + Send>(
245 &self,
246 path: P,
247 type_: AccessType,
248 ) -> io::Result<()>;
249
250 async fn set_symlink_permissions<P: AsRef<async_std::path::Path> + Send>(
253 &self,
254 path: P,
255 perm: Permissions,
256 ) -> io::Result<()>;
257}
258
259#[cfg(all(any(feature = "std", feature = "async_std"), feature = "fs_utf8"))]
261pub trait DirExtUtf8 {
262 fn set_atime<P: AsRef<Utf8Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()>;
268
269 fn set_mtime<P: AsRef<Utf8Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()>;
275
276 fn set_times<P: AsRef<Utf8Path>>(
282 &self,
283 path: P,
284 atime: Option<SystemTimeSpec>,
285 mtime: Option<SystemTimeSpec>,
286 ) -> io::Result<()>;
287
288 fn set_symlink_times<P: AsRef<Utf8Path>>(
295 &self,
296 path: P,
297 atime: Option<SystemTimeSpec>,
298 mtime: Option<SystemTimeSpec>,
299 ) -> io::Result<()>;
300
301 fn symlink<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(&self, src: P, dst: Q) -> io::Result<()>;
309
310 fn symlink_file<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
318 &self,
319 src: P,
320 dst: Q,
321 ) -> io::Result<()>;
322
323 fn symlink_dir<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(&self, src: P, dst: Q)
331 -> io::Result<()>;
332
333 fn open_dir_nofollow<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<Self>
336 where
337 Self: Sized;
338
339 fn remove_file_or_symlink<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<()>;
345
346 fn access<P: AsRef<Utf8Path>>(&self, path: P, type_: AccessType) -> io::Result<()>;
348
349 fn access_symlink<P: AsRef<Utf8Path>>(&self, path: P, type_: AccessType) -> io::Result<()>;
352
353 fn set_symlink_permissions<P: AsRef<Utf8Path>>(
356 &self,
357 path: P,
358 perm: Permissions,
359 ) -> io::Result<()>;
360}
361
362#[cfg(all(feature = "async_std", feature = "fs_utf8"))]
366#[async_trait]
367pub trait AsyncDirExtUtf8 {
368 async fn set_atime<P: AsRef<Utf8Path> + Send>(
374 &self,
375 path: P,
376 atime: SystemTimeSpec,
377 ) -> io::Result<()>;
378
379 async fn set_mtime<P: AsRef<Utf8Path> + Send>(
385 &self,
386 path: P,
387 mtime: SystemTimeSpec,
388 ) -> io::Result<()>;
389
390 async fn set_times<P: AsRef<Utf8Path> + Send>(
396 &self,
397 path: P,
398 atime: Option<SystemTimeSpec>,
399 mtime: Option<SystemTimeSpec>,
400 ) -> io::Result<()>;
401
402 async fn set_symlink_times<P: AsRef<Utf8Path> + Send>(
409 &self,
410 path: P,
411 atime: Option<SystemTimeSpec>,
412 mtime: Option<SystemTimeSpec>,
413 ) -> io::Result<()>;
414
415 async fn symlink<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
423 &self,
424 src: P,
425 dst: Q,
426 ) -> io::Result<()>;
427
428 async fn symlink_file<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
436 &self,
437 src: P,
438 dst: Q,
439 ) -> io::Result<()>;
440
441 async fn symlink_dir<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
449 &self,
450 src: P,
451 dst: Q,
452 ) -> io::Result<()>;
453
454 async fn open_dir_nofollow<P: AsRef<Utf8Path> + Send>(&self, path: P) -> io::Result<Self>
457 where
458 Self: Sized;
459
460 async fn remove_file_or_symlink<P: AsRef<Utf8Path> + Send>(&self, path: P) -> io::Result<()>;
466
467 async fn access<P: AsRef<Utf8Path> + Send>(&self, path: P, type_: AccessType)
469 -> io::Result<()>;
470
471 async fn access_symlink<P: AsRef<Utf8Path> + Send>(
474 &self,
475 path: P,
476 type_: AccessType,
477 ) -> io::Result<()>;
478
479 async fn set_symlink_permissions<P: AsRef<Utf8Path> + Send>(
482 &self,
483 path: P,
484 perm: Permissions,
485 ) -> io::Result<()>;
486}
487
488#[cfg(feature = "std")]
489impl DirExt for cap_std::fs::Dir {
490 #[inline]
491 fn set_atime<P: AsRef<Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()> {
492 set_times(
493 &self.as_filelike_view::<std::fs::File>(),
494 path.as_ref(),
495 Some(atime),
496 None,
497 )
498 }
499
500 #[inline]
501 fn set_mtime<P: AsRef<Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()> {
502 set_times(
503 &self.as_filelike_view::<std::fs::File>(),
504 path.as_ref(),
505 None,
506 Some(mtime),
507 )
508 }
509
510 #[inline]
511 fn set_times<P: AsRef<Path>>(
512 &self,
513 path: P,
514 atime: Option<SystemTimeSpec>,
515 mtime: Option<SystemTimeSpec>,
516 ) -> io::Result<()> {
517 set_times(
518 &self.as_filelike_view::<std::fs::File>(),
519 path.as_ref(),
520 atime,
521 mtime,
522 )
523 }
524
525 #[inline]
526 fn set_symlink_times<P: AsRef<Path>>(
527 &self,
528 path: P,
529 atime: Option<SystemTimeSpec>,
530 mtime: Option<SystemTimeSpec>,
531 ) -> io::Result<()> {
532 set_times_nofollow(
533 &self.as_filelike_view::<std::fs::File>(),
534 path.as_ref(),
535 atime,
536 mtime,
537 )
538 }
539
540 #[cfg(not(windows))]
541 #[inline]
542 fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
543 symlink(
544 src.as_ref(),
545 &self.as_filelike_view::<std::fs::File>(),
546 dst.as_ref(),
547 )
548 }
549
550 #[cfg(not(windows))]
551 #[inline]
552 fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
553 self.symlink(src, dst)
554 }
555
556 #[cfg(not(windows))]
557 #[inline]
558 fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
559 self.symlink(src, dst)
560 }
561
562 #[cfg(windows)]
563 #[inline]
564 fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
565 if self.metadata(src.as_ref())?.is_dir() {
566 self.symlink_dir(src, dst)
567 } else {
568 self.symlink_file(src, dst)
569 }
570 }
571
572 #[cfg(windows)]
573 #[inline]
574 fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
575 symlink_file(
576 src.as_ref(),
577 &self.as_filelike_view::<std::fs::File>(),
578 dst.as_ref(),
579 )
580 }
581
582 #[cfg(windows)]
583 #[inline]
584 fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(&self, src: P, dst: Q) -> io::Result<()> {
585 symlink_dir(
586 src.as_ref(),
587 &self.as_filelike_view::<std::fs::File>(),
588 dst.as_ref(),
589 )
590 }
591
592 #[inline]
593 fn open_dir_nofollow<P: AsRef<Path>>(&self, path: P) -> io::Result<Self> {
594 match open_dir_nofollow(&self.as_filelike_view::<std::fs::File>(), path.as_ref()) {
595 Ok(file) => Ok(Self::from_std_file(file)),
596 Err(e) => Err(e),
597 }
598 }
599
600 #[cfg(not(windows))]
601 #[inline]
602 fn remove_file_or_symlink<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
603 self.remove_file(path.as_ref())
604 }
605
606 #[cfg(windows)]
607 #[inline]
608 fn remove_file_or_symlink<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
609 use crate::OpenOptionsFollowExt;
610 use cap_primitives::fs::_WindowsByHandle;
611 use cap_std::fs::{OpenOptions, OpenOptionsExt};
612 use windows_sys::Win32::Storage::FileSystem::{
613 DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
614 FILE_FLAG_OPEN_REPARSE_POINT,
615 };
616 let path = path.as_ref();
617
618 let mut opts = OpenOptions::new();
619 opts.access_mode(DELETE);
620 opts.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
621 opts.follow(FollowSymlinks::No);
622 let file = self.open_with(path, &opts)?;
623
624 let meta = file.metadata()?;
625 if meta.file_type().is_symlink()
626 && meta.file_attributes() & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY
627 {
628 self.remove_dir(path)?;
629 } else {
630 self.remove_file(path)?;
631 }
632
633 drop(file);
638
639 Ok(())
640 }
641
642 fn access<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
644 access(
645 &self.as_filelike_view::<std::fs::File>(),
646 path.as_ref(),
647 type_,
648 FollowSymlinks::Yes,
649 )
650 }
651
652 fn access_symlink<P: AsRef<Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
654 access(
655 &self.as_filelike_view::<std::fs::File>(),
656 path.as_ref(),
657 type_,
658 FollowSymlinks::No,
659 )
660 }
661
662 fn set_symlink_permissions<P: AsRef<Path>>(
665 &self,
666 path: P,
667 perm: Permissions,
668 ) -> io::Result<()> {
669 set_symlink_permissions(
670 &self.as_filelike_view::<std::fs::File>(),
671 path.as_ref(),
672 perm,
673 )
674 }
675}
676
677#[cfg(feature = "async_std")]
678#[async_trait]
679impl AsyncDirExt for cap_async_std::fs::Dir {
680 #[inline]
681 async fn set_atime<P: AsRef<async_std::path::Path> + Send>(
682 &self,
683 path: P,
684 atime: SystemTimeSpec,
685 ) -> io::Result<()> {
686 let path = path.as_ref().to_path_buf();
687 let clone = self.clone();
688 spawn_blocking(move || {
689 set_times(
690 &clone.as_filelike_view::<std::fs::File>(),
691 path.as_ref(),
692 Some(atime),
693 None,
694 )
695 })
696 .await
697 }
698
699 #[inline]
700 async fn set_mtime<P: AsRef<async_std::path::Path> + Send>(
701 &self,
702 path: P,
703 mtime: SystemTimeSpec,
704 ) -> io::Result<()> {
705 let path = path.as_ref().to_path_buf();
706 let clone = self.clone();
707 spawn_blocking(move || {
708 set_times(
709 &clone.as_filelike_view::<std::fs::File>(),
710 path.as_ref(),
711 None,
712 Some(mtime),
713 )
714 })
715 .await
716 }
717
718 #[inline]
719 async fn set_times<P: AsRef<async_std::path::Path> + Send>(
720 &self,
721 path: P,
722 atime: Option<SystemTimeSpec>,
723 mtime: Option<SystemTimeSpec>,
724 ) -> io::Result<()> {
725 let path = path.as_ref().to_path_buf();
726 let clone = self.clone();
727 spawn_blocking(move || {
728 set_times(
729 &clone.as_filelike_view::<std::fs::File>(),
730 path.as_ref(),
731 atime,
732 mtime,
733 )
734 })
735 .await
736 }
737
738 #[inline]
739 async fn set_symlink_times<P: AsRef<async_std::path::Path> + Send>(
740 &self,
741 path: P,
742 atime: Option<SystemTimeSpec>,
743 mtime: Option<SystemTimeSpec>,
744 ) -> io::Result<()> {
745 let path = path.as_ref().to_path_buf();
746 let clone = self.clone();
747 spawn_blocking(move || {
748 set_times_nofollow(
749 &clone.as_filelike_view::<std::fs::File>(),
750 path.as_ref(),
751 atime,
752 mtime,
753 )
754 })
755 .await
756 }
757
758 #[cfg(not(windows))]
759 #[inline]
760 async fn symlink<
761 P: AsRef<async_std::path::Path> + Send,
762 Q: AsRef<async_std::path::Path> + Send,
763 >(
764 &self,
765 src: P,
766 dst: Q,
767 ) -> io::Result<()> {
768 let src = src.as_ref().to_path_buf();
769 let dst = dst.as_ref().to_path_buf();
770 let clone = self.clone();
771 spawn_blocking(move || {
772 symlink(
773 src.as_ref(),
774 &clone.as_filelike_view::<std::fs::File>(),
775 dst.as_ref(),
776 )
777 })
778 .await
779 }
780
781 #[cfg(not(windows))]
782 #[inline]
783 async fn symlink_file<
784 P: AsRef<async_std::path::Path> + Send,
785 Q: AsRef<async_std::path::Path> + Send,
786 >(
787 &self,
788 src: P,
789 dst: Q,
790 ) -> io::Result<()> {
791 let src = src.as_ref().to_path_buf();
792 let dst = dst.as_ref().to_path_buf();
793 let clone = self.clone();
794 spawn_blocking(move || {
795 symlink(
796 src.as_ref(),
797 &clone.as_filelike_view::<std::fs::File>(),
798 dst.as_ref(),
799 )
800 })
801 .await
802 }
803
804 #[cfg(not(windows))]
805 #[inline]
806 async fn symlink_dir<
807 P: AsRef<async_std::path::Path> + Send,
808 Q: AsRef<async_std::path::Path> + Send,
809 >(
810 &self,
811 src: P,
812 dst: Q,
813 ) -> io::Result<()> {
814 let src = src.as_ref().to_path_buf();
815 let dst = dst.as_ref().to_path_buf();
816 let clone = self.clone();
817 spawn_blocking(move || {
818 symlink(
819 src.as_ref(),
820 &clone.as_filelike_view::<std::fs::File>(),
821 dst.as_ref(),
822 )
823 })
824 .await
825 }
826
827 #[cfg(windows)]
828 #[inline]
829 async fn symlink<
830 P: AsRef<async_std::path::Path> + Send,
831 Q: AsRef<async_std::path::Path> + Send,
832 >(
833 &self,
834 src: P,
835 dst: Q,
836 ) -> io::Result<()> {
837 let src = src.as_ref().to_path_buf();
838 let dst = dst.as_ref().to_path_buf();
839 let clone = self.clone();
840 if self.metadata(&src).await?.is_dir() {
841 spawn_blocking(move || {
842 symlink_dir(
843 src.as_ref(),
844 &clone.as_filelike_view::<std::fs::File>(),
845 dst.as_ref(),
846 )
847 })
848 .await
849 } else {
850 spawn_blocking(move || {
851 symlink_file(
852 src.as_ref(),
853 &clone.as_filelike_view::<std::fs::File>(),
854 dst.as_ref(),
855 )
856 })
857 .await
858 }
859 }
860
861 #[cfg(windows)]
862 #[inline]
863 async fn symlink_file<
864 P: AsRef<async_std::path::Path> + Send,
865 Q: AsRef<async_std::path::Path> + Send,
866 >(
867 &self,
868 src: P,
869 dst: Q,
870 ) -> io::Result<()> {
871 let src = src.as_ref().to_path_buf();
872 let dst = dst.as_ref().to_path_buf();
873 let clone = self.clone();
874 spawn_blocking(move || {
875 symlink_file(
876 src.as_ref(),
877 &clone.as_filelike_view::<std::fs::File>(),
878 dst.as_ref(),
879 )
880 })
881 .await
882 }
883
884 #[cfg(windows)]
885 #[inline]
886 async fn symlink_dir<
887 P: AsRef<async_std::path::Path> + Send,
888 Q: AsRef<async_std::path::Path> + Send,
889 >(
890 &self,
891 src: P,
892 dst: Q,
893 ) -> io::Result<()> {
894 let src = src.as_ref().to_path_buf();
895 let dst = dst.as_ref().to_path_buf();
896 let clone = self.clone();
897 spawn_blocking(move || {
898 symlink_dir(
899 src.as_ref(),
900 &clone.as_filelike_view::<std::fs::File>(),
901 dst.as_ref(),
902 )
903 })
904 .await
905 }
906
907 #[inline]
908 async fn open_dir_nofollow<P: AsRef<async_std::path::Path> + Send>(
909 &self,
910 path: P,
911 ) -> io::Result<Self> {
912 let path = path.as_ref().to_path_buf();
913 let clone = self.clone();
914 spawn_blocking(move || {
915 match open_dir_nofollow(&clone.as_filelike_view::<std::fs::File>(), path.as_ref()) {
916 Ok(file) => Ok(Self::from_std_file(file.into())),
917 Err(e) => Err(e),
918 }
919 })
920 .await
921 }
922
923 #[cfg(not(windows))]
924 #[inline]
925 async fn remove_file_or_symlink<P: AsRef<async_std::path::Path> + Send>(
926 &self,
927 path: P,
928 ) -> io::Result<()> {
929 self.remove_file(path).await
930 }
931
932 #[cfg(windows)]
933 #[inline]
934 async fn remove_file_or_symlink<P: AsRef<async_std::path::Path> + Send>(
935 &self,
936 path: P,
937 ) -> io::Result<()> {
938 use crate::OpenOptionsFollowExt;
939 use cap_primitives::fs::_WindowsByHandle;
940 use cap_std::fs::{OpenOptions, OpenOptionsExt};
941 use windows_sys::Win32::Storage::FileSystem::{
942 DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
943 FILE_FLAG_OPEN_REPARSE_POINT,
944 };
945 let path = path.as_ref();
946
947 let mut opts = OpenOptions::new();
948 opts.access_mode(DELETE);
949 opts.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
950 opts.follow(FollowSymlinks::No);
951 let file = self.open_with(path, &opts).await?;
952
953 let meta = file.metadata().await?;
954 if meta.file_type().is_symlink()
955 && meta.file_attributes() & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY
956 {
957 self.remove_dir(path).await?;
958 } else {
959 self.remove_file(path).await?;
960 }
961
962 drop(file);
967
968 Ok(())
969 }
970
971 async fn access<P: AsRef<async_std::path::Path> + Send>(
973 &self,
974 path: P,
975 type_: AccessType,
976 ) -> io::Result<()> {
977 let path = path.as_ref().to_path_buf();
978 let clone = self.clone();
979 spawn_blocking(move || {
980 access(
981 &clone.as_filelike_view::<std::fs::File>(),
982 path.as_ref(),
983 type_,
984 FollowSymlinks::Yes,
985 )
986 })
987 .await
988 }
989
990 async fn access_symlink<P: AsRef<async_std::path::Path> + Send>(
993 &self,
994 path: P,
995 type_: AccessType,
996 ) -> io::Result<()> {
997 let path = path.as_ref().to_path_buf();
998 let clone = self.clone();
999 spawn_blocking(move || {
1000 access(
1001 &clone.as_filelike_view::<std::fs::File>(),
1002 path.as_ref(),
1003 type_,
1004 FollowSymlinks::No,
1005 )
1006 })
1007 .await
1008 }
1009
1010 async fn set_symlink_permissions<P: AsRef<async_std::path::Path> + Send>(
1013 &self,
1014 path: P,
1015 perm: Permissions,
1016 ) -> io::Result<()> {
1017 let path = path.as_ref().to_path_buf();
1018 let clone = self.clone();
1019 spawn_blocking(move || {
1020 set_symlink_permissions(
1021 &clone.as_filelike_view::<std::fs::File>(),
1022 path.as_ref(),
1023 perm,
1024 )
1025 })
1026 .await
1027 }
1028}
1029
1030#[cfg(all(feature = "std", feature = "fs_utf8"))]
1031impl DirExtUtf8 for cap_std::fs_utf8::Dir {
1032 #[inline]
1033 fn set_atime<P: AsRef<Utf8Path>>(&self, path: P, atime: SystemTimeSpec) -> io::Result<()> {
1034 let path = from_utf8(path.as_ref())?;
1035 set_times(
1036 &self.as_filelike_view::<std::fs::File>(),
1037 &path,
1038 Some(atime),
1039 None,
1040 )
1041 }
1042
1043 #[inline]
1044 fn set_mtime<P: AsRef<Utf8Path>>(&self, path: P, mtime: SystemTimeSpec) -> io::Result<()> {
1045 let path = from_utf8(path.as_ref())?;
1046 set_times(
1047 &self.as_filelike_view::<std::fs::File>(),
1048 &path,
1049 None,
1050 Some(mtime),
1051 )
1052 }
1053
1054 #[inline]
1055 fn set_times<P: AsRef<Utf8Path>>(
1056 &self,
1057 path: P,
1058 atime: Option<SystemTimeSpec>,
1059 mtime: Option<SystemTimeSpec>,
1060 ) -> io::Result<()> {
1061 let path = from_utf8(path.as_ref())?;
1062 set_times(
1063 &self.as_filelike_view::<std::fs::File>(),
1064 &path,
1065 atime,
1066 mtime,
1067 )
1068 }
1069
1070 #[inline]
1071 fn set_symlink_times<P: AsRef<Utf8Path>>(
1072 &self,
1073 path: P,
1074 atime: Option<SystemTimeSpec>,
1075 mtime: Option<SystemTimeSpec>,
1076 ) -> io::Result<()> {
1077 let path = from_utf8(path.as_ref())?;
1078 set_times_nofollow(
1079 &self.as_filelike_view::<std::fs::File>(),
1080 &path,
1081 atime,
1082 mtime,
1083 )
1084 }
1085
1086 #[cfg(not(windows))]
1087 #[inline]
1088 fn symlink<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(&self, src: P, dst: Q) -> io::Result<()> {
1089 Self::symlink(self, src, dst)
1090 }
1091
1092 #[cfg(not(windows))]
1093 #[inline]
1094 fn symlink_file<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
1095 &self,
1096 src: P,
1097 dst: Q,
1098 ) -> io::Result<()> {
1099 Self::symlink(self, src, dst)
1100 }
1101
1102 #[cfg(not(windows))]
1103 #[inline]
1104 fn symlink_dir<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
1105 &self,
1106 src: P,
1107 dst: Q,
1108 ) -> io::Result<()> {
1109 Self::symlink(self, src, dst)
1110 }
1111
1112 #[cfg(windows)]
1113 #[inline]
1114 fn symlink<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(&self, src: P, dst: Q) -> io::Result<()> {
1115 if self.metadata(src.as_ref())?.is_dir() {
1116 Self::symlink_dir(self, src, dst)
1117 } else {
1118 Self::symlink_file(self, src, dst)
1119 }
1120 }
1121
1122 #[cfg(windows)]
1123 #[inline]
1124 fn symlink_file<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
1125 &self,
1126 src: P,
1127 dst: Q,
1128 ) -> io::Result<()> {
1129 Self::symlink_file(self, src, dst)
1130 }
1131
1132 #[cfg(windows)]
1133 #[inline]
1134 fn symlink_dir<P: AsRef<Utf8Path>, Q: AsRef<Utf8Path>>(
1135 &self,
1136 src: P,
1137 dst: Q,
1138 ) -> io::Result<()> {
1139 Self::symlink_dir(self, src, dst)
1140 }
1141
1142 #[inline]
1143 fn open_dir_nofollow<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<Self> {
1144 match open_dir_nofollow(
1145 &self.as_filelike_view::<std::fs::File>(),
1146 path.as_ref().as_ref(),
1147 ) {
1148 Ok(file) => Ok(Self::from_std_file(file)),
1149 Err(e) => Err(e),
1150 }
1151 }
1152
1153 #[cfg(not(windows))]
1154 #[inline]
1155 fn remove_file_or_symlink<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<()> {
1156 self.remove_file(path.as_ref())
1157 }
1158
1159 #[cfg(windows)]
1160 #[inline]
1161 fn remove_file_or_symlink<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<()> {
1162 use crate::OpenOptionsFollowExt;
1163 use cap_primitives::fs::_WindowsByHandle;
1164 use cap_std::fs::{OpenOptions, OpenOptionsExt};
1165 use windows_sys::Win32::Storage::FileSystem::{
1166 DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
1167 FILE_FLAG_OPEN_REPARSE_POINT,
1168 };
1169 let path = path.as_ref();
1170
1171 let mut opts = OpenOptions::new();
1172 opts.access_mode(DELETE);
1173 opts.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
1174 opts.follow(FollowSymlinks::No);
1175 let file = self.open_with(path, &opts)?;
1176
1177 let meta = file.metadata()?;
1178 if meta.file_type().is_symlink()
1179 && meta.file_attributes() & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY
1180 {
1181 self.remove_dir(path)?;
1182 } else {
1183 self.remove_file(path)?;
1184 }
1185
1186 drop(file);
1191
1192 Ok(())
1193 }
1194
1195 fn access<P: AsRef<Utf8Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
1197 access(
1198 &self.as_filelike_view::<std::fs::File>(),
1199 path.as_ref().as_ref(),
1200 type_,
1201 FollowSymlinks::Yes,
1202 )
1203 }
1204
1205 fn access_symlink<P: AsRef<Utf8Path>>(&self, path: P, type_: AccessType) -> io::Result<()> {
1207 access(
1208 &self.as_filelike_view::<std::fs::File>(),
1209 path.as_ref().as_ref(),
1210 type_,
1211 FollowSymlinks::No,
1212 )
1213 }
1214
1215 fn set_symlink_permissions<P: AsRef<Utf8Path>>(
1218 &self,
1219 path: P,
1220 perm: Permissions,
1221 ) -> io::Result<()> {
1222 set_symlink_permissions(
1223 &self.as_filelike_view::<std::fs::File>(),
1224 path.as_ref().as_ref(),
1225 perm,
1226 )
1227 }
1228}
1229
1230#[cfg(all(feature = "async_std", feature = "fs_utf8"))]
1231#[async_trait]
1232impl AsyncDirExtUtf8 for cap_async_std::fs_utf8::Dir {
1233 #[inline]
1234 async fn set_atime<P: AsRef<Utf8Path> + Send>(
1235 &self,
1236 path: P,
1237 atime: SystemTimeSpec,
1238 ) -> io::Result<()> {
1239 let path = from_utf8(path.as_ref())?;
1240 let clone = self.clone();
1241 spawn_blocking(move || {
1242 set_times(
1243 &clone.as_filelike_view::<std::fs::File>(),
1244 &path,
1245 Some(atime),
1246 None,
1247 )
1248 })
1249 .await
1250 }
1251
1252 #[inline]
1253 async fn set_mtime<P: AsRef<Utf8Path> + Send>(
1254 &self,
1255 path: P,
1256 mtime: SystemTimeSpec,
1257 ) -> io::Result<()> {
1258 let path = from_utf8(path.as_ref())?;
1259 let clone = self.clone();
1260 spawn_blocking(move || {
1261 set_times(
1262 &clone.as_filelike_view::<std::fs::File>(),
1263 &path,
1264 None,
1265 Some(mtime),
1266 )
1267 })
1268 .await
1269 }
1270
1271 #[inline]
1272 async fn set_times<P: AsRef<Utf8Path> + Send>(
1273 &self,
1274 path: P,
1275 atime: Option<SystemTimeSpec>,
1276 mtime: Option<SystemTimeSpec>,
1277 ) -> io::Result<()> {
1278 let path = from_utf8(path.as_ref())?;
1279 let clone = self.clone();
1280 spawn_blocking(move || {
1281 set_times(
1282 &clone.as_filelike_view::<std::fs::File>(),
1283 &path,
1284 atime,
1285 mtime,
1286 )
1287 })
1288 .await
1289 }
1290
1291 #[inline]
1292 async fn set_symlink_times<P: AsRef<Utf8Path> + Send>(
1293 &self,
1294 path: P,
1295 atime: Option<SystemTimeSpec>,
1296 mtime: Option<SystemTimeSpec>,
1297 ) -> io::Result<()> {
1298 let path = from_utf8(path.as_ref())?;
1299 let clone = self.clone();
1300 spawn_blocking(move || {
1301 set_times_nofollow(
1302 &clone.as_filelike_view::<std::fs::File>(),
1303 &path,
1304 atime,
1305 mtime,
1306 )
1307 })
1308 .await
1309 }
1310
1311 #[cfg(not(windows))]
1312 #[inline]
1313 async fn symlink<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
1314 &self,
1315 src: P,
1316 dst: Q,
1317 ) -> io::Result<()> {
1318 let src = from_utf8(src.as_ref())?;
1319 let dst = from_utf8(dst.as_ref())?;
1320 let clone = self.clone();
1321 spawn_blocking(move || symlink(&src, &clone.as_filelike_view::<std::fs::File>(), &dst))
1322 .await
1323 }
1324
1325 #[cfg(not(windows))]
1326 #[inline]
1327 async fn symlink_file<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
1328 &self,
1329 src: P,
1330 dst: Q,
1331 ) -> io::Result<()> {
1332 self.symlink(src, dst).await
1333 }
1334
1335 #[cfg(not(windows))]
1336 #[inline]
1337 async fn symlink_dir<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
1338 &self,
1339 src: P,
1340 dst: Q,
1341 ) -> io::Result<()> {
1342 self.symlink(src, dst).await
1343 }
1344
1345 #[cfg(windows)]
1346 #[inline]
1347 async fn symlink<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
1348 &self,
1349 src: P,
1350 dst: Q,
1351 ) -> io::Result<()> {
1352 let src = from_utf8(src.as_ref())?;
1353 let src_ = src.clone();
1354 let dst = from_utf8(dst.as_ref())?;
1355 let clone = self.clone();
1356 let metadata = spawn_blocking(move || {
1358 stat(
1359 &clone.as_filelike_view::<std::fs::File>(),
1360 &src_,
1361 FollowSymlinks::Yes,
1362 )
1363 })
1364 .await?;
1365 let clone = self.clone();
1366 if metadata.is_dir() {
1367 spawn_blocking(move || {
1368 symlink_dir(&src, &clone.as_filelike_view::<std::fs::File>(), &dst)
1369 })
1370 .await
1371 } else {
1372 spawn_blocking(move || {
1373 symlink_file(&src, &clone.as_filelike_view::<std::fs::File>(), &dst)
1374 })
1375 .await
1376 }
1377 }
1378
1379 #[cfg(windows)]
1380 #[inline]
1381 async fn symlink_file<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
1382 &self,
1383 src: P,
1384 dst: Q,
1385 ) -> io::Result<()> {
1386 let src = from_utf8(src.as_ref())?;
1387 let dst = from_utf8(dst.as_ref())?;
1388 let clone = self.clone();
1389 spawn_blocking(move || symlink_file(&src, &clone.as_filelike_view::<std::fs::File>(), &dst))
1390 .await
1391 }
1392
1393 #[cfg(windows)]
1394 #[inline]
1395 async fn symlink_dir<P: AsRef<Utf8Path> + Send, Q: AsRef<Utf8Path> + Send>(
1396 &self,
1397 src: P,
1398 dst: Q,
1399 ) -> io::Result<()> {
1400 let src = from_utf8(src.as_ref())?;
1401 let dst = from_utf8(dst.as_ref())?;
1402 let clone = self.clone();
1403 spawn_blocking(move || symlink_dir(&src, &clone.as_filelike_view::<std::fs::File>(), &dst))
1404 .await
1405 }
1406
1407 #[inline]
1408 async fn open_dir_nofollow<P: AsRef<Utf8Path> + Send>(&self, path: P) -> io::Result<Self> {
1409 let path = from_utf8(path.as_ref())?;
1410 let clone = self.clone();
1411 spawn_blocking(move || {
1412 match open_dir_nofollow(&clone.as_filelike_view::<std::fs::File>(), path.as_ref()) {
1413 Ok(file) => Ok(Self::from_std_file(file.into())),
1414 Err(e) => Err(e),
1415 }
1416 })
1417 .await
1418 }
1419
1420 #[cfg(not(windows))]
1421 #[inline]
1422 async fn remove_file_or_symlink<P: AsRef<Utf8Path> + Send>(&self, path: P) -> io::Result<()> {
1423 self.remove_file(path).await
1424 }
1425
1426 #[cfg(windows)]
1427 #[inline]
1428 async fn remove_file_or_symlink<P: AsRef<Utf8Path> + Send>(&self, path: P) -> io::Result<()> {
1429 use crate::{FollowSymlinks, OpenOptionsFollowExt};
1430 use cap_primitives::fs::_WindowsByHandle;
1431 use cap_std::fs::{OpenOptions, OpenOptionsExt};
1432 use windows_sys::Win32::Storage::FileSystem::{
1433 DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
1434 FILE_FLAG_OPEN_REPARSE_POINT,
1435 };
1436 let path = path.as_ref();
1437
1438 let mut opts = OpenOptions::new();
1439 opts.access_mode(DELETE);
1440 opts.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS);
1441 opts.follow(FollowSymlinks::No);
1442 let file = self.open_with(path, &opts).await?;
1443
1444 let meta = file.metadata().await?;
1445 if meta.file_type().is_symlink()
1446 && meta.file_attributes() & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY
1447 {
1448 self.remove_dir(path).await?;
1449 } else {
1450 self.remove_file(path).await?;
1451 }
1452
1453 drop(file);
1458
1459 Ok(())
1460 }
1461
1462 async fn access<P: AsRef<Utf8Path> + Send>(
1463 &self,
1464 path: P,
1465 type_: AccessType,
1466 ) -> io::Result<()> {
1467 let path = from_utf8(path.as_ref())?;
1468 let clone = self.clone();
1469 spawn_blocking(move || {
1470 access(
1471 &clone.as_filelike_view::<std::fs::File>(),
1472 path.as_ref(),
1473 type_,
1474 FollowSymlinks::Yes,
1475 )
1476 })
1477 .await
1478 }
1479
1480 async fn access_symlink<P: AsRef<Utf8Path> + Send>(
1481 &self,
1482 path: P,
1483 type_: AccessType,
1484 ) -> io::Result<()> {
1485 let path = from_utf8(path.as_ref())?;
1486 let clone = self.clone();
1487 spawn_blocking(move || {
1488 access(
1489 &clone.as_filelike_view::<std::fs::File>(),
1490 path.as_ref(),
1491 type_,
1492 FollowSymlinks::No,
1493 )
1494 })
1495 .await
1496 }
1497
1498 async fn set_symlink_permissions<P: AsRef<Utf8Path> + Send>(
1501 &self,
1502 path: P,
1503 perm: Permissions,
1504 ) -> io::Result<()> {
1505 let path = from_utf8(path.as_ref())?;
1506 let clone = self.clone();
1507 spawn_blocking(move || {
1508 set_symlink_permissions(
1509 &clone.as_filelike_view::<std::fs::File>(),
1510 path.as_ref(),
1511 perm,
1512 )
1513 })
1514 .await
1515 }
1516}
1517
1518#[cfg(all(any(feature = "std", feature = "async_std"), feature = "fs_utf8"))]
1519#[cfg(not(feature = "arf_strings"))]
1520fn from_utf8<'a>(path: &'a Utf8Path) -> io::Result<&'a std::path::Path> {
1521 Ok(path.as_std_path())
1522}
1523
1524#[cfg(all(any(feature = "std", feature = "async_std"), feature = "fs_utf8"))]
1525#[cfg(feature = "arf_strings")]
1526fn from_utf8<'a>(path: &'a Utf8Path) -> io::Result<std::path::PathBuf> {
1527 #[cfg(not(windows))]
1528 let path = {
1529 #[cfg(unix)]
1530 use std::{ffi::OsString, os::unix::ffi::OsStringExt};
1531 #[cfg(target_os = "wasi")]
1532 use std::{ffi::OsString, os::wasi::ffi::OsStringExt};
1533
1534 let string = arf_strings::str_to_host(path.as_str())?;
1535 OsString::from_vec(string.into_bytes())
1536 };
1537
1538 #[cfg(windows)]
1539 let path = arf_strings::str_to_host(path.as_str())?;
1540
1541 Ok(path.into())
1542}