1 /* makepath.c -- Ensure that a directory path exists.
2 Copyright (C) 1990, 2006 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public
15 License along with this program; if not, write to the Free
16 Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301 USA. */
19 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> and
20 Jim Meyering <meyering@cs.utexas.edu>. */
22 /* This copy of makepath is almost like the fileutils one, but has
23 changes for HPUX CDF's. Maybe the 2 versions of makepath can
24 come together again in the future. */
30 #include <sys/types.h>
33 /* Ensure that the directory ARGPATH exists.
34 Remove any trailing slashes from ARGPATH before calling this function.
36 Make any leading directories that don't already exist, with
37 permissions PARENT_MODE.
38 If the last element of ARGPATH does not exist, create it as
39 a new directory with permissions MODE.
40 If OWNER and GROUP are non-negative, make them the UID and GID of
42 If VERBOSE_FMT_STRING is nonzero, use it as a printf format
43 string for printing a message after successfully making a directory,
44 with the name of the directory that was just made as an argument.
46 Return 0 if ARGPATH exists as a directory with the proper
47 ownership and permissions when done, otherwise 1. */
50 make_path (char *argpath,
55 char *verbose_fmt_string)
57 char *dirpath; /* A copy we can scribble NULs on. */
60 int oldmask = umask (0);
61 dirpath = alloca (strlen (argpath) + 1);
62 strcpy (dirpath, argpath);
64 if (stat (dirpath, &stats))
67 int tmp_mode; /* Initial perms for leading dirs. */
68 int re_protect; /* Should leading dirs be unwritable? */
72 struct ptr_list *next;
74 struct ptr_list *p, *leading_dirs = NULL;
76 /* If leading directories shouldn't be writable or executable,
77 or should have set[ug]id or sticky bits set and we are setting
78 their owners, we need to fix their permissions after making them. */
79 if (((parent_mode & 0300) != 0300)
80 || (owner != (uid_t) -1 && group != (gid_t) -1
81 && (parent_mode & 07000) != 0))
88 tmp_mode = parent_mode;
95 while ((slash = strchr (slash, '/')))
102 if (stat (dirpath, &stats))
105 /* If this component of the pathname ends in `+' and is
106 followed by 2 `/'s, then this is a CDF. We remove the
107 `+' from the name and create the directory. Later
108 we will "hide" the directory. */
109 if ( (*(slash +1) == '/') && (*(slash -1) == '+') )
115 if (mkdir (dirpath, tmp_mode))
117 error (0, errno, _("cannot make directory `%s'"), dirpath);
123 if (verbose_fmt_string != NULL)
124 error (0, 0, verbose_fmt_string, dirpath);
126 if (owner != (uid_t) -1 && group != (gid_t) -1
127 && chown (dirpath, owner, group)
133 chown_error_details (dirpath, owner, group);
138 struct ptr_list *new = (struct ptr_list *)
139 alloca (sizeof (struct ptr_list));
140 new->dirname_end = slash;
141 new->next = leading_dirs;
147 /* If this is a CDF, "hide" the directory by setting
148 its hidden/setuid bit. Also add the `+' back to
149 its name (since once it's "hidden" we must refer
150 to as `name+' instead of `name'). */
151 chmod (dirpath, 04700);
157 else if (!S_ISDIR (stats.st_mode))
159 error (0, 0, _("`%s' exists but is not a directory"), dirpath);
166 /* Avoid unnecessary calls to `stat' when given
167 pathnames containing multiple adjacent slashes. */
168 while (*slash == '/')
172 /* We're done making leading directories.
173 Make the final component of the path. */
175 if (mkdir (dirpath, mode))
177 /* In some cases, if the final component in dirpath was `.' then we
178 just got an EEXIST error from that last mkdir(). If that's
179 the case, ignore it. */
180 if ( (errno != EEXIST) ||
181 (stat (dirpath, &stats) != 0) ||
182 (!S_ISDIR (stats.st_mode) ) )
184 error (0, errno, _("cannot make directory `%s'"), dirpath);
189 if (verbose_fmt_string != NULL)
190 error (0, 0, verbose_fmt_string, dirpath);
192 if (owner != (uid_t) -1 && group != (gid_t) -1)
194 if (chown (dirpath, owner, group)
200 chown_error_details (dirpath, owner, group);
204 /* chown may have turned off some permission bits we wanted. */
205 if ((mode & 07000) != 0 && chmod (dirpath, mode))
207 chmod_error_details (dirpath, mode);
211 /* If the mode for leading directories didn't include owner "wx"
212 privileges, we have to reset their protections to the correct
214 for (p = leading_dirs; p != NULL; p = p->next)
216 *p->dirname_end = '\0';
218 /* cpio always calls make_path with parent mode 0700, so
219 we don't have to do this. If we ever do have to do this,
220 we have to stat the directory first to get the setuid
221 bit so we don't break HP CDF's. */
222 if (chmod (dirpath, parent_mode))
224 chmod_error_details (dirpath, parent_mode);
233 /* We get here if the entire path already exists. */
235 if (!S_ISDIR (stats.st_mode))
237 error (0, 0, _("`%s' exists but is not a directory"), dirpath);
242 /* chown must precede chmod because on some systems,
243 chown clears the set[ug]id bits for non-superusers,
244 resulting in incorrect permissions.
245 On System V, users can give away files with chown and then not
246 be able to chmod them. So don't give files away. */
248 if (owner != (uid_t) -1 && group != (gid_t) -1
249 && chown (dirpath, owner, group)
255 chown_error_details (dirpath, owner, group);
258 if (chmod (dirpath, mode))
260 chmod_error_details (dirpath, mode);