@@ -150,7 +150,12 @@ impl DirEntry {
150
150
nFileSizeHigh : self . data . nFileSizeHigh ,
151
151
nFileSizeLow : self . data . nFileSizeLow ,
152
152
} ,
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
+ } ,
154
159
} )
155
160
}
156
161
}
@@ -240,15 +245,6 @@ impl OpenOptions {
240
245
}
241
246
242
247
impl 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
-
252
248
pub fn open ( path : & Path , opts : & OpenOptions ) -> io:: Result < File > {
253
249
let path = try!( to_u16s ( path) ) ;
254
250
let handle = unsafe {
@@ -371,19 +367,34 @@ impl File {
371
367
fn readlink ( & self ) -> io:: Result < PathBuf > {
372
368
let mut space = [ 0u8 ; c:: MAXIMUM_REPARSE_DATA_BUFFER_SIZE ] ;
373
369
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
-
378
370
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
+ } ;
383
391
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
+ }
387
398
Ok ( PathBuf :: from ( OsString :: from_wide ( subst) ) )
388
399
}
389
400
}
@@ -577,8 +588,15 @@ fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
577
588
rmdir ( path)
578
589
}
579
590
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) ) ;
582
600
file. readlink ( )
583
601
}
584
602
@@ -605,42 +623,23 @@ pub fn link(src: &Path, dst: &Path) -> io::Result<()> {
605
623
Ok ( ( ) )
606
624
}
607
625
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 ( )
627
634
}
628
635
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 ( )
644
643
}
645
644
646
645
pub 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
709
708
fn symlink_junction_inner ( target : & Path , junction : & Path ) -> io:: Result < ( ) > {
710
709
let d = DirBuilder :: new ( ) ;
711
710
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) ) ;
713
717
let h = f. handle ( ) . raw ( ) ;
714
718
715
719
unsafe {
0 commit comments