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