@@ -277,18 +277,19 @@ int git_futils_mkdir(
277
277
mode_t mode ,
278
278
uint32_t flags )
279
279
{
280
- int error = -1 , tmp_errno ;
280
+ int error = -1 ;
281
281
git_buf make_path = GIT_BUF_INIT ;
282
282
ssize_t root = 0 , min_root_len ;
283
- char lastch , * tail ;
283
+ char lastch = '/' , * tail ;
284
+ struct stat st ;
284
285
285
286
/* build path and find "root" where we should start calling mkdir */
286
287
if (git_path_join_unrooted (& make_path , path , base , & root ) < 0 )
287
288
return -1 ;
288
289
289
290
if (make_path .size == 0 ) {
290
291
giterr_set (GITERR_OS , "Attempt to create empty path" );
291
- goto fail ;
292
+ goto done ;
292
293
}
293
294
294
295
/* remove trailing slashes on path */
@@ -305,24 +306,32 @@ int git_futils_mkdir(
305
306
if ((flags & GIT_MKDIR_SKIP_LAST ) != 0 )
306
307
git_buf_rtruncate_at_char (& make_path , '/' );
307
308
309
+ /* if nothing left after truncation, then we're done! */
310
+ if (!make_path .size ) {
311
+ error = 0 ;
312
+ goto done ;
313
+ }
314
+
308
315
/* if we are not supposed to make the whole path, reset root */
309
316
if ((flags & GIT_MKDIR_PATH ) == 0 )
310
317
root = git_buf_rfind (& make_path , '/' );
311
318
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 */
319
320
min_root_len = git_path_root (make_path .ptr );
320
321
if (root < min_root_len )
321
322
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 ;
322
331
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 ) {
324
334
325
- while (* tail ) {
326
335
/* advance tail to include next path component */
327
336
while (* tail == '/' )
328
337
tail ++ ;
@@ -332,72 +341,48 @@ int git_futils_mkdir(
332
341
/* truncate path at next component */
333
342
lastch = * tail ;
334
343
* tail = '\0' ;
344
+ st .st_mode = 0 ;
335
345
336
346
/* make directory */
337
347
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 )) {
368
352
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 ;
373
355
}
374
356
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 );
378
360
error = GIT_EEXISTS ;
379
- goto fail ;
361
+ goto done ;
380
362
}
381
363
}
382
364
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 ;
392
372
}
393
-
394
- * tail = lastch ;
395
373
}
396
374
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
+ }
399
384
400
- fail :
385
+ done :
401
386
git_buf_free (& make_path );
402
387
return error ;
403
388
}
0 commit comments