Merge pull request #1635 from arrbee/simplify-mkdir

Simplify git_futils_mkdir
This commit is contained in:
Vicent Martí 2013-06-06 17:07:29 -07:00
commit 3de768ca2f

View File

@ -277,10 +277,11 @@ int git_futils_mkdir(
mode_t mode, mode_t mode,
uint32_t flags) uint32_t flags)
{ {
int error = -1, tmp_errno; int error = -1;
git_buf make_path = GIT_BUF_INIT; git_buf make_path = GIT_BUF_INIT;
ssize_t root = 0, min_root_len; ssize_t root = 0, min_root_len;
char lastch, *tail; char lastch = '/', *tail;
struct stat st;
/* build path and find "root" where we should start calling mkdir */ /* build path and find "root" where we should start calling mkdir */
if (git_path_join_unrooted(&make_path, path, base, &root) < 0) if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
@ -288,7 +289,7 @@ int git_futils_mkdir(
if (make_path.size == 0) { if (make_path.size == 0) {
giterr_set(GITERR_OS, "Attempt to create empty path"); giterr_set(GITERR_OS, "Attempt to create empty path");
goto fail; goto done;
} }
/* remove trailing slashes on path */ /* remove trailing slashes on path */
@ -305,24 +306,32 @@ int git_futils_mkdir(
if ((flags & GIT_MKDIR_SKIP_LAST) != 0) if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
git_buf_rtruncate_at_char(&make_path, '/'); git_buf_rtruncate_at_char(&make_path, '/');
/* if nothing left after truncation, then we're done! */
if (!make_path.size) {
error = 0;
goto done;
}
/* if we are not supposed to make the whole path, reset root */ /* if we are not supposed to make the whole path, reset root */
if ((flags & GIT_MKDIR_PATH) == 0) if ((flags & GIT_MKDIR_PATH) == 0)
root = git_buf_rfind(&make_path, '/'); root = git_buf_rfind(&make_path, '/');
/* clip root to make_path length */ /* advance root past drive name or network mount prefix */
if (root >= (ssize_t)make_path.size)
root = (ssize_t)make_path.size - 1;
if (root < 0)
root = 0;
/* make sure mkdir root is at least after filesystem root */
min_root_len = git_path_root(make_path.ptr); min_root_len = git_path_root(make_path.ptr);
if (root < min_root_len) if (root < min_root_len)
root = min_root_len; root = min_root_len;
while (make_path.ptr[root] == '/')
++root;
tail = & make_path.ptr[root]; /* clip root to make_path length */
if (root > (ssize_t)make_path.size)
root = (ssize_t)make_path.size; /* i.e. NUL byte of string */
if (root < 0)
root = 0;
/* walk down tail of path making each directory */
for (tail = &make_path.ptr[root]; *tail; *tail = lastch) {
while (*tail) {
/* advance tail to include next path component */ /* advance tail to include next path component */
while (*tail == '/') while (*tail == '/')
tail++; tail++;
@ -332,72 +341,48 @@ int git_futils_mkdir(
/* truncate path at next component */ /* truncate path at next component */
lastch = *tail; lastch = *tail;
*tail = '\0'; *tail = '\0';
st.st_mode = 0;
/* make directory */ /* make directory */
if (p_mkdir(make_path.ptr, mode) < 0) { if (p_mkdir(make_path.ptr, mode) < 0) {
int already_exists = 0; int tmp_errno = errno;
switch (errno) { /* ignore error if directory already exists */
case EEXIST: if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
!git_path_isdir(make_path.ptr)) {
giterr_set(
GITERR_OS, "Existing path is not a directory '%s'",
make_path.ptr);
error = GIT_ENOTFOUND;
goto fail;
}
already_exists = 1;
break;
case ENOSYS:
case EACCES:
/* Possible recoverable errors. These errors could occur
* on some OS if we try to mkdir at a network mount point
* or at the root of a volume. If the path is a dir, just
* treat as EEXIST.
*/
tmp_errno = errno;
if (git_path_isdir(make_path.ptr)) {
already_exists = 1;
break;
}
/* Fall through */
errno = tmp_errno; errno = tmp_errno;
default: giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
giterr_set(GITERR_OS, "Failed to make directory '%s'", goto done;
make_path.ptr);
goto fail;
} }
if (already_exists && (flags & GIT_MKDIR_EXCL) != 0) { /* with exclusive create, existing dir is an error */
giterr_set(GITERR_OS, "Directory already exists '%s'", if ((flags & GIT_MKDIR_EXCL) != 0) {
make_path.ptr); giterr_set(GITERR_OS, "Directory already exists '%s'", make_path.ptr);
error = GIT_EEXISTS; error = GIT_EEXISTS;
goto fail; goto done;
} }
} }
/* chmod if requested */ /* chmod if requested and necessary */
if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 || if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
((flags & GIT_MKDIR_CHMOD) != 0 && lastch == '\0')) (lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
{ st.st_mode != mode &&
if (p_chmod(make_path.ptr, mode) < 0) { (error = p_chmod(make_path.ptr, mode)) < 0) {
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr);
make_path.ptr); goto done;
goto fail;
}
} }
*tail = lastch;
} }
git_buf_free(&make_path); error = 0;
return 0;
fail: /* check that full path really is a directory if requested & needed */
if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
lastch != '\0' &&
(p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode))) {
giterr_set(GITERR_OS, "Path is not a directory '%s'", make_path.ptr);
error = GIT_ENOTFOUND;
}
done:
git_buf_free(&make_path); git_buf_free(&make_path);
return error; return error;
} }