# Buggy code
def file_hash(load, fnd):
    """
    Return a file hash, the hash type is set in the master config file
    """
    if "env" in load:
        salt.utils.warn_until(
            "Boron",
            "Passing a salt environment should be done using 'saltenv' "
            "not 'env'. This functionality will be removed in Salt Boron.",
        )
        load["saltenv"] = load.pop("env")

    if "path" not in load or "saltenv" not in load:
        return ""
    path = fnd["path"]
    ret = {}

    # if the file doesn't exist, we can't get a hash
    if not path or not os.path.isfile(path):
        return ret

    # set the hash_type as it is determined by config-- so mechanism won't change that
    ret["hash_type"] = __opts__["hash_type"]

    # check if the hash is cached
    # cache file's contents should be "hash:mtime"
    cache_path = os.path.join(
        __opts__["cachedir"],
        "roots/hash",
        load["saltenv"],
        "{0}.hash.{1}".format(fnd["rel"], __opts__["hash_type"]),
    )
    # if we have a cache, serve that if the mtime hasn't changed
    if os.path.exists(cache_path):
        try:
            with salt.utils.fopen(cache_path, "rb") as fp_:
                try:
                    hsum, mtime = fp_.read().split(":")
                except ValueError:
                    log.debug(
                        "Fileserver attempted to read incomplete cache file. Retrying."
                    )
                    # Delete the file since its incomplete (either corrupted or incomplete)
                    try:
                        os.unlink(cache_path)
                    except OSError:
                        pass
                    return file_hash(load, fnd)
                if os.path.getmtime(path) == mtime:
                    # check if mtime changed
                    ret["hsum"] = hsum
                    return ret
        except (
            os.error,
            IOError,
        ):  # Can't use Python select() because we need Windows support
            log.debug("Fileserver encountered lock when reading cache file. Retrying.")
            # Delete the file since its incomplete (either corrupted or incomplete)
            try:
                os.unlink(cache_path)
            except OSError:
                pass
            return file_hash(load, fnd)

    # if we don't have a cache entry-- lets make one
    ret["hsum"] = salt.utils.get_hash(path, __opts__["hash_type"])
    cache_dir = os.path.dirname(cache_path)
    # make cache directory if it doesn't exist
    if not os.path.exists(cache_dir):
        os.makedirs(cache_dir)
    # save the cache object "hash:mtime"
    cache_object = "{0}:{1}".format(ret["hsum"], os.path.getmtime(path))
    with salt.utils.flopen(cache_path, "w") as fp_:
        fp_.write(cache_object)
    return ret


# Traceback (OSError)
2014-12-08 18:42:10,787 [salt.master                                 ][ERROR   ] Error in function _file_hash:
Traceback (most recent call last):
File "/usr/lib/python2.6/site-packages/salt/master.py", line 1687, in run_func
ret = getattr(self, func)(load)
File "/usr/lib/python2.6/site-packages/salt/fileserver/__init__.py", line 354, in file_hash
return self.servers[fstr](load, fnd)
File "/usr/lib/python2.6/site-packages/salt/fileserver/roots.py", line 218, in file_hash
os.makedirs(cache_dir)
File "/usr/lib64/python2.6/os.py", line 157, in makedirs
mkdir(name, mode)
OSError: [Errno 17] File exists: '/var/cache/salt/master/roots/hash/base/common/repos'

# Fixed code
def file_hash(load, fnd):
    """
    Return a file hash, the hash type is set in the master config file
    """
    if "env" in load:
        salt.utils.warn_until(
            "Boron",
            "Passing a salt environment should be done using 'saltenv' "
            "not 'env'. This functionality will be removed in Salt Boron.",
        )
        load["saltenv"] = load.pop("env")

    if "path" not in load or "saltenv" not in load:
        return ""
    path = fnd["path"]
    ret = {}

    # if the file doesn't exist, we can't get a hash
    if not path or not os.path.isfile(path):
        return ret

    # set the hash_type as it is determined by config-- so mechanism won't change that
    ret["hash_type"] = __opts__["hash_type"]

    # check if the hash is cached
    # cache file's contents should be "hash:mtime"
    cache_path = os.path.join(
        __opts__["cachedir"],
        "roots/hash",
        load["saltenv"],
        "{0}.hash.{1}".format(fnd["rel"], __opts__["hash_type"]),
    )
    # if we have a cache, serve that if the mtime hasn't changed
    if os.path.exists(cache_path):
        try:
            with salt.utils.fopen(cache_path, "rb") as fp_:
                try:
                    hsum, mtime = fp_.read().split(":")
                except ValueError:
                    log.debug(
                        "Fileserver attempted to read incomplete cache file. Retrying."
                    )
                    # Delete the file since its incomplete (either corrupted or incomplete)
                    try:
                        os.unlink(cache_path)
                    except OSError:
                        pass
                    return file_hash(load, fnd)
                if os.path.getmtime(path) == mtime:
                    # check if mtime changed
                    ret["hsum"] = hsum
                    return ret
        except (
            os.error,
            IOError,
        ):  # Can't use Python select() because we need Windows support
            log.debug("Fileserver encountered lock when reading cache file. Retrying.")
            # Delete the file since its incomplete (either corrupted or incomplete)
            try:
                os.unlink(cache_path)
            except OSError:
                pass
            return file_hash(load, fnd)

    # if we don't have a cache entry-- lets make one
    ret["hsum"] = salt.utils.get_hash(path, __opts__["hash_type"])
    cache_dir = os.path.dirname(cache_path)
    # make cache directory if it doesn't exist
    if not os.path.exists(cache_dir):
        try:
            os.makedirs(cache_dir)
        except OSError:
            pass
    # save the cache object "hash:mtime"
    cache_object = "{0}:{1}".format(ret["hsum"], os.path.getmtime(path))
    with salt.utils.flopen(cache_path, "w") as fp_:
        fp_.write(cache_object)
    return ret