@@ -261,6 +261,84 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
261261 this. try_unwrap_io_result ( result)
262262 }
263263
264+ fn stat (
265+ & mut self ,
266+ path_op : OpTy < ' tcx , Tag > ,
267+ buf_op : OpTy < ' tcx , Tag > ,
268+ ) -> InterpResult < ' tcx , i32 > {
269+ let this = self . eval_context_mut ( ) ;
270+
271+ if this. tcx . sess . target . target . target_os . to_lowercase ( ) != "macos" {
272+ throw_unsup_format ! ( "The `stat` shim is only available for `macos` targets." )
273+ }
274+
275+ let path_scalar = this. read_scalar ( path_op) ?. not_undef ( ) ?;
276+ let path: PathBuf = this. read_os_str_from_c_str ( path_scalar) ?. into ( ) ;
277+
278+ let buf = this. deref_operand ( buf_op) ?;
279+
280+ // `stat` always follows symlinks. `lstat` is used to get symlink metadata.
281+ let metadata = match FileMetadata :: new ( this, path, true ) ? {
282+ Some ( metadata) => metadata,
283+ None => return Ok ( -1 ) ,
284+ } ;
285+
286+ // FIXME: use Scalar::to_u16
287+ let mode: u16 = metadata. mode . to_bits ( Size :: from_bits ( 16 ) ) ? as u16 ;
288+
289+ let ( access_sec, access_nsec) = metadata. accessed . unwrap_or ( ( 0 , 0 ) ) ;
290+ let ( created_sec, created_nsec) = metadata. created . unwrap_or ( ( 0 , 0 ) ) ;
291+ let ( modified_sec, modified_nsec) = metadata. modified . unwrap_or ( ( 0 , 0 ) ) ;
292+
293+ let dev_t_layout = this. libc_ty_layout ( "dev_t" ) ?;
294+ let mode_t_layout = this. libc_ty_layout ( "mode_t" ) ?;
295+ let nlink_t_layout = this. libc_ty_layout ( "nlink_t" ) ?;
296+ let ino_t_layout = this. libc_ty_layout ( "ino_t" ) ?;
297+ let uid_t_layout = this. libc_ty_layout ( "uid_t" ) ?;
298+ let gid_t_layout = this. libc_ty_layout ( "gid_t" ) ?;
299+ let time_t_layout = this. libc_ty_layout ( "time_t" ) ?;
300+ let long_layout = this. libc_ty_layout ( "c_long" ) ?;
301+ let off_t_layout = this. libc_ty_layout ( "off_t" ) ?;
302+ let blkcnt_t_layout = this. libc_ty_layout ( "blkcnt_t" ) ?;
303+ let blksize_t_layout = this. libc_ty_layout ( "blksize_t" ) ?;
304+ let uint32_t_layout = this. libc_ty_layout ( "uint32_t" ) ?;
305+
306+ // We need to add 32 bits of padding after `st_rdev` if we are on a 64-bit platform.
307+ let pad_layout = if this. tcx . sess . target . ptr_width == 64 {
308+ uint32_t_layout
309+ } else {
310+ this. layout_of ( this. tcx . mk_unit ( ) ) ?
311+ } ;
312+
313+ let imms = [
314+ immty_from_uint_checked ( 0u128 , dev_t_layout) ?, // st_dev
315+ immty_from_uint_checked ( mode, mode_t_layout) ?, // st_mode
316+ immty_from_uint_checked ( 0u128 , nlink_t_layout) ?, // st_nlink
317+ immty_from_uint_checked ( 0u128 , ino_t_layout) ?, // st_ino
318+ immty_from_uint_checked ( 0u128 , uid_t_layout) ?, // st_uid
319+ immty_from_uint_checked ( 0u128 , gid_t_layout) ?, // st_gid
320+ immty_from_uint_checked ( 0u128 , dev_t_layout) ?, // st_rdev
321+ immty_from_uint_checked ( 0u128 , pad_layout) ?, // padding for 64-bit targets
322+ immty_from_uint_checked ( access_sec, time_t_layout) ?, // st_atime
323+ immty_from_uint_checked ( access_nsec, long_layout) ?, // st_atime_nsec
324+ immty_from_uint_checked ( modified_sec, time_t_layout) ?, // st_mtime
325+ immty_from_uint_checked ( modified_nsec, long_layout) ?, // st_mtime_nsec
326+ immty_from_uint_checked ( 0u128 , time_t_layout) ?, // st_ctime
327+ immty_from_uint_checked ( 0u128 , long_layout) ?, // st_ctime_nsec
328+ immty_from_uint_checked ( created_sec, time_t_layout) ?, // st_birthtime
329+ immty_from_uint_checked ( created_nsec, long_layout) ?, // st_birthtime_nsec
330+ immty_from_uint_checked ( metadata. size , off_t_layout) ?, // st_size
331+ immty_from_uint_checked ( 0u128 , blkcnt_t_layout) ?, // st_blocks
332+ immty_from_uint_checked ( 0u128 , blksize_t_layout) ?, // st_blksize
333+ immty_from_uint_checked ( 0u128 , uint32_t_layout) ?, // st_flags
334+ immty_from_uint_checked ( 0u128 , uint32_t_layout) ?, // st_gen
335+ ] ;
336+
337+ this. write_packed_immediates ( & buf, & imms) ?;
338+
339+ Ok ( 0 )
340+ }
341+
264342 fn statx (
265343 & mut self ,
266344 dirfd_op : OpTy < ' tcx , Tag > , // Should be an `int`
@@ -273,6 +351,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
273351
274352 this. check_no_isolation ( "statx" ) ?;
275353
354+ if this. tcx . sess . target . target . target_os . to_lowercase ( ) != "linux" {
355+ throw_unsup_format ! ( "The `statx` shim is only available for `linux` targets." )
356+ }
357+
276358 let statxbuf_scalar = this. read_scalar ( statxbuf_op) ?. not_undef ( ) ?;
277359 let pathname_scalar = this. read_scalar ( pathname_op) ?. not_undef ( ) ?;
278360
@@ -330,68 +412,46 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
330412
331413 // If the `AT_SYMLINK_NOFOLLOW` flag is set, we query the file's metadata without following
332414 // symbolic links.
333- let metadata = if flags & this. eval_libc ( "AT_SYMLINK_NOFOLLOW" ) ?. to_i32 ( ) ? != 0 {
334- // FIXME: metadata for symlinks need testing.
335- std:: fs:: symlink_metadata ( path)
336- } else {
337- std:: fs:: metadata ( path)
338- } ;
339-
340- let metadata = match metadata {
341- Ok ( metadata) => metadata,
342- Err ( e) => {
343- this. set_last_error_from_io_error ( e) ?;
344- return Ok ( -1 ) ;
345- }
346- } ;
415+ let follow_symlink = flags & this. eval_libc ( "AT_SYMLINK_NOFOLLOW" ) ?. to_i32 ( ) ? == 0 ;
347416
348- let file_type = metadata. file_type ( ) ;
349-
350- let mode_name = if file_type. is_file ( ) {
351- "S_IFREG"
352- } else if file_type. is_dir ( ) {
353- "S_IFDIR"
354- } else {
355- "S_IFLNK"
417+ let metadata = match FileMetadata :: new ( this, path, follow_symlink) ? {
418+ Some ( metadata) => metadata,
419+ None => return Ok ( -1 ) ,
356420 } ;
357421
358422 // The `mode` field specifies the type of the file and the permissions over the file for
359423 // the owner, its group and other users. Given that we can only provide the file type
360424 // without using platform specific methods, we only set the bits corresponding to the file
361425 // type. This should be an `__u16` but `libc` provides its values as `u32`.
362- let mode: u16 = this
363- . eval_libc ( mode_name ) ?
426+ let mode: u16 = metadata
427+ . mode
364428 . to_u32 ( ) ?
365429 . try_into ( )
366- . unwrap_or_else ( |_| bug ! ( "libc contains bad value for `{}` constant" , mode_name) ) ;
367-
368- let size = metadata. len ( ) ;
430+ . unwrap_or_else ( |_| bug ! ( "libc contains bad value for constant" ) ) ;
369431
370- let ( access_sec, access_nsec) = extract_sec_and_nsec (
371- metadata. accessed ( ) ,
372- & mut mask,
373- this. eval_libc ( "STATX_ATIME" ) ?. to_u32 ( ) ?,
374- ) ?;
432+ // We need to set the corresponding bits of `mask` if the access, creation and modification
433+ // times were available. Otherwise we let them be zero.
434+ let ( access_sec, access_nsec) = metadata. accessed . map ( |tup| {
435+ mask |= this. eval_libc ( "STATX_ATIME" ) ?. to_u32 ( ) ?;
436+ InterpResult :: Ok ( tup)
437+ } ) . unwrap_or ( Ok ( ( 0 , 0 ) ) ) ?;
375438
376- let ( created_sec, created_nsec) = extract_sec_and_nsec (
377- metadata. created ( ) ,
378- & mut mask,
379- this. eval_libc ( "STATX_BTIME" ) ?. to_u32 ( ) ?,
380- ) ?;
439+ let ( created_sec, created_nsec) = metadata. created . map ( |tup| {
440+ mask |= this. eval_libc ( "STATX_BTIME" ) ?. to_u32 ( ) ?;
441+ InterpResult :: Ok ( tup)
442+ } ) . unwrap_or ( Ok ( ( 0 , 0 ) ) ) ?;
381443
382- let ( modified_sec, modified_nsec) = extract_sec_and_nsec (
383- metadata. modified ( ) ,
384- & mut mask,
385- this. eval_libc ( "STATX_MTIME" ) ?. to_u32 ( ) ?,
386- ) ?;
444+ let ( modified_sec, modified_nsec) = metadata. modified . map ( |tup| {
445+ mask |= this. eval_libc ( "STATX_MTIME" ) ?. to_u32 ( ) ?;
446+ InterpResult :: Ok ( tup)
447+ } ) . unwrap_or ( Ok ( ( 0 , 0 ) ) ) ?;
387448
388449 let __u32_layout = this. libc_ty_layout ( "__u32" ) ?;
389450 let __u64_layout = this. libc_ty_layout ( "__u64" ) ?;
390451 let __u16_layout = this. libc_ty_layout ( "__u16" ) ?;
391452
392453 // Now we transform all this fields into `ImmTy`s and write them to `statxbuf`. We write a
393454 // zero for the unavailable fields.
394- // FIXME: Provide more fields using platform specific methods.
395455 let imms = [
396456 immty_from_uint_checked ( mask, __u32_layout) ?, // stx_mask
397457 immty_from_uint_checked ( 0u128 , __u32_layout) ?, // stx_blksize
@@ -402,7 +462,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
402462 immty_from_uint_checked ( mode, __u16_layout) ?, // stx_mode
403463 immty_from_uint_checked ( 0u128 , __u16_layout) ?, // statx padding
404464 immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_ino
405- immty_from_uint_checked ( size, __u64_layout) ?, // stx_size
465+ immty_from_uint_checked ( metadata . size , __u64_layout) ?, // stx_size
406466 immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_blocks
407467 immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_attributes
408468 immty_from_uint_checked ( access_sec, __u64_layout) ?, // stx_atime.tv_sec
@@ -440,19 +500,68 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
440500 }
441501}
442502
443- // Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch, and
444- // then sets the `mask` bits determined by `flag` when `time` is Ok. If `time` is an error, it
445- // returns `(0, 0)` without setting any bits .
503+ /// Extracts the number of seconds and nanoseconds elapsed between `time` and the unix epoch when
504+ /// `time` is Ok. Returns `None` if `time` is an error. Fails if `time` happens before the unix
505+ /// epoch .
446506fn extract_sec_and_nsec < ' tcx > (
447- time : std:: io:: Result < SystemTime > ,
448- mask : & mut u32 ,
449- flag : u32 ,
450- ) -> InterpResult < ' tcx , ( u64 , u32 ) > {
451- if let Ok ( time) = time {
507+ time : std:: io:: Result < SystemTime >
508+ ) -> InterpResult < ' tcx , Option < ( u64 , u32 ) > > {
509+ time. ok ( ) . map ( |time| {
452510 let duration = system_time_to_duration ( & time) ?;
453- * mask |= flag;
454511 Ok ( ( duration. as_secs ( ) , duration. subsec_nanos ( ) ) )
455- } else {
456- Ok ( ( 0 , 0 ) )
512+ } ) . transpose ( )
513+ }
514+
515+ /// Stores a file's metadata in order to avoid code duplication in the different metadata related
516+ /// shims.
517+ struct FileMetadata {
518+ mode : Scalar < Tag > ,
519+ size : u64 ,
520+ created : Option < ( u64 , u32 ) > ,
521+ accessed : Option < ( u64 , u32 ) > ,
522+ modified : Option < ( u64 , u32 ) > ,
523+ }
524+
525+ impl FileMetadata {
526+ fn new < ' tcx , ' mir > (
527+ ecx : & mut MiriEvalContext < ' mir , ' tcx > ,
528+ path : PathBuf ,
529+ follow_symlink : bool
530+ ) -> InterpResult < ' tcx , Option < FileMetadata > > {
531+ let metadata = if follow_symlink {
532+ std:: fs:: metadata ( path)
533+ } else {
534+ // FIXME: metadata for symlinks need testing.
535+ std:: fs:: symlink_metadata ( path)
536+ } ;
537+
538+ let metadata = match metadata {
539+ Ok ( metadata) => metadata,
540+ Err ( e) => {
541+ ecx. set_last_error_from_io_error ( e) ?;
542+ return Ok ( None ) ;
543+ }
544+ } ;
545+
546+ let file_type = metadata. file_type ( ) ;
547+
548+ let mode_name = if file_type. is_file ( ) {
549+ "S_IFREG"
550+ } else if file_type. is_dir ( ) {
551+ "S_IFDIR"
552+ } else {
553+ "S_IFLNK"
554+ } ;
555+
556+ let mode = ecx. eval_libc ( mode_name) ?;
557+
558+ let size = metadata. len ( ) ;
559+
560+ let created = extract_sec_and_nsec ( metadata. created ( ) ) ?;
561+ let accessed = extract_sec_and_nsec ( metadata. accessed ( ) ) ?;
562+ let modified = extract_sec_and_nsec ( metadata. modified ( ) ) ?;
563+
564+ // FIXME: Provide more fields using platform specific methods.
565+ Ok ( Some ( FileMetadata { mode, size, created, accessed, modified } ) )
457566 }
458567}
0 commit comments