1 /* makepath.c -- Ensure that a directory path exists.
2 Copyright (C) 1990 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 License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> and
19 Jim Meyering <meyering@cs.utexas.edu>. */
21 /* This copy of makepath is almost like the fileutils one, but has
22 changes for HPUX CDF's. Maybe the 2 versions of makepath can
23 come together again in the future. */
28 #define alloca __builtin_alloca
42 #include <sys/types.h>
47 #if !defined(S_ISDIR) && defined(S_IFDIR)
48 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
58 #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
67 /* Ensure that the directory ARGPATH exists.
68 Remove any trailing slashes from ARGPATH before calling this function.
70 Make any leading directories that don't already exist, with
71 permissions PARENT_MODE.
72 If the last element of ARGPATH does not exist, create it as
73 a new directory with permissions MODE.
74 If OWNER and GROUP are non-negative, make them the UID and GID of
76 If VERBOSE_FMT_STRING is nonzero, use it as a printf format
77 string for printing a message after successfully making a directory,
78 with the name of the directory that was just made as an argument.
80 Return 0 if ARGPATH exists as a directory with the proper
81 ownership and permissions when done, otherwise 1. */
84 make_path (char *argpath,
89 char *verbose_fmt_string)
91 char *dirpath; /* A copy we can scribble NULs on. */
94 int oldmask = umask (0);
95 dirpath = alloca (strlen (argpath) + 1);
96 strcpy (dirpath, argpath);
98 if (stat (dirpath, &stats))
101 int tmp_mode; /* Initial perms for leading dirs. */
102 int re_protect; /* Should leading dirs be unwritable? */
106 struct ptr_list *next;
108 struct ptr_list *p, *leading_dirs = NULL;
110 /* If leading directories shouldn't be writable or executable,
111 or should have set[ug]id or sticky bits set and we are setting
112 their owners, we need to fix their permissions after making them. */
113 if (((parent_mode & 0300) != 0300)
114 || (owner != (uid_t) -1 && group != (gid_t) -1
115 && (parent_mode & 07000) != 0))
122 tmp_mode = parent_mode;
127 while (*slash == '/')
129 while ((slash = index (slash, '/')))
136 if (stat (dirpath, &stats))
139 /* If this component of the pathname ends in `+' and is
140 followed by 2 `/'s, then this is a CDF. We remove the
141 `+' from the name and create the directory. Later
142 we will "hide" the directory. */
143 if ( (*(slash +1) == '/') && (*(slash -1) == '+') )
149 if (mkdir (dirpath, tmp_mode))
151 error (0, errno, _("cannot make directory `%s'"), dirpath);
157 if (verbose_fmt_string != NULL)
158 error (0, 0, verbose_fmt_string, dirpath);
160 if (owner != (uid_t) -1 && group != (gid_t) -1
161 && chown (dirpath, owner, group)
167 error (0, errno, "%s", dirpath);
172 struct ptr_list *new = (struct ptr_list *)
173 alloca (sizeof (struct ptr_list));
174 new->dirname_end = slash;
175 new->next = leading_dirs;
181 /* If this is a CDF, "hide" the directory by setting
182 its hidden/setuid bit. Also add the `+' back to
183 its name (since once it's "hidden" we must refer
184 to as `name+' instead of `name'). */
185 chmod (dirpath, 04700);
191 else if (!S_ISDIR (stats.st_mode))
193 error (0, 0, _("`%s' exists but is not a directory"), dirpath);
200 /* Avoid unnecessary calls to `stat' when given
201 pathnames containing multiple adjacent slashes. */
202 while (*slash == '/')
206 /* We're done making leading directories.
207 Make the final component of the path. */
209 if (mkdir (dirpath, mode))
211 /* In some cases, if the final component in dirpath was `.' then we
212 just got an EEXIST error from that last mkdir(). If that's
213 the case, ignore it. */
214 if ( (errno != EEXIST) ||
215 (stat (dirpath, &stats) != 0) ||
216 (!S_ISDIR (stats.st_mode) ) )
218 error (0, errno, _("cannot make directory `%s'"), dirpath);
223 if (verbose_fmt_string != NULL)
224 error (0, 0, verbose_fmt_string, dirpath);
226 if (owner != (uid_t) -1 && group != (gid_t) -1)
228 if (chown (dirpath, owner, group)
234 error (0, errno, "%s", dirpath);
238 /* chown may have turned off some permission bits we wanted. */
239 if ((mode & 07000) != 0 && chmod (dirpath, mode))
241 error (0, errno, "%s", dirpath);
245 /* If the mode for leading directories didn't include owner "wx"
246 privileges, we have to reset their protections to the correct
248 for (p = leading_dirs; p != NULL; p = p->next)
250 *(p->dirname_end) = '\0';
252 /* cpio always calls make_path with parent mode 0700, so
253 we don't have to do this. If we ever do have to do this,
254 we have to stat the directory first to get the setuid
255 bit so we don't break HP CDF's. */
256 if (chmod (dirpath, parent_mode))
258 error (0, errno, "%s", dirpath);
267 /* We get here if the entire path already exists. */
269 if (!S_ISDIR (stats.st_mode))
271 error (0, 0, _("`%s' exists but is not a directory"), dirpath);
276 /* chown must precede chmod because on some systems,
277 chown clears the set[ug]id bits for non-superusers,
278 resulting in incorrect permissions.
279 On System V, users can give away files with chown and then not
280 be able to chmod them. So don't give files away. */
282 if (owner != (uid_t) -1 && group != (gid_t) -1
283 && chown (dirpath, owner, group)
289 error (0, errno, "%s", dirpath);
292 if (chmod (dirpath, mode))
294 error (0, errno, "%s", dirpath);