Merge branch 'mh/safe-create-leading-directories'
Code clean-up and protection against concurrent write access to the ref namespace. * mh/safe-create-leading-directories: rename_tmp_log(): on SCLD_VANISHED, retry rename_tmp_log(): limit the number of remote_empty_directories() attempts rename_tmp_log(): handle a possible mkdir/rmdir race rename_ref(): extract function rename_tmp_log() remove_dir_recurse(): handle disappearing files and directories remove_dir_recurse(): tighten condition for removing unreadable dir lock_ref_sha1_basic(): if locking fails with ENOENT, retry lock_ref_sha1_basic(): on SCLD_VANISHED, retry safe_create_leading_directories(): add new error value SCLD_VANISHED cmd_init_db(): when creating directories, handle errors conservatively safe_create_leading_directories(): introduce enum for return values safe_create_leading_directories(): always restore slash at end of loop safe_create_leading_directories(): split on first of multiple slashes safe_create_leading_directories(): rename local variable safe_create_leading_directories(): add explicit "slash" pointer safe_create_leading_directories(): reduce scope of local variable safe_create_leading_directories(): fix format of "if" chaining
This commit is contained in:
67
sha1_file.c
67
sha1_file.c
@ -105,50 +105,59 @@ int mkdir_in_gitdir(const char *path)
|
||||
return adjust_shared_perm(path);
|
||||
}
|
||||
|
||||
int safe_create_leading_directories(char *path)
|
||||
enum scld_error safe_create_leading_directories(char *path)
|
||||
{
|
||||
char *pos = path + offset_1st_component(path);
|
||||
struct stat st;
|
||||
char *next_component = path + offset_1st_component(path);
|
||||
enum scld_error ret = SCLD_OK;
|
||||
|
||||
while (pos) {
|
||||
pos = strchr(pos, '/');
|
||||
if (!pos)
|
||||
while (ret == SCLD_OK && next_component) {
|
||||
struct stat st;
|
||||
char *slash = strchr(next_component, '/');
|
||||
|
||||
if (!slash)
|
||||
break;
|
||||
while (*++pos == '/')
|
||||
;
|
||||
if (!*pos)
|
||||
|
||||
next_component = slash + 1;
|
||||
while (*next_component == '/')
|
||||
next_component++;
|
||||
if (!*next_component)
|
||||
break;
|
||||
*--pos = '\0';
|
||||
|
||||
*slash = '\0';
|
||||
if (!stat(path, &st)) {
|
||||
/* path exists */
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
*pos = '/';
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
else if (mkdir(path, 0777)) {
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
ret = SCLD_EXISTS;
|
||||
} else if (mkdir(path, 0777)) {
|
||||
if (errno == EEXIST &&
|
||||
!stat(path, &st) && S_ISDIR(st.st_mode)) {
|
||||
!stat(path, &st) && S_ISDIR(st.st_mode))
|
||||
; /* somebody created it since we checked */
|
||||
} else {
|
||||
*pos = '/';
|
||||
return -1;
|
||||
}
|
||||
else if (errno == ENOENT)
|
||||
/*
|
||||
* Either mkdir() failed because
|
||||
* somebody just pruned the containing
|
||||
* directory, or stat() failed because
|
||||
* the file that was in our way was
|
||||
* just removed. Either way, inform
|
||||
* the caller that it might be worth
|
||||
* trying again:
|
||||
*/
|
||||
ret = SCLD_VANISHED;
|
||||
else
|
||||
ret = SCLD_FAILED;
|
||||
} else if (adjust_shared_perm(path)) {
|
||||
ret = SCLD_PERMS;
|
||||
}
|
||||
else if (adjust_shared_perm(path)) {
|
||||
*pos = '/';
|
||||
return -2;
|
||||
}
|
||||
*pos++ = '/';
|
||||
*slash = '/';
|
||||
}
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int safe_create_leading_directories_const(const char *path)
|
||||
enum scld_error safe_create_leading_directories_const(const char *path)
|
||||
{
|
||||
/* path points to cache entries, so xstrdup before messing with it */
|
||||
char *buf = xstrdup(path);
|
||||
int result = safe_create_leading_directories(buf);
|
||||
enum scld_error result = safe_create_leading_directories(buf);
|
||||
free(buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user