зеркало из https://github.com/microsoft/git.git
refs file backend: move raceproof_create_file() here
Move the raceproof_create_file() API added to cache.h and
object-file.c in 177978f56a
(raceproof_create_file(): new function,
2017-01-06) to its only user, refs/files-backend.c.
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Родитель
48cdcd9ca0
Коммит
3fa2e91d17
43
cache.h
43
cache.h
|
@ -1202,49 +1202,6 @@ enum scld_error safe_create_leading_directories(char *path);
|
|||
enum scld_error safe_create_leading_directories_const(const char *path);
|
||||
enum scld_error safe_create_leading_directories_no_share(char *path);
|
||||
|
||||
/*
|
||||
* Callback function for raceproof_create_file(). This function is
|
||||
* expected to do something that makes dirname(path) permanent despite
|
||||
* the fact that other processes might be cleaning up empty
|
||||
* directories at the same time. Usually it will create a file named
|
||||
* path, but alternatively it could create another file in that
|
||||
* directory, or even chdir() into that directory. The function should
|
||||
* return 0 if the action was completed successfully. On error, it
|
||||
* should return a nonzero result and set errno.
|
||||
* raceproof_create_file() treats two errno values specially:
|
||||
*
|
||||
* - ENOENT -- dirname(path) does not exist. In this case,
|
||||
* raceproof_create_file() tries creating dirname(path)
|
||||
* (and any parent directories, if necessary) and calls
|
||||
* the function again.
|
||||
*
|
||||
* - EISDIR -- the file already exists and is a directory. In this
|
||||
* case, raceproof_create_file() removes the directory if
|
||||
* it is empty (and recursively any empty directories that
|
||||
* it contains) and calls the function again.
|
||||
*
|
||||
* Any other errno causes raceproof_create_file() to fail with the
|
||||
* callback's return value and errno.
|
||||
*
|
||||
* Obviously, this function should be OK with being called again if it
|
||||
* fails with ENOENT or EISDIR. In other scenarios it will not be
|
||||
* called again.
|
||||
*/
|
||||
typedef int create_file_fn(const char *path, void *cb);
|
||||
|
||||
/*
|
||||
* Create a file in dirname(path) by calling fn, creating leading
|
||||
* directories if necessary. Retry a few times in case we are racing
|
||||
* with another process that is trying to clean up the directory that
|
||||
* contains path. See the documentation for create_file_fn for more
|
||||
* details.
|
||||
*
|
||||
* Return the value and set the errno that resulted from the most
|
||||
* recent call of fn. fn is always called at least once, and will be
|
||||
* called more than once if it returns ENOENT or EISDIR.
|
||||
*/
|
||||
int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
|
||||
|
||||
int mkdir_in_gitdir(const char *path);
|
||||
char *expand_user_path(const char *path, int real_home);
|
||||
const char *enter_repo(const char *path, int strict);
|
||||
|
|
|
@ -414,74 +414,6 @@ enum scld_error safe_create_leading_directories_const(const char *path)
|
|||
return result;
|
||||
}
|
||||
|
||||
int raceproof_create_file(const char *path, create_file_fn fn, void *cb)
|
||||
{
|
||||
/*
|
||||
* The number of times we will try to remove empty directories
|
||||
* in the way of path. This is only 1 because if another
|
||||
* process is racily creating directories that conflict with
|
||||
* us, we don't want to fight against them.
|
||||
*/
|
||||
int remove_directories_remaining = 1;
|
||||
|
||||
/*
|
||||
* The number of times that we will try to create the
|
||||
* directories containing path. We are willing to attempt this
|
||||
* more than once, because another process could be trying to
|
||||
* clean up empty directories at the same time as we are
|
||||
* trying to create them.
|
||||
*/
|
||||
int create_directories_remaining = 3;
|
||||
|
||||
/* A scratch copy of path, filled lazily if we need it: */
|
||||
struct strbuf path_copy = STRBUF_INIT;
|
||||
|
||||
int ret, save_errno;
|
||||
|
||||
/* Sanity check: */
|
||||
assert(*path);
|
||||
|
||||
retry_fn:
|
||||
ret = fn(path, cb);
|
||||
save_errno = errno;
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
if (errno == EISDIR && remove_directories_remaining-- > 0) {
|
||||
/*
|
||||
* A directory is in the way. Maybe it is empty; try
|
||||
* to remove it:
|
||||
*/
|
||||
if (!path_copy.len)
|
||||
strbuf_addstr(&path_copy, path);
|
||||
|
||||
if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY))
|
||||
goto retry_fn;
|
||||
} else if (errno == ENOENT && create_directories_remaining-- > 0) {
|
||||
/*
|
||||
* Maybe the containing directory didn't exist, or
|
||||
* maybe it was just deleted by a process that is
|
||||
* racing with us to clean up empty directories. Try
|
||||
* to create it:
|
||||
*/
|
||||
enum scld_error scld_result;
|
||||
|
||||
if (!path_copy.len)
|
||||
strbuf_addstr(&path_copy, path);
|
||||
|
||||
do {
|
||||
scld_result = safe_create_leading_directories(path_copy.buf);
|
||||
if (scld_result == SCLD_OK)
|
||||
goto retry_fn;
|
||||
} while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0);
|
||||
}
|
||||
|
||||
out:
|
||||
strbuf_release(&path_copy);
|
||||
errno = save_errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fill_loose_path(struct strbuf *buf, const struct object_id *oid)
|
||||
{
|
||||
int i;
|
||||
|
|
|
@ -852,6 +852,115 @@ static struct ref_iterator *files_ref_iterator_begin(
|
|||
return ref_iterator;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback function for raceproof_create_file(). This function is
|
||||
* expected to do something that makes dirname(path) permanent despite
|
||||
* the fact that other processes might be cleaning up empty
|
||||
* directories at the same time. Usually it will create a file named
|
||||
* path, but alternatively it could create another file in that
|
||||
* directory, or even chdir() into that directory. The function should
|
||||
* return 0 if the action was completed successfully. On error, it
|
||||
* should return a nonzero result and set errno.
|
||||
* raceproof_create_file() treats two errno values specially:
|
||||
*
|
||||
* - ENOENT -- dirname(path) does not exist. In this case,
|
||||
* raceproof_create_file() tries creating dirname(path)
|
||||
* (and any parent directories, if necessary) and calls
|
||||
* the function again.
|
||||
*
|
||||
* - EISDIR -- the file already exists and is a directory. In this
|
||||
* case, raceproof_create_file() removes the directory if
|
||||
* it is empty (and recursively any empty directories that
|
||||
* it contains) and calls the function again.
|
||||
*
|
||||
* Any other errno causes raceproof_create_file() to fail with the
|
||||
* callback's return value and errno.
|
||||
*
|
||||
* Obviously, this function should be OK with being called again if it
|
||||
* fails with ENOENT or EISDIR. In other scenarios it will not be
|
||||
* called again.
|
||||
*/
|
||||
typedef int create_file_fn(const char *path, void *cb);
|
||||
|
||||
/*
|
||||
* Create a file in dirname(path) by calling fn, creating leading
|
||||
* directories if necessary. Retry a few times in case we are racing
|
||||
* with another process that is trying to clean up the directory that
|
||||
* contains path. See the documentation for create_file_fn for more
|
||||
* details.
|
||||
*
|
||||
* Return the value and set the errno that resulted from the most
|
||||
* recent call of fn. fn is always called at least once, and will be
|
||||
* called more than once if it returns ENOENT or EISDIR.
|
||||
*/
|
||||
static int raceproof_create_file(const char *path, create_file_fn fn, void *cb)
|
||||
{
|
||||
/*
|
||||
* The number of times we will try to remove empty directories
|
||||
* in the way of path. This is only 1 because if another
|
||||
* process is racily creating directories that conflict with
|
||||
* us, we don't want to fight against them.
|
||||
*/
|
||||
int remove_directories_remaining = 1;
|
||||
|
||||
/*
|
||||
* The number of times that we will try to create the
|
||||
* directories containing path. We are willing to attempt this
|
||||
* more than once, because another process could be trying to
|
||||
* clean up empty directories at the same time as we are
|
||||
* trying to create them.
|
||||
*/
|
||||
int create_directories_remaining = 3;
|
||||
|
||||
/* A scratch copy of path, filled lazily if we need it: */
|
||||
struct strbuf path_copy = STRBUF_INIT;
|
||||
|
||||
int ret, save_errno;
|
||||
|
||||
/* Sanity check: */
|
||||
assert(*path);
|
||||
|
||||
retry_fn:
|
||||
ret = fn(path, cb);
|
||||
save_errno = errno;
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
if (errno == EISDIR && remove_directories_remaining-- > 0) {
|
||||
/*
|
||||
* A directory is in the way. Maybe it is empty; try
|
||||
* to remove it:
|
||||
*/
|
||||
if (!path_copy.len)
|
||||
strbuf_addstr(&path_copy, path);
|
||||
|
||||
if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY))
|
||||
goto retry_fn;
|
||||
} else if (errno == ENOENT && create_directories_remaining-- > 0) {
|
||||
/*
|
||||
* Maybe the containing directory didn't exist, or
|
||||
* maybe it was just deleted by a process that is
|
||||
* racing with us to clean up empty directories. Try
|
||||
* to create it:
|
||||
*/
|
||||
enum scld_error scld_result;
|
||||
|
||||
if (!path_copy.len)
|
||||
strbuf_addstr(&path_copy, path);
|
||||
|
||||
do {
|
||||
scld_result = safe_create_leading_directories(path_copy.buf);
|
||||
if (scld_result == SCLD_OK)
|
||||
goto retry_fn;
|
||||
} while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0);
|
||||
}
|
||||
|
||||
out:
|
||||
strbuf_release(&path_copy);
|
||||
errno = save_errno;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int remove_empty_directories(struct strbuf *path)
|
||||
{
|
||||
/*
|
||||
|
|
Загрузка…
Ссылка в новой задаче