fn should_unmount()

in xar/clean_xar_mounts/src/main.rs [463:549]


fn should_unmount(
    logger: &slog::Logger,
    mount: &MountedFilesystem,
    timeout: u32,
) -> Result<ShouldUnmountResult> {
    // Only consider certain mount types.
    match mount.fstype.as_str() {
        "fuse.squashfuse" | "fuse.squashfuse_ll" | "osxfusefs" | "osxfuse" | "macfuse" => {}
        _ => return Ok(ShouldUnmountResult::new(false, None)),
    }
    info!(
        logger,
        "Considering {} ({})", mount.mountpoint, mount.fstype
    );

    let lockfiles = get_lockfile_path(&logger, &mount);

    // Sometimes mtab gets out of sync with reality; all XARs should
    // contain files, so let's confirm they actually do, and if not,
    // still consider them for unmounting.
    let mut chrooted_mountpoint = mount.chroot.clone();
    chrooted_mountpoint.push(&mount.mountpoint[1..]);
    if let Ok(it) = fs::read_dir(&chrooted_mountpoint) {
        if it.take_while(|r| r.is_ok()).next().is_none() {
            debug!(
                logger,
                "Unmounting empty directory: {:?}", chrooted_mountpoint
            );
            return Ok(ShouldUnmountResult::new(true, None));
        }
    } else {
        info!(
            logger,
            "Unable to read dir {:?}, skipping emptiness check", chrooted_mountpoint
        );
    }

    debug!(logger, "lockfile candidates: {:?}", lockfiles);

    // Find the lockfile; use its mtime to determine if the mount
    // point is old enough to try to reap.
    let lock_opt = lockfiles
        .iter()
        .map(|ref filename| {
            nix::fcntl::open(
                filename.as_path().as_os_str(),
                nix::fcntl::OFlag::O_RDWR | nix::fcntl::OFlag::O_CLOEXEC,
                nix::sys::stat::Mode::from_bits(0700).unwrap(),
            )
        })
        .filter_map(|open_result| open_result.ok())
        .next();
    let lock_fd = match lock_opt {
        Some(fd) => fd,
        None => {
            debug!(
                logger,
                "Unable to find lock file for {}, skipping...", mount.mountpoint
            );
            return Ok(ShouldUnmountResult::new(false, None));
        }
    };

    // lock the file before checking timestamp to protect against a
    // race with XarexecFuse.
    if !flock_with_timeout(lock_fd, 60) {
        info!(
            logger,
            "Unable to flock {:?}, skipping...", chrooted_mountpoint
        );
        return Ok(ShouldUnmountResult::new(false, lock_opt));
    }
    let stat = nix::sys::stat::fstat(lock_fd)?;
    let epoch_now = SystemTime::now().duration_since(UNIX_EPOCH)?;
    let age = epoch_now.as_secs_f64()
        - Duration::new(stat.st_mtime as u64, stat.st_mtime_nsec as u32).as_secs_f64();
    let timeout = timeout as f64 * 60.0;
    if age <= timeout {
        info!(
            logger,
            "Skipping unmount of {}, too recent ({:.2}s)", mount.mountpoint, age
        );
        return Ok(ShouldUnmountResult::new(false, lock_opt));
    }

    Ok(ShouldUnmountResult::new(true, lock_opt))
}