@@ -272,6 +272,10 @@ int git_futils_mkdir(
272272 }
273273
274274 /* if we are not supposed to made the last element, truncate it */
275+ if ((flags & GIT_MKDIR_SKIP_LAST2 ) != 0 ) {
276+ git_buf_rtruncate_at_char (& make_path , '/' );
277+ flags |= GIT_MKDIR_SKIP_LAST ;
278+ }
275279 if ((flags & GIT_MKDIR_SKIP_LAST ) != 0 )
276280 git_buf_rtruncate_at_char (& make_path , '/' );
277281
@@ -303,34 +307,34 @@ int git_futils_mkdir(
303307 int already_exists = 0 ;
304308
305309 switch (errno ) {
306- case EEXIST :
307- if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR ) != 0 &&
308- !git_path_isdir (make_path .ptr )) {
309- giterr_set (
310- GITERR_OS , "Existing path is not a directory '%s'" ,
311- make_path .ptr );
312- error = GIT_ENOTFOUND ;
313- goto fail ;
314- }
315-
316- already_exists = 1 ;
317- break ;
318- case ENOSYS :
319- /* Solaris can generate this error if you try to mkdir
320- * a path which is already a mount point. In that case,
321- * the path does already exist; but it's not implied by
322- * the definition of the error, so let's recheck */
323- if (git_path_isdir (make_path .ptr )) {
324- already_exists = 1 ;
325- break ;
326- }
327-
328- /* Fall through */
329- errno = ENOSYS ;
330- default :
331- giterr_set (GITERR_OS , "Failed to make directory '%s'" ,
310+ case EEXIST :
311+ if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR ) != 0 &&
312+ !git_path_isdir (make_path .ptr )) {
313+ giterr_set (
314+ GITERR_OS , "Existing path is not a directory '%s'" ,
332315 make_path .ptr );
316+ error = GIT_ENOTFOUND ;
333317 goto fail ;
318+ }
319+
320+ already_exists = 1 ;
321+ break ;
322+ case ENOSYS :
323+ /* Solaris can generate this error if you try to mkdir
324+ * a path which is already a mount point. In that case,
325+ * the path does already exist; but it's not implied by
326+ * the definition of the error, so let's recheck */
327+ if (git_path_isdir (make_path .ptr )) {
328+ already_exists = 1 ;
329+ break ;
330+ }
331+
332+ /* Fall through */
333+ errno = ENOSYS ;
334+ default :
335+ giterr_set (GITERR_OS , "Failed to make directory '%s'" ,
336+ make_path .ptr );
337+ goto fail ;
334338 }
335339
336340 if (already_exists && (flags & GIT_MKDIR_EXCL ) != 0 ) {
@@ -714,8 +718,33 @@ typedef struct {
714718 mode_t dirmode ;
715719} cp_r_info ;
716720
721+ #define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10)
722+
723+ static int _cp_r_mkdir (cp_r_info * info , git_buf * from )
724+ {
725+ int error = 0 ;
726+
727+ /* create root directory the first time we need to create a directory */
728+ if ((info -> flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT ) == 0 ) {
729+ error = git_futils_mkdir (
730+ info -> to_root , NULL , info -> dirmode ,
731+ (info -> flags & GIT_CPDIR_CHMOD_DIRS ) ? GIT_MKDIR_CHMOD : 0 );
732+
733+ info -> flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT ;
734+ }
735+
736+ /* create directory with root as base to prevent excess chmods */
737+ if (!error )
738+ error = git_futils_mkdir (
739+ from -> ptr + info -> from_prefix , info -> to_root ,
740+ info -> dirmode , info -> mkdir_flags );
741+
742+ return error ;
743+ }
744+
717745static int _cp_r_callback (void * ref , git_buf * from )
718746{
747+ int error = 0 ;
719748 cp_r_info * info = ref ;
720749 struct stat from_st , to_st ;
721750 bool exists = false;
@@ -737,24 +766,22 @@ static int _cp_r_callback(void *ref, git_buf *from)
737766 } else
738767 exists = true;
739768
740- if (git_path_lstat (from -> ptr , & from_st ) < 0 )
741- return -1 ;
769+ if (( error = git_path_lstat (from -> ptr , & from_st ) ) < 0 )
770+ return error ;
742771
743772 if (S_ISDIR (from_st .st_mode )) {
744- int error = 0 ;
745773 mode_t oldmode = info -> dirmode ;
746774
747775 /* if we are not chmod'ing, then overwrite dirmode */
748- if ((info -> flags & GIT_CPDIR_CHMOD ) == 0 )
776+ if ((info -> flags & GIT_CPDIR_CHMOD_DIRS ) == 0 )
749777 info -> dirmode = from_st .st_mode ;
750778
751779 /* make directory now if CREATE_EMPTY_DIRS is requested and needed */
752780 if (!exists && (info -> flags & GIT_CPDIR_CREATE_EMPTY_DIRS ) != 0 )
753- error = git_futils_mkdir (
754- info -> to .ptr , NULL , info -> dirmode , info -> mkdir_flags );
781+ error = _cp_r_mkdir (info , from );
755782
756783 /* recurse onto target directory */
757- if (!exists || S_ISDIR (to_st .st_mode ))
784+ if (!error && (! exists || S_ISDIR (to_st .st_mode ) ))
758785 error = git_path_direach (from , _cp_r_callback , info );
759786
760787 if (oldmode != 0 )
@@ -782,15 +809,22 @@ static int _cp_r_callback(void *ref, git_buf *from)
782809
783810 /* Make container directory on demand if needed */
784811 if ((info -> flags & GIT_CPDIR_CREATE_EMPTY_DIRS ) == 0 &&
785- git_futils_mkdir (
786- info -> to .ptr , NULL , info -> dirmode , info -> mkdir_flags ) < 0 )
787- return -1 ;
812+ (error = _cp_r_mkdir (info , from )) < 0 )
813+ return error ;
788814
789815 /* make symlink or regular file */
790816 if (S_ISLNK (from_st .st_mode ))
791- return cp_link (from -> ptr , info -> to .ptr , (size_t )from_st .st_size );
792- else
793- return git_futils_cp (from -> ptr , info -> to .ptr , from_st .st_mode );
817+ error = cp_link (from -> ptr , info -> to .ptr , (size_t )from_st .st_size );
818+ else {
819+ mode_t usemode = from_st .st_mode ;
820+
821+ if ((info -> flags & GIT_CPDIR_SIMPLE_TO_MODE ) != 0 )
822+ usemode = (usemode & 0111 ) ? 0777 : 0666 ;
823+
824+ error = git_futils_cp (from -> ptr , info -> to .ptr , usemode );
825+ }
826+
827+ return error ;
794828}
795829
796830int git_futils_cp_r (
@@ -803,7 +837,7 @@ int git_futils_cp_r(
803837 git_buf path = GIT_BUF_INIT ;
804838 cp_r_info info ;
805839
806- if (git_buf_sets (& path , from ) < 0 )
840+ if (git_buf_joinpath (& path , from , "" ) < 0 ) /* ensure trailing slash */
807841 return -1 ;
808842
809843 info .to_root = to ;
@@ -814,12 +848,16 @@ int git_futils_cp_r(
814848
815849 /* precalculate mkdir flags */
816850 if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS ) == 0 ) {
851+ /* if not creating empty dirs, then use mkdir to create the path on
852+ * demand right before files are copied.
853+ */
817854 info .mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST ;
818- if ((flags & GIT_CPDIR_CHMOD ) != 0 )
855+ if ((flags & GIT_CPDIR_CHMOD_DIRS ) != 0 )
819856 info .mkdir_flags |= GIT_MKDIR_CHMOD_PATH ;
820857 } else {
858+ /* otherwise, we will do simple mkdir as directories are encountered */
821859 info .mkdir_flags =
822- ((flags & GIT_CPDIR_CHMOD ) != 0 ) ? GIT_MKDIR_CHMOD : 0 ;
860+ ((flags & GIT_CPDIR_CHMOD_DIRS ) != 0 ) ? GIT_MKDIR_CHMOD : 0 ;
823861 }
824862
825863 error = _cp_r_callback (& info , & path );
0 commit comments