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

Skip to content

Commit f7e5615

Browse files
committed
Make mkdir early exit cases clearer
There are two places where git_futils_mkdir should exit early or at least do less. The first is when using GIT_MKDIR_SKIP_LAST and having that flag leave no directory left to create; it was being handled previously, but the behavior was subtle. Now I put in a clear explicit check that exits early in that case. The second is when there is no directory to create, but there is a valid path that should be verified. I shifted the logic a bit so we'll be better about not entering the loop than that happens.
1 parent 999d440 commit f7e5615

File tree

1 file changed

+30
-35
lines changed

1 file changed

+30
-35
lines changed

src/fileops.c

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,10 @@ int git_futils_mkdir(
277277
mode_t mode,
278278
uint32_t flags)
279279
{
280-
int error = -1, verify_final;
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;
284284
struct stat st;
285285

286286
/* build path and find "root" where we should start calling mkdir */
@@ -306,27 +306,32 @@ int git_futils_mkdir(
306306
if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
307307
git_buf_rtruncate_at_char(&make_path, '/');
308308

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

319+
/* advance root past drive name or network mount prefix */
320+
min_root_len = git_path_root(make_path.ptr);
321+
if (root < min_root_len)
322+
root = min_root_len;
323+
while (make_path.ptr[root] == '/')
324+
++root;
325+
313326
/* clip root to make_path length */
314-
if (root >= (ssize_t)make_path.size)
315-
root = (ssize_t)make_path.size - 1;
327+
if (root > (ssize_t)make_path.size)
328+
root = (ssize_t)make_path.size; /* i.e. NUL byte of string */
316329
if (root < 0)
317330
root = 0;
318331

319-
tail = &make_path.ptr[root];
320-
321-
/* is there any path to make? */
322-
verify_final = ((flags & GIT_MKDIR_VERIFY_DIR) != 0) && (*tail != 0);
332+
/* walk down tail of path making each directory */
333+
for (tail = &make_path.ptr[root]; *tail; *tail = lastch) {
323334

324-
/* make sure mkdir root is at least after filesystem root */
325-
min_root_len = git_path_root(make_path.ptr);
326-
if (root < min_root_len)
327-
tail = &make_path.ptr[min_root_len];
328-
329-
while (*tail) {
330335
/* advance tail to include next path component */
331336
while (*tail == '/')
332337
tail++;
@@ -345,45 +350,35 @@ int git_futils_mkdir(
345350
/* ignore error if directory already exists */
346351
if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
347352
errno = tmp_errno;
348-
giterr_set(GITERR_OS, "Failed to make directory '%s'",
349-
make_path.ptr);
353+
giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
350354
goto done;
351355
}
352356

353357
/* with exclusive create, existing dir is an error */
354358
if ((flags & GIT_MKDIR_EXCL) != 0) {
355-
giterr_set(GITERR_OS, "Directory already exists '%s'",
356-
make_path.ptr);
359+
giterr_set(GITERR_OS, "Directory already exists '%s'", make_path.ptr);
357360
error = GIT_EEXISTS;
358361
goto done;
359362
}
360363
}
361364

362365
/* chmod if requested and necessary */
363-
if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
364-
(lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0))
365-
{
366-
if (st.st_mode != mode && p_chmod(make_path.ptr, mode) < 0) {
367-
giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
368-
make_path.ptr);
369-
goto done;
370-
}
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;
371372
}
372-
373-
/* if we made it to the end, then final isdir check is not needed */
374-
if (lastch == '\0')
375-
verify_final = false;
376-
377-
*tail = lastch;
378373
}
379374

380375
error = 0;
381376

382377
/* check that full path really is a directory if requested & needed */
383-
if (verify_final &&
378+
if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
379+
lastch != '\0' &&
384380
(p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode))) {
385-
giterr_set(
386-
GITERR_OS, "Path is not a directory '%s'", make_path.ptr);
381+
giterr_set(GITERR_OS, "Path is not a directory '%s'", make_path.ptr);
387382
error = GIT_ENOTFOUND;
388383
}
389384

0 commit comments

Comments
 (0)