@@ -269,40 +269,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
269269 let this = self . eval_context_mut ( ) ;
270270
271271 if this. tcx . sess . target . target . target_os . to_lowercase ( ) != "macos" {
272- throw_unsup_format ! ( "The `stat` shim is only only available in the `macos` platform ." )
272+ throw_unsup_format ! ( "The `stat` shim is only only available for `macos` targets ." )
273273 }
274274
275275 let path_scalar = this. read_scalar ( path_op) ?. not_undef ( ) ?;
276- let path = this. read_os_str_from_c_str ( path_scalar) ?;
276+ let path: PathBuf = this. read_os_str_from_c_str ( path_scalar) ?. into ( ) ;
277277
278278 let buf = this. deref_operand ( buf_op) ?;
279279
280- let metadata = match std:: fs:: metadata ( path) {
281- Ok ( metadata) => metadata,
282- Err ( e) => {
283- this. set_last_error_from_io_error ( e) ?;
284- return Ok ( -1 ) ;
285- }
286- } ;
287-
288- let file_type = metadata. file_type ( ) ;
289-
290- let mode_name = if file_type. is_file ( ) {
291- "S_IFREG"
292- } else if file_type. is_dir ( ) {
293- "S_IFDIR"
294- } else {
295- "S_IFLNK"
280+ let stats = match FileStatus :: new ( this, path, false ) ? {
281+ Some ( stats) => stats,
282+ None => return Ok ( -1 ) ,
296283 } ;
297284
298285 // FIXME: use Scalar::to_u16
299- let mode = this. eval_libc ( mode_name) ?. to_bits ( Size :: from_bits ( 16 ) ) ? as u16 ;
300-
301- let size = metadata. len ( ) ;
286+ let mode: u16 = stats. mode . to_bits ( Size :: from_bits ( 16 ) ) ? as u16 ;
302287
303- let ( access_sec, access_nsec) = extract_sec_and_nsec ( metadata . accessed ( ) , & mut 0 , 0 ) ? ;
304- let ( created_sec, created_nsec) = extract_sec_and_nsec ( metadata . created ( ) , & mut 0 , 0 ) ? ;
305- let ( modified_sec, modified_nsec) = extract_sec_and_nsec ( metadata . modified ( ) , & mut 0 , 0 ) ? ;
288+ let ( access_sec, access_nsec) = stats . accessed . unwrap_or ( ( 0 , 0 ) ) ;
289+ let ( created_sec, created_nsec) = stats . created . unwrap_or ( ( 0 , 0 ) ) ;
290+ let ( modified_sec, modified_nsec) = stats . modified . unwrap_or ( ( 0 , 0 ) ) ;
306291
307292 let dev_t_layout = this. libc_ty_layout ( "dev_t" ) ?;
308293 let mode_t_layout = this. libc_ty_layout ( "mode_t" ) ?;
@@ -341,7 +326,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
341326 immty_from_uint_checked ( 0u128 , long_layout) ?, // st_ctime_nsec
342327 immty_from_uint_checked ( created_sec, time_t_layout) ?, // st_birthtime
343328 immty_from_uint_checked ( created_nsec, long_layout) ?, // st_birthtime_nsec
344- immty_from_uint_checked ( size, off_t_layout) ?, // st_size
329+ immty_from_uint_checked ( stats . size , off_t_layout) ?, // st_size
345330 immty_from_uint_checked ( 0u128 , blkcnt_t_layout) ?, // st_blocks
346331 immty_from_uint_checked ( 0u128 , blksize_t_layout) ?, // st_blksize
347332 immty_from_uint_checked ( 0u128 , uint32_t_layout) ?, // st_flags
@@ -365,6 +350,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
365350
366351 this. check_no_isolation ( "statx" ) ?;
367352
353+ if this. tcx . sess . target . target . target_os . to_lowercase ( ) != "linux" {
354+ throw_unsup_format ! ( "The `statx` shim is only only available for `linux` targets." )
355+ }
356+
368357 let statxbuf_scalar = this. read_scalar ( statxbuf_op) ?. not_undef ( ) ?;
369358 let pathname_scalar = this. read_scalar ( pathname_op) ?. not_undef ( ) ?;
370359
@@ -422,68 +411,50 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
422411
423412 // If the `AT_SYMLINK_NOFOLLOW` flag is set, we query the file's metadata without following
424413 // symbolic links.
425- let metadata = if flags & this. eval_libc ( "AT_SYMLINK_NOFOLLOW" ) ?. to_i32 ( ) ? != 0 {
426- // FIXME: metadata for symlinks need testing.
427- std:: fs:: symlink_metadata ( path)
428- } else {
429- std:: fs:: metadata ( path)
430- } ;
431-
432- let metadata = match metadata {
433- Ok ( metadata) => metadata,
434- Err ( e) => {
435- this. set_last_error_from_io_error ( e) ?;
436- return Ok ( -1 ) ;
437- }
438- } ;
439-
440- let file_type = metadata. file_type ( ) ;
414+ let is_symlink = flags & this. eval_libc ( "AT_SYMLINK_NOFOLLOW" ) ?. to_i32 ( ) ? != 0 ;
441415
442- let mode_name = if file_type. is_file ( ) {
443- "S_IFREG"
444- } else if file_type. is_dir ( ) {
445- "S_IFDIR"
446- } else {
447- "S_IFLNK"
416+ let stats = match FileStatus :: new ( this, path, is_symlink) ? {
417+ Some ( stats) => stats,
418+ None => return Ok ( -1 ) ,
448419 } ;
449420
450421 // The `mode` field specifies the type of the file and the permissions over the file for
451422 // the owner, its group and other users. Given that we can only provide the file type
452423 // without using platform specific methods, we only set the bits corresponding to the file
453424 // type. This should be an `__u16` but `libc` provides its values as `u32`.
454- let mode: u16 = this
455- . eval_libc ( mode_name ) ?
425+ let mode: u16 = stats
426+ . mode
456427 . to_u32 ( ) ?
457428 . try_into ( )
458- . unwrap_or_else ( |_| bug ! ( "libc contains bad value for `{}` constant" , mode_name) ) ;
459-
460- let size = metadata. len ( ) ;
429+ . unwrap_or_else ( |_| bug ! ( "libc contains bad value for constant" ) ) ;
461430
462- let ( access_sec, access_nsec) = extract_sec_and_nsec (
463- metadata. accessed ( ) ,
464- & mut mask,
465- this. eval_libc ( "STATX_ATIME" ) ?. to_u32 ( ) ?,
466- ) ?;
431+ let ( access_sec, access_nsec) = if let Some ( tup) = stats. accessed {
432+ tup
433+ } else {
434+ mask |= this. eval_libc ( "STATX_ATIME" ) ?. to_u32 ( ) ?;
435+ ( 0 , 0 )
436+ } ;
467437
468- let ( created_sec, created_nsec) = extract_sec_and_nsec (
469- metadata. created ( ) ,
470- & mut mask,
471- this. eval_libc ( "STATX_BTIME" ) ?. to_u32 ( ) ?,
472- ) ?;
438+ let ( created_sec, created_nsec) = if let Some ( tup) = stats. created {
439+ tup
440+ } else {
441+ mask |= this. eval_libc ( "STATX_BTIME" ) ?. to_u32 ( ) ?;
442+ ( 0 , 0 )
443+ } ;
473444
474- let ( modified_sec, modified_nsec) = extract_sec_and_nsec (
475- metadata. modified ( ) ,
476- & mut mask,
477- this. eval_libc ( "STATX_MTIME" ) ?. to_u32 ( ) ?,
478- ) ?;
445+ let ( modified_sec, modified_nsec) = if let Some ( tup) = stats. modified {
446+ tup
447+ } else {
448+ mask |= this. eval_libc ( "STATX_MTIME" ) ?. to_u32 ( ) ?;
449+ ( 0 , 0 )
450+ } ;
479451
480452 let __u32_layout = this. libc_ty_layout ( "__u32" ) ?;
481453 let __u64_layout = this. libc_ty_layout ( "__u64" ) ?;
482454 let __u16_layout = this. libc_ty_layout ( "__u16" ) ?;
483455
484456 // Now we transform all this fields into `ImmTy`s and write them to `statxbuf`. We write a
485457 // zero for the unavailable fields.
486- // FIXME: Provide more fields using platform specific methods.
487458 let imms = [
488459 immty_from_uint_checked ( mask, __u32_layout) ?, // stx_mask
489460 immty_from_uint_checked ( 0u128 , __u32_layout) ?, // stx_blksize
@@ -494,7 +465,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
494465 immty_from_uint_checked ( mode, __u16_layout) ?, // stx_mode
495466 immty_from_uint_checked ( 0u128 , __u16_layout) ?, // statx padding
496467 immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_ino
497- immty_from_uint_checked ( size, __u64_layout) ?, // stx_size
468+ immty_from_uint_checked ( stats . size , __u64_layout) ?, // stx_size
498469 immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_blocks
499470 immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_attributes
500471 immty_from_uint_checked ( access_sec, __u64_layout) ?, // stx_atime.tv_sec
@@ -532,19 +503,59 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
532503 }
533504}
534505
535- // Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch, and
536- // then sets the `mask` bits determined by `flag` when `time` is Ok. If `time` is an error, it
537- // returns `(0, 0)` without setting any bits.
538- fn extract_sec_and_nsec < ' tcx > (
539- time : std:: io:: Result < SystemTime > ,
540- mask : & mut u32 ,
541- flag : u32 ,
542- ) -> InterpResult < ' tcx , ( u64 , u32 ) > {
543- if let Ok ( time) = time {
506+ // Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when
507+ // `time` is Ok. If `time` is an error, it returns `None`.
508+ fn extract_sec_and_nsec < ' tcx > ( time : std:: io:: Result < SystemTime > ) -> InterpResult < ' tcx , Option < ( u64 , u32 ) > > {
509+ time. ok ( ) . map ( |time| {
544510 let duration = system_time_to_duration ( & time) ?;
545- * mask |= flag;
546511 Ok ( ( duration. as_secs ( ) , duration. subsec_nanos ( ) ) )
547- } else {
548- Ok ( ( 0 , 0 ) )
512+ } ) . transpose ( )
513+ }
514+
515+ struct FileStatus {
516+ mode : Scalar < Tag > ,
517+ size : u64 ,
518+ created : Option < ( u64 , u32 ) > ,
519+ accessed : Option < ( u64 , u32 ) > ,
520+ modified : Option < ( u64 , u32 ) > ,
521+ }
522+
523+ impl FileStatus {
524+ fn new < ' tcx , ' mir > ( ecx : & mut MiriEvalContext < ' mir , ' tcx > , path : PathBuf , is_symlink : bool ) -> InterpResult < ' tcx , Option < FileStatus > > {
525+ let metadata = if is_symlink {
526+ // FIXME: metadata for symlinks need testing.
527+ std:: fs:: symlink_metadata ( path)
528+ } else {
529+ std:: fs:: metadata ( path)
530+ } ;
531+
532+ let metadata = match metadata {
533+ Ok ( metadata) => metadata,
534+ Err ( e) => {
535+ ecx. set_last_error_from_io_error ( e) ?;
536+ return Ok ( None ) ;
537+ }
538+ } ;
539+
540+ let file_type = metadata. file_type ( ) ;
541+
542+ let mode_name = if file_type. is_file ( ) {
543+ "S_IFREG"
544+ } else if file_type. is_dir ( ) {
545+ "S_IFDIR"
546+ } else {
547+ "S_IFLNK"
548+ } ;
549+
550+ let mode = ecx. eval_libc ( mode_name) ?;
551+
552+ let size = metadata. len ( ) ;
553+
554+ let created = extract_sec_and_nsec ( metadata. created ( ) ) ?;
555+ let accessed = extract_sec_and_nsec ( metadata. accessed ( ) ) ?;
556+ let modified = extract_sec_and_nsec ( metadata. modified ( ) ) ?;
557+
558+ // FIXME: Provide more fields using platform specific methods.
559+ Ok ( Some ( FileStatus { mode, size, created, accessed, modified } ) )
549560 }
550561}
0 commit comments