def should_unmount()

in xar/deprecated/mount_xar.py [0:0]


def should_unmount(devname, mountpath, fstype, timeout):
    if fstype not in (
        "fuse.squashfuse",
        "fuse.squashfuse_ll",
        "osxfusefs",
        "osxfuse",
        "macfuse",
    ):
        return (False, None)

    logging.debug("Considering %s (%s)..." % (mountpath, fstype))
    # Only consider certain prefixes, and strip them off into
    # mount_suffix
    allowed_prefixes = ("/mnt/xarfuse/", "/dev/shm/")
    mount_suffix = None
    for prefix in allowed_prefixes:
        if mountpath.startswith(prefix):
            mount_suffix = mountpath[len(prefix) :]
            logging.debug("Mount suffix: %s" % mount_suffix)
            break

    if mount_suffix is None:
        logger.info("Skipping unmount of %s, incorrect prefix" % mountpath)
        return (False, None)

    # Mounts are of the form /prefix/uid-N/UUID-ns-NSID/... -- we need to
    # extract the UUID portion.
    uuid_regex = re.compile(r"uid-\d+/([^/]+)-ns-([^-/]+)$")
    match = uuid_regex.match(mount_suffix)
    if not match:
        logger.info("Skipping unmount of %s, unexpected path strucure" % mountpath)
        return (False, None)

    # Sometimes mtab gets out of sync with reality; all XARs should
    # contain files, so let's confirm they actually do, and if not,
    # consider them worth unmounting.
    try:
        if len(os.listdir(mountpath)) == 0:
            logger.info("Unmounting empty directory %s", mountpath)
            return (True, None)
    except OSError as oe:
        logger.info("Unable to listdir %s, skipping emptiness check", mountpath)

    # Look for the lockfile for this uuid.
    stat_target = None
    if match:
        stat_target = os.path.join(
            os.path.dirname(mountpath), "lockfile." + match.group(1)
        )

    # Legacy case from when lockfiles lacked the uuid portion.
    if not os.path.exists(stat_target):
        stat_target = os.path.join(os.path.dirname(mountpath), "lockfile")

    logging.debug("Using stat target %s" % stat_target)
    # We have a lockfile; use its mtime to determine if the mount
    # point is old enough to try to reap.
    lock_fd = None
    try:
        O_CLOEXEC = 524288  # not in os prior to 3.3
        lock_fd = os.open(stat_target, os.O_RDWR | O_CLOEXEC)
        # lock the file before checking timestamp to protect against a
        # race with XarexecFuse.
        if not flock_with_timeout(lock_fd, fcntl.LOCK_EX, 60):
            logging.info("Unable to lock %s, skipping..." % stat_target)
            os.close(lock_fd)
            return (False, None)
        st = os.fstat(lock_fd)
    except OSError as oe:
        # Chances are the open itself failed.  In this case, we fail
        # open and unmount.
        if lock_fd is not None:
            os.close(lock_fd)
        if oe.errno == errno.ENOENT:
            logger.info("Unable to open %s, assuming unmount...", stat_target)
            return (True, None)
        raise

    if time.time() - st.st_mtime <= timeout * 60:
        logger.info(
            "Skipping unmount of %s, too recent (%.2fs)"
            % (mountpath, time.time() - st.st_mtime)
        )
        os.close(lock_fd)
        return (False, None)

    # TODO(chip): one day we will need to support permanent mounts
    # somehow (for fbcode runtime, etc).  Hueristic TBD.
    return (True, lock_fd)