X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fmakepath.c;h=7631c48329f67c69ff7fbbc77b7485ef4ce77b93;hb=1cc4ba44c23782d32d4d2986b355e336f5e21335;hp=460129477b81bb3e210433a2721bb779d1e7ce60;hpb=0bd4ecccc6c6547b998b029445c9d9ba36f5f73a;p=gnulib.git diff --git a/lib/makepath.c b/lib/makepath.c index 460129477..7631c4832 100644 --- a/lib/makepath.c +++ b/lib/makepath.c @@ -87,7 +87,7 @@ void strip_trailing_slashes (); { \ /* We're done operating on basename_dir. \ Restore working directory. */ \ - if (saved_cwd) \ + if (do_chdir) \ { \ int fail = restore_cwd (&cwd, NULL, NULL); \ free_cwd (&cwd); \ @@ -159,10 +159,9 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, struct ptr_list *next; }; struct ptr_list *p, *leading_dirs = NULL; - int saved_cwd = 0; + int do_chdir; /* Whether to chdir before each mkdir. */ struct saved_cwd cwd; char *basename_dir; - int first_subdir = 1; char *dirpath; /* Temporarily relax umask in case it's overly restrictive. */ @@ -189,6 +188,16 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, re_protect = 0; } + /* If we can record the current working directory, we may be able + to do the chdir optimization. */ + do_chdir = !save_cwd (&cwd); + + /* If we've saved the cwd and DIRPATH is an absolute pathname, + we must chdir to `/' in order to enable the chdir optimization. + So if chdir ("/") fails, turn off the optimization. */ + if (do_chdir && *dirpath == '/' && chdir ("/") < 0) + do_chdir = 0; + slash = dirpath; /* Skip over leading slashes. */ @@ -197,6 +206,8 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, while (1) { + int newly_created_dir = 1; + /* slash points to the leftmost unprocessed component of dirpath. */ basename_dir = slash; @@ -204,59 +215,62 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, if (slash == NULL) break; - if (first_subdir) - { - first_subdir = 0; - saved_cwd = !save_cwd (&cwd); - } - - if (!saved_cwd) + /* If we're *not* doing chdir before each mkdir, then we have to refer + to the target using the full (multi-component) directory name. */ + if (!do_chdir) basename_dir = dirpath; *slash = '\0'; - if (stat (basename_dir, &stats)) + if (mkdir (basename_dir, tmp_mode)) { - if (mkdir (basename_dir, tmp_mode)) + if (stat (basename_dir, &stats)) { error (0, errno, "cannot create directory `%s'", dirpath); CLEANUP; return 1; } + else if (!S_ISDIR (stats.st_mode)) + { + error (0, 0, "`%s' exists but is not a directory", dirpath); + CLEANUP; + return 1; + } else { - if (verbose_fmt_string != NULL) - error (0, 0, verbose_fmt_string, dirpath); + /* DIRPATH already exists and is a directory. */ + newly_created_dir = 0; + } + } + + if (newly_created_dir && verbose_fmt_string != NULL) + fprintf (stderr, verbose_fmt_string, dirpath); - if (owner != (uid_t) -1 && group != (gid_t) -1 - && chown (basename_dir, owner, group) + if (owner != (uid_t) -1 && group != (gid_t) -1 + && chown (basename_dir, owner, group) #if defined(AFS) && defined (EPERM) - && errno != EPERM + && errno != EPERM #endif - ) - { - error (0, errno, "%s", dirpath); - CLEANUP; - return 1; - } - - if (re_protect) - { - struct ptr_list *new = (struct ptr_list *) - alloca (sizeof (struct ptr_list)); - new->dirname_end = slash; - new->next = leading_dirs; - leading_dirs = new; - } - } - } - else if (!S_ISDIR (stats.st_mode)) + ) { - error (0, 0, "`%s' exists but is not a directory", dirpath); + error (0, errno, "%s", dirpath); CLEANUP; return 1; } - if (saved_cwd && chdir (basename_dir) < 0) + if (re_protect) + { + struct ptr_list *new = (struct ptr_list *) + alloca (sizeof (struct ptr_list)); + new->dirname_end = slash; + new->next = leading_dirs; + leading_dirs = new; + } + + /* If we were able to save the initial working directory, + then we can use chdir to change into each directory before + creating an entry in that directory. This avoids making + stat and mkdir process O(n^2) file name components. */ + if (do_chdir && chdir (basename_dir) < 0) { error (0, errno, "cannot chdir to directory, %s", dirpath); CLEANUP; @@ -271,7 +285,7 @@ make_path (argpath, mode, parent_mode, owner, group, preserve_existing, slash++; } - if (!saved_cwd) + if (!do_chdir) basename_dir = dirpath; /* We're done making leading directories.