Skip to content

Commit d176259

Browse files
committed
deduplicate shared code between stat and statx
1 parent 515c119 commit d176259

File tree

1 file changed

+92
-81
lines changed

1 file changed

+92
-81
lines changed

src/shims/fs.rs

Lines changed: 92 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)