@@ -660,6 +660,137 @@ fn test_symlinkat() {
660
660
) ;
661
661
}
662
662
663
+ #[ test]
664
+ fn test_linkat_file ( ) {
665
+ let tempdir = tempfile:: tempdir ( ) . unwrap ( ) ;
666
+ let oldfilename = "foo.txt" ;
667
+ let oldfilepath = tempdir. path ( ) . join ( oldfilename) ;
668
+
669
+ let newfilename = "bar.txt" ;
670
+ let newfilepath = tempdir. path ( ) . join ( newfilename) ;
671
+
672
+ // Create file
673
+ File :: create ( & oldfilepath) . unwrap ( ) ;
674
+
675
+ // Get file descriptor for base directory
676
+ let dirfd = fcntl:: open ( tempdir. path ( ) , fcntl:: OFlag :: empty ( ) , stat:: Mode :: empty ( ) ) . unwrap ( ) ;
677
+
678
+ // Attempt hard link file at relative path
679
+ linkat ( Some ( dirfd) , oldfilename, Some ( dirfd) , newfilename, LinkatFlags :: SymlinkFollow ) . unwrap ( ) ;
680
+ assert ! ( newfilepath. exists( ) ) ;
681
+ }
682
+
683
+ #[ test]
684
+ fn test_linkat_olddirfd_none ( ) {
685
+ let tempdir_oldfile = tempfile:: tempdir ( ) . unwrap ( ) ;
686
+ let oldfilename = "foo.txt" ;
687
+ let oldfilepath = tempdir_oldfile. path ( ) . join ( oldfilename) ;
688
+
689
+ let tempdir_newfile = tempfile:: tempdir ( ) . unwrap ( ) ;
690
+ let newfilename = "bar.txt" ;
691
+ let newfilepath = tempdir_newfile. path ( ) . join ( newfilename) ;
692
+
693
+ // Create file
694
+ File :: create ( & oldfilepath) . unwrap ( ) ;
695
+
696
+ // Get file descriptor for base directory of new file
697
+ let dirfd = fcntl:: open ( tempdir_newfile. path ( ) , fcntl:: OFlag :: empty ( ) , stat:: Mode :: empty ( ) ) . unwrap ( ) ;
698
+
699
+ // Attempt hard link file using curent working directory as relative path for old file path
700
+ chdir ( tempdir_oldfile. path ( ) ) . unwrap ( ) ;
701
+ linkat ( None , oldfilename, Some ( dirfd) , newfilename, LinkatFlags :: SymlinkFollow ) . unwrap ( ) ;
702
+ assert ! ( newfilepath. exists( ) ) ;
703
+ }
704
+
705
+ #[ test]
706
+ fn test_linkat_newdirfd_none ( ) {
707
+ let tempdir_oldfile = tempfile:: tempdir ( ) . unwrap ( ) ;
708
+ let oldfilename = "foo.txt" ;
709
+ let oldfilepath = tempdir_oldfile. path ( ) . join ( oldfilename) ;
710
+
711
+ let tempdir_newfile = tempfile:: tempdir ( ) . unwrap ( ) ;
712
+ let newfilename = "bar.txt" ;
713
+ let newfilepath = tempdir_newfile. path ( ) . join ( newfilename) ;
714
+
715
+ // Create file
716
+ File :: create ( & oldfilepath) . unwrap ( ) ;
717
+
718
+ // Get file descriptor for base directory of old file
719
+ let dirfd = fcntl:: open ( tempdir_oldfile. path ( ) , fcntl:: OFlag :: empty ( ) , stat:: Mode :: empty ( ) ) . unwrap ( ) ;
720
+
721
+ // Attempt hard link file using current working directory as relative path for new file path
722
+ chdir ( tempdir_newfile. path ( ) ) . unwrap ( ) ;
723
+ linkat ( Some ( dirfd) , oldfilename, None , newfilename, LinkatFlags :: SymlinkFollow ) . unwrap ( ) ;
724
+ assert ! ( newfilepath. exists( ) ) ;
725
+ }
726
+
727
+ #[ test]
728
+ #[ cfg( not( any( target_os = "ios" , target_os = "macos" ) ) ) ]
729
+ fn test_linkat_no_follow_symlink ( ) {
730
+ let tempdir = tempfile:: tempdir ( ) . unwrap ( ) ;
731
+ let oldfilename = "foo.txt" ;
732
+ let oldfilepath = tempdir. path ( ) . join ( oldfilename) ;
733
+
734
+ let symoldfilename = "symfoo.txt" ;
735
+ let symoldfilepath = tempdir. path ( ) . join ( symoldfilename) ;
736
+
737
+ let newfilename = "nofollowsymbar.txt" ;
738
+ let newfilepath = tempdir. path ( ) . join ( newfilename) ;
739
+
740
+ // Create file
741
+ File :: create ( & oldfilepath) . unwrap ( ) ;
742
+
743
+ // Create symlink to file
744
+ symlinkat ( & oldfilepath, None , & symoldfilepath) . unwrap ( ) ;
745
+
746
+ // Get file descriptor for base directory
747
+ let dirfd = fcntl:: open ( tempdir. path ( ) , fcntl:: OFlag :: empty ( ) , stat:: Mode :: empty ( ) ) . unwrap ( ) ;
748
+
749
+ // Attempt link symlink of file at relative path
750
+ linkat ( Some ( dirfd) , symoldfilename, Some ( dirfd) , newfilename, LinkatFlags :: NoSymlinkFollow ) . unwrap ( ) ;
751
+
752
+ // Assert newfile is actually a symlink to oldfile.
753
+ assert_eq ! (
754
+ readlink( & newfilepath)
755
+ . unwrap( )
756
+ . to_str( )
757
+ . unwrap( ) ,
758
+ oldfilepath. to_str( ) . unwrap( )
759
+ ) ;
760
+ }
761
+
762
+ #[ test]
763
+ fn test_linkat_follow_symlink ( ) {
764
+ let tempdir = tempfile:: tempdir ( ) . unwrap ( ) ;
765
+ let oldfilename = "foo.txt" ;
766
+ let oldfilepath = tempdir. path ( ) . join ( oldfilename) ;
767
+
768
+ let symoldfilename = "symfoo.txt" ;
769
+ let symoldfilepath = tempdir. path ( ) . join ( symoldfilename) ;
770
+
771
+ let newfilename = "nofollowsymbar.txt" ;
772
+ let newfilepath = tempdir. path ( ) . join ( newfilename) ;
773
+
774
+ // Create file
775
+ File :: create ( & oldfilepath) . unwrap ( ) ;
776
+
777
+ // Create symlink to file
778
+ symlinkat ( & oldfilepath, None , & symoldfilepath) . unwrap ( ) ;
779
+
780
+ // Get file descriptor for base directory
781
+ let dirfd = fcntl:: open ( tempdir. path ( ) , fcntl:: OFlag :: empty ( ) , stat:: Mode :: empty ( ) ) . unwrap ( ) ;
782
+
783
+ // Attempt link target of symlink of file at relative path
784
+ linkat ( Some ( dirfd) , symoldfilename, Some ( dirfd) , newfilename, LinkatFlags :: SymlinkFollow ) . unwrap ( ) ;
785
+
786
+ let newfilestat = stat:: stat ( & newfilepath) . unwrap ( ) ;
787
+
788
+ // Check the file type of the new link
789
+ assert ! ( ( stat:: SFlag :: from_bits_truncate( newfilestat. st_mode) & SFlag :: S_IFMT ) == SFlag :: S_IFREG ) ;
790
+
791
+ // Check the number of hard links to the original file
792
+ assert_eq ! ( newfilestat. st_nlink, 2 ) ;
793
+ }
663
794
664
795
#[ test]
665
796
fn test_unlinkat_dir_noremovedir ( ) {
0 commit comments