@@ -261,6 +261,84 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
261
261
this. try_unwrap_io_result ( result)
262
262
}
263
263
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
+
264
342
fn statx (
265
343
& mut self ,
266
344
dirfd_op : OpTy < ' tcx , Tag > , // Should be an `int`
@@ -273,6 +351,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
273
351
274
352
this. check_no_isolation ( "statx" ) ?;
275
353
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
+
276
358
let statxbuf_scalar = this. read_scalar ( statxbuf_op) ?. not_undef ( ) ?;
277
359
let pathname_scalar = this. read_scalar ( pathname_op) ?. not_undef ( ) ?;
278
360
@@ -330,68 +412,46 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
330
412
331
413
// If the `AT_SYMLINK_NOFOLLOW` flag is set, we query the file's metadata without following
332
414
// 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 ;
347
416
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 ) ,
356
420
} ;
357
421
358
422
// The `mode` field specifies the type of the file and the permissions over the file for
359
423
// the owner, its group and other users. Given that we can only provide the file type
360
424
// without using platform specific methods, we only set the bits corresponding to the file
361
425
// 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
364
428
. to_u32 ( ) ?
365
429
. 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" ) ) ;
369
431
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 ) ) ) ?;
375
438
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 ) ) ) ?;
381
443
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 ) ) ) ?;
387
448
388
449
let __u32_layout = this. libc_ty_layout ( "__u32" ) ?;
389
450
let __u64_layout = this. libc_ty_layout ( "__u64" ) ?;
390
451
let __u16_layout = this. libc_ty_layout ( "__u16" ) ?;
391
452
392
453
// Now we transform all this fields into `ImmTy`s and write them to `statxbuf`. We write a
393
454
// zero for the unavailable fields.
394
- // FIXME: Provide more fields using platform specific methods.
395
455
let imms = [
396
456
immty_from_uint_checked ( mask, __u32_layout) ?, // stx_mask
397
457
immty_from_uint_checked ( 0u128 , __u32_layout) ?, // stx_blksize
@@ -402,7 +462,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
402
462
immty_from_uint_checked ( mode, __u16_layout) ?, // stx_mode
403
463
immty_from_uint_checked ( 0u128 , __u16_layout) ?, // statx padding
404
464
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
406
466
immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_blocks
407
467
immty_from_uint_checked ( 0u128 , __u64_layout) ?, // stx_attributes
408
468
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
440
500
}
441
501
}
442
502
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 .
446
506
fn 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| {
452
510
let duration = system_time_to_duration ( & time) ?;
453
- * mask |= flag;
454
511
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 } ) )
457
566
}
458
567
}
0 commit comments