{ \
/* 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); \
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. */
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. */
while (1)
{
+ int newly_created_dir = 1;
+
/* slash points to the leftmost unprocessed component of dirpath. */
basename_dir = slash;
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;
slash++;
}
- if (!saved_cwd)
+ if (!do_chdir)
basename_dir = dirpath;
/* We're done making leading directories.