@@ -150,7 +150,12 @@ impl DirEntry {
150150 nFileSizeHigh : self . data . nFileSizeHigh ,
151151 nFileSizeLow : self . data . nFileSizeLow ,
152152 } ,
153- reparse_tag : self . data . dwReserved0 ,
153+ reparse_tag : if self . data . dwFileAttributes & c:: FILE_ATTRIBUTE_REPARSE_POINT != 0 {
154+ // reserved unless this is a reparse point
155+ self . data . dwReserved0
156+ } else {
157+ 0
158+ } ,
154159 } )
155160 }
156161}
@@ -240,15 +245,6 @@ impl OpenOptions {
240245}
241246
242247impl File {
243- fn open_reparse_point ( path : & Path , write : bool ) -> io:: Result < File > {
244- let mut opts = OpenOptions :: new ( ) ;
245- opts. read ( !write) ;
246- opts. write ( write) ;
247- opts. custom_flags ( c:: FILE_FLAG_OPEN_REPARSE_POINT |
248- c:: FILE_FLAG_BACKUP_SEMANTICS ) ;
249- File :: open ( path, & opts)
250- }
251-
252248 pub fn open ( path : & Path , opts : & OpenOptions ) -> io:: Result < File > {
253249 let path = try!( to_u16s ( path) ) ;
254250 let handle = unsafe {
@@ -371,19 +367,34 @@ impl File {
371367 fn readlink ( & self ) -> io:: Result < PathBuf > {
372368 let mut space = [ 0u8 ; c:: MAXIMUM_REPARSE_DATA_BUFFER_SIZE ] ;
373369 let ( _bytes, buf) = try!( self . reparse_point ( & mut space) ) ;
374- if buf. ReparseTag != c:: IO_REPARSE_TAG_SYMLINK {
375- return Err ( io:: Error :: new ( io:: ErrorKind :: Other , "not a symlink" ) )
376- }
377-
378370 unsafe {
379- let info: * const c:: SYMBOLIC_LINK_REPARSE_BUFFER =
380- & buf. rest as * const _ as * const _ ;
381- let path_buffer = & ( * info) . PathBuffer as * const _ as * const u16 ;
382- let subst_off = ( * info) . SubstituteNameOffset / 2 ;
371+ let ( path_buffer, subst_off, subst_len, relative) = match buf. ReparseTag {
372+ c:: IO_REPARSE_TAG_SYMLINK => {
373+ let info: * const c:: SYMBOLIC_LINK_REPARSE_BUFFER =
374+ & buf. rest as * const _ as * const _ ;
375+ ( & ( * info) . PathBuffer as * const _ as * const u16 ,
376+ ( * info) . SubstituteNameOffset / 2 ,
377+ ( * info) . SubstituteNameLength / 2 ,
378+ ( * info) . Flags & c:: SYMLINK_FLAG_RELATIVE != 0 )
379+ } ,
380+ c:: IO_REPARSE_TAG_MOUNT_POINT => {
381+ let info: * const c:: MOUNT_POINT_REPARSE_BUFFER =
382+ & buf. rest as * const _ as * const _ ;
383+ ( & ( * info) . PathBuffer as * const _ as * const u16 ,
384+ ( * info) . SubstituteNameOffset / 2 ,
385+ ( * info) . SubstituteNameLength / 2 ,
386+ false )
387+ } ,
388+ _ => return Err ( io:: Error :: new ( io:: ErrorKind :: Other ,
389+ "Unsupported reparse point type" ) )
390+ } ;
383391 let subst_ptr = path_buffer. offset ( subst_off as isize ) ;
384- let subst_len = ( * info) . SubstituteNameLength / 2 ;
385- let subst = slice:: from_raw_parts ( subst_ptr, subst_len as usize ) ;
386-
392+ let mut subst = slice:: from_raw_parts ( subst_ptr, subst_len as usize ) ;
393+ // Absolute paths start with an NT internal namespace prefix `\??\`
394+ // We should not let it leak through.
395+ if !relative && subst. starts_with ( & [ 92u16 , 63u16 , 63u16 , 92u16 ] ) {
396+ subst = & subst[ 4 ..] ;
397+ }
387398 Ok ( PathBuf :: from ( OsString :: from_wide ( subst) ) )
388399 }
389400 }
@@ -577,8 +588,15 @@ fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
577588 rmdir ( path)
578589}
579590
580- pub fn readlink ( p : & Path ) -> io:: Result < PathBuf > {
581- let file = try!( File :: open_reparse_point ( p, false ) ) ;
591+ pub fn readlink ( path : & Path ) -> io:: Result < PathBuf > {
592+ // Open the link with no access mode, instead of generic read.
593+ // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so
594+ // this is needed for a common case.
595+ let mut opts = OpenOptions :: new ( ) ;
596+ opts. access_mode ( 0 ) ;
597+ opts. custom_flags ( c:: FILE_FLAG_OPEN_REPARSE_POINT |
598+ c:: FILE_FLAG_BACKUP_SEMANTICS ) ;
599+ let file = try!( File :: open ( & path, & opts) ) ;
582600 file. readlink ( )
583601}
584602
@@ -605,42 +623,23 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
605623 Ok ( ( ) )
606624}
607625
608- pub fn stat ( p : & Path ) -> io:: Result < FileAttr > {
609- let attr = try!( lstat ( p) ) ;
610-
611- // If this is a reparse point, then we need to reopen the file to get the
612- // actual destination. We also pass the FILE_FLAG_BACKUP_SEMANTICS flag to
613- // ensure that we can open directories (this path may be a directory
614- // junction). Once the file is opened we ask the opened handle what its
615- // metadata information is.
616- if attr. is_reparse_point ( ) {
617- let mut opts = OpenOptions :: new ( ) ;
618- // No read or write permissions are necessary
619- opts. access_mode ( 0 ) ;
620- // This flag is so we can open directories too
621- opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS ) ;
622- let file = try!( File :: open ( p, & opts) ) ;
623- file. file_attr ( )
624- } else {
625- Ok ( attr)
626- }
626+ pub fn stat ( path : & Path ) -> io:: Result < FileAttr > {
627+ let mut opts = OpenOptions :: new ( ) ;
628+ // No read or write permissions are necessary
629+ opts. access_mode ( 0 ) ;
630+ // This flag is so we can open directories too
631+ opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS ) ;
632+ let file = try!( File :: open ( path, & opts) ) ;
633+ file. file_attr ( )
627634}
628635
629- pub fn lstat ( p : & Path ) -> io:: Result < FileAttr > {
630- let u16s = try!( to_u16s ( p) ) ;
631- unsafe {
632- let mut attr: FileAttr = mem:: zeroed ( ) ;
633- try!( cvt ( c:: GetFileAttributesExW ( u16s. as_ptr ( ) ,
634- c:: GetFileExInfoStandard ,
635- & mut attr. data as * mut _ as * mut _ ) ) ) ;
636- if attr. is_reparse_point ( ) {
637- attr. reparse_tag = File :: open_reparse_point ( p, false ) . and_then ( |f| {
638- let mut b = [ 0 ; c:: MAXIMUM_REPARSE_DATA_BUFFER_SIZE ] ;
639- f. reparse_point ( & mut b) . map ( |( _, b) | b. ReparseTag )
640- } ) . unwrap_or ( 0 ) ;
641- }
642- Ok ( attr)
643- }
636+ pub fn lstat ( path : & Path ) -> io:: Result < FileAttr > {
637+ let mut opts = OpenOptions :: new ( ) ;
638+ // No read or write permissions are necessary
639+ opts. access_mode ( 0 ) ;
640+ opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS | c:: FILE_FLAG_OPEN_REPARSE_POINT ) ;
641+ let file = try!( File :: open ( path, & opts) ) ;
642+ file. file_attr ( )
644643}
645644
646645pub fn set_perm ( p : & Path , perm : FilePermissions ) -> io:: Result < ( ) > {
@@ -709,7 +708,12 @@ pub fn symlink_junction<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::R
709708fn symlink_junction_inner ( target : & Path , junction : & Path ) -> io:: Result < ( ) > {
710709 let d = DirBuilder :: new ( ) ;
711710 try!( d. mkdir ( & junction) ) ;
712- let f = try!( File :: open_reparse_point ( junction, true ) ) ;
711+
712+ let mut opts = OpenOptions :: new ( ) ;
713+ opts. write ( true ) ;
714+ opts. custom_flags ( c:: FILE_FLAG_OPEN_REPARSE_POINT |
715+ c:: FILE_FLAG_BACKUP_SEMANTICS ) ;
716+ let f = try!( File :: open ( junction, & opts) ) ;
713717 let h = f. handle ( ) . raw ( ) ;
714718
715719 unsafe {
0 commit comments