Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 3de768c

Browse files
author
Vicent Martí
committed
Merge pull request libgit2#1635 from arrbee/simplify-mkdir
Simplify git_futils_mkdir
2 parents 2e1fa15 + f7e5615 commit 3de768c

File tree

1 file changed

+49
-64
lines changed

1 file changed

+49
-64
lines changed

src/fileops.c

Lines changed: 49 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -277,18 +277,19 @@ int git_futils_mkdir(
277277
mode_t mode,
278278
uint32_t flags)
279279
{
280-
int error = -1, tmp_errno;
280+
int error = -1;
281281
git_buf make_path = GIT_BUF_INIT;
282282
ssize_t root = 0, min_root_len;
283-
char lastch, *tail;
283+
char lastch = '/', *tail;
284+
struct stat st;
284285

285286
/* build path and find "root" where we should start calling mkdir */
286287
if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
287288
return -1;
288289

289290
if (make_path.size == 0) {
290291
giterr_set(GITERR_OS, "Attempt to create empty path");
291-
goto fail;
292+
goto done;
292293
}
293294

294295
/* remove trailing slashes on path */
@@ -305,24 +306,32 @@ int git_futils_mkdir(
305306
if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
306307
git_buf_rtruncate_at_char(&make_path, '/');
307308

309+
/* if nothing left after truncation, then we're done! */
310+
if (!make_path.size) {
311+
error = 0;
312+
goto done;
313+
}
314+
308315
/* if we are not supposed to make the whole path, reset root */
309316
if ((flags & GIT_MKDIR_PATH) == 0)
310317
root = git_buf_rfind(&make_path, '/');
311318

312-
/* clip root to make_path length */
313-
if (root >= (ssize_t)make_path.size)
314-
root = (ssize_t)make_path.size - 1;
315-
if (root < 0)
316-
root = 0;
317-
318-
/* make sure mkdir root is at least after filesystem root */
319+
/* advance root past drive name or network mount prefix */
319320
min_root_len = git_path_root(make_path.ptr);
320321
if (root < min_root_len)
321322
root = min_root_len;
323+
while (make_path.ptr[root] == '/')
324+
++root;
325+
326+
/* clip root to make_path length */
327+
if (root > (ssize_t)make_path.size)
328+
root = (ssize_t)make_path.size; /* i.e. NUL byte of string */
329+
if (root < 0)
330+
root = 0;
322331

323-
tail = & make_path.ptr[root];
332+
/* walk down tail of path making each directory */
333+
for (tail = &make_path.ptr[root]; *tail; *tail = lastch) {
324334

325-
while (*tail) {
326335
/* advance tail to include next path component */
327336
while (*tail == '/')
328337
tail++;
@@ -332,72 +341,48 @@ int git_futils_mkdir(
332341
/* truncate path at next component */
333342
lastch = *tail;
334343
*tail = '\0';
344+
st.st_mode = 0;
335345

336346
/* make directory */
337347
if (p_mkdir(make_path.ptr, mode) < 0) {
338-
int already_exists = 0;
339-
340-
switch (errno) {
341-
case EEXIST:
342-
if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
343-
!git_path_isdir(make_path.ptr)) {
344-
giterr_set(
345-
GITERR_OS, "Existing path is not a directory '%s'",
346-
make_path.ptr);
347-
error = GIT_ENOTFOUND;
348-
goto fail;
349-
}
350-
351-
already_exists = 1;
352-
break;
353-
case ENOSYS:
354-
case EACCES:
355-
/* Possible recoverable errors. These errors could occur
356-
* on some OS if we try to mkdir at a network mount point
357-
* or at the root of a volume. If the path is a dir, just
358-
* treat as EEXIST.
359-
*/
360-
tmp_errno = errno;
361-
362-
if (git_path_isdir(make_path.ptr)) {
363-
already_exists = 1;
364-
break;
365-
}
366-
367-
/* Fall through */
348+
int tmp_errno = errno;
349+
350+
/* ignore error if directory already exists */
351+
if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
368352
errno = tmp_errno;
369-
default:
370-
giterr_set(GITERR_OS, "Failed to make directory '%s'",
371-
make_path.ptr);
372-
goto fail;
353+
giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
354+
goto done;
373355
}
374356

375-
if (already_exists && (flags & GIT_MKDIR_EXCL) != 0) {
376-
giterr_set(GITERR_OS, "Directory already exists '%s'",
377-
make_path.ptr);
357+
/* with exclusive create, existing dir is an error */
358+
if ((flags & GIT_MKDIR_EXCL) != 0) {
359+
giterr_set(GITERR_OS, "Directory already exists '%s'", make_path.ptr);
378360
error = GIT_EEXISTS;
379-
goto fail;
361+
goto done;
380362
}
381363
}
382364

383-
/* chmod if requested */
384-
if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
385-
((flags & GIT_MKDIR_CHMOD) != 0 && lastch == '\0'))
386-
{
387-
if (p_chmod(make_path.ptr, mode) < 0) {
388-
giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
389-
make_path.ptr);
390-
goto fail;
391-
}
365+
/* chmod if requested and necessary */
366+
if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
367+
(lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
368+
st.st_mode != mode &&
369+
(error = p_chmod(make_path.ptr, mode)) < 0) {
370+
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr);
371+
goto done;
392372
}
393-
394-
*tail = lastch;
395373
}
396374

397-
git_buf_free(&make_path);
398-
return 0;
375+
error = 0;
376+
377+
/* check that full path really is a directory if requested & needed */
378+
if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
379+
lastch != '\0' &&
380+
(p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode))) {
381+
giterr_set(GITERR_OS, "Path is not a directory '%s'", make_path.ptr);
382+
error = GIT_ENOTFOUND;
383+
}
399384

400-
fail:
385+
done:
401386
git_buf_free(&make_path);
402387
return error;
403388
}

0 commit comments

Comments
 (0)