- char *slash;
- mode_t tmp_mode; /* Initial perms for leading dirs. */
- bool re_protect; /* Should leading dirs be unwritable? */
- struct ptr_list
- {
- char *dirname_end;
- struct ptr_list *next;
- };
- struct ptr_list *p, *leading_dirs = NULL;
- bool do_chdir; /* Whether to chdir before each mkdir. */
- struct saved_cwd cwd;
- char *basename_dir;
- char *dir;
-
- /* Temporarily relax umask in case it's overly restrictive. */
- mode_t oldmask = umask (0);
-
- /* Make a copy of ARG that we can scribble NULs on. */
- dir = (char *) alloca (strlen (arg) + 1);
- strcpy (dir, arg);
- strip_trailing_slashes (dir);
-
- /* If leading directories shouldn't be writable or executable,
- or should have set[ug]id or sticky bits set and we are setting
- their owners, we need to fix their permissions after making them. */
- if (((parent_mode & WX_USR) != WX_USR)
- || ((owner != (uid_t) -1 || group != (gid_t) -1)
- && (parent_mode & (S_ISUID | S_ISGID | S_ISVTX)) != 0))
- {
- tmp_mode = S_IRWXU;
- re_protect = true;
- }
- else
- {
- tmp_mode = parent_mode;
- re_protect = false;
- }
-
- /* If we can record the current working directory, we may be able
- to do the chdir optimization. */
- do_chdir = (save_cwd (&cwd) == 0);
-
- /* If we've saved the cwd and DIR is an absolute file name,
- we must chdir to `/' in order to enable the chdir optimization.
- So if chdir ("/") fails, turn off the optimization. */
- if (do_chdir && dir[0] == '/')
- {
- /* POSIX says "//" might be special, so chdir to "//" if the
- file name starts with exactly two slashes. */
- char const *root = "//" + (dir[1] != '/' || dir[2] == '/');
- if (chdir (root) != 0)
- do_chdir = false;
- }
-
- slash = dir;
-
- /* Skip over leading slashes. */
- while (*slash == '/')
- slash++;
-
- while (1)
- {
- bool newly_created_dir;
-
- /* slash points to the leftmost unprocessed component of dir. */
- basename_dir = slash;
-
- slash = strchr (slash, '/');
- if (slash == NULL)
- break;
-
- /* 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 = dir;
-
- *slash = '\0';
- if (! make_dir (basename_dir, dir, tmp_mode, &newly_created_dir))
- {
- CLEANUP;
- return false;
- }
-
- if (newly_created_dir)
- {
- if (verbose_fmt_string)
- error (0, 0, verbose_fmt_string, quote (dir));
-
- if ((owner != (uid_t) -1 || group != (gid_t) -1)
- && chown (basename_dir, owner, group)
-#if defined AFS && defined EPERM
- && errno != EPERM
-#endif
- )
- {
- error (0, errno, _("cannot change owner and/or group of %s"),
- quote (dir));
- CLEANUP;
- return false;
- }
-
- if (re_protect)
- {
- struct ptr_list *new = (struct ptr_list *)
- alloca (sizeof *new);
- new->dirname_end = slash;
- new->next = leading_dirs;
- leading_dirs = new;
- }
- }