1 /* makepath.c -- Ensure that a directory path exists.
2 Copyright (C) 1990, 1997 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
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> and Jim Meyering. */
25 # define alloca __builtin_alloca
39 #include <sys/types.h>
45 #if STAT_MACROS_BROKEN
49 #if !defined(S_ISDIR) && defined(S_IFDIR)
50 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
83 void strip_trailing_slashes ();
88 /* We're done operating on basename_dir. \
89 Restore working directory. */ \
92 int fail = restore_cwd (&cwd, NULL, NULL); \
108 /* Ensure that the directory ARGPATH exists.
109 Remove any trailing slashes from ARGPATH before calling this function.
111 Create any leading directories that don't already exist, with
112 permissions PARENT_MODE.
113 If the last element of ARGPATH does not exist, create it as
114 a new directory with permissions MODE.
115 If OWNER and GROUP are non-negative, use them to set the UID and GID of
116 any created directories.
117 If VERBOSE_FMT_STRING is nonzero, use it as a printf format
118 string for printing a message after successfully making a directory,
119 with the name of the directory that was just made as an argument.
120 If PRESERVE_EXISTING is non-zero and ARGPATH is an existing directory,
121 then do not attempt to set its permissions and ownership.
123 Return 0 if ARGPATH exists as a directory with the proper
124 ownership and permissions when done, otherwise 1. */
127 make_path (const char *argpath,
132 int preserve_existing,
133 const char *verbose_fmt_string)
138 if (stat (argpath, &stats))
141 int tmp_mode; /* Initial perms for leading dirs. */
142 int re_protect; /* Should leading dirs be unwritable? */
146 struct ptr_list *next;
148 struct ptr_list *p, *leading_dirs = NULL;
149 int do_chdir; /* Whether to chdir before each mkdir. */
150 struct saved_cwd cwd;
154 /* Temporarily relax umask in case it's overly restrictive. */
155 int oldmask = umask (0);
157 /* Make a copy of ARGPATH that we can scribble NULs on. */
158 dirpath = (char *) alloca (strlen (argpath) + 1);
159 strcpy (dirpath, argpath);
160 strip_trailing_slashes (dirpath);
162 /* If leading directories shouldn't be writable or executable,
163 or should have set[ug]id or sticky bits set and we are setting
164 their owners, we need to fix their permissions after making them. */
165 if (((parent_mode & 0300) != 0300)
166 || (owner != (uid_t) -1 && group != (gid_t) -1
167 && (parent_mode & 07000) != 0))
174 tmp_mode = parent_mode;
178 /* If we can record the current working directory, we may be able
179 to do the chdir optimization. */
180 do_chdir = !save_cwd (&cwd);
182 /* If we've saved the cwd and DIRPATH is an absolute pathname,
183 we must chdir to `/' in order to enable the chdir optimization.
184 So if chdir ("/") fails, turn off the optimization. */
185 if (do_chdir && *dirpath == '/' && chdir ("/") < 0)
190 /* Skip over leading slashes. */
191 while (*slash == '/')
196 int newly_created_dir = 1;
198 /* slash points to the leftmost unprocessed component of dirpath. */
199 basename_dir = slash;
201 slash = strchr (slash, '/');
205 /* If we're *not* doing chdir before each mkdir, then we have to refer
206 to the target using the full (multi-component) directory name. */
208 basename_dir = dirpath;
211 if (mkdir (basename_dir, tmp_mode))
213 if (stat (basename_dir, &stats))
215 error (0, errno, "cannot create directory `%s'", dirpath);
219 else if (!S_ISDIR (stats.st_mode))
221 error (0, 0, "`%s' exists but is not a directory", dirpath);
227 /* DIRPATH already exists and is a directory. */
228 newly_created_dir = 0;
232 if (newly_created_dir && verbose_fmt_string != NULL)
233 fprintf (stderr, verbose_fmt_string, dirpath);
235 if (owner != (uid_t) -1 && group != (gid_t) -1
236 && chown (basename_dir, owner, group)
237 #if defined(AFS) && defined (EPERM)
242 error (0, errno, "%s", dirpath);
249 struct ptr_list *new = (struct ptr_list *)
250 alloca (sizeof (struct ptr_list));
251 new->dirname_end = slash;
252 new->next = leading_dirs;
256 /* If we were able to save the initial working directory,
257 then we can use chdir to change into each directory before
258 creating an entry in that directory. This avoids making
259 stat and mkdir process O(n^2) file name components. */
260 if (do_chdir && chdir (basename_dir) < 0)
262 error (0, errno, "cannot chdir to directory, %s", dirpath);
269 /* Avoid unnecessary calls to `stat' when given
270 pathnames containing multiple adjacent slashes. */
271 while (*slash == '/')
276 basename_dir = dirpath;
278 /* We're done making leading directories.
279 Create the final component of the path. */
281 /* The path could end in "/." or contain "/..", so test
282 if we really have to create the directory. */
284 if (stat (basename_dir, &stats) && mkdir (basename_dir, mode))
286 error (0, errno, "cannot create directory `%s'", dirpath);
291 /* Done creating directories. Restore original umask. */
294 if (verbose_fmt_string != NULL)
295 error (0, 0, verbose_fmt_string, dirpath);
297 if (owner != (uid_t) -1 && group != (gid_t) -1)
299 if (chown (basename_dir, owner, group)
305 error (0, errno, "cannot chown %s", dirpath);
308 /* chown may have turned off some permission bits we wanted. */
309 if ((mode & 07000) != 0 && chmod (basename_dir, mode))
311 error (0, errno, "cannot chmod %s", dirpath);
318 /* If the mode for leading directories didn't include owner "wx"
319 privileges, we have to reset their protections to the correct
321 for (p = leading_dirs; p != NULL; p = p->next)
323 *(p->dirname_end) = '\0';
324 if (chmod (dirpath, parent_mode))
326 error (0, errno, "%s", dirpath);
333 /* We get here if the entire path already exists. */
335 const char *dirpath = argpath;
337 if (!S_ISDIR (stats.st_mode))
339 error (0, 0, "`%s' exists but is not a directory", dirpath);
343 if (!preserve_existing)
345 /* chown must precede chmod because on some systems,
346 chown clears the set[ug]id bits for non-superusers,
347 resulting in incorrect permissions.
348 On System V, users can give away files with chown and then not
349 be able to chmod them. So don't give files away. */
351 if (owner != (uid_t) -1 && group != (gid_t) -1
352 && chown (dirpath, owner, group)
358 error (0, errno, "%s", dirpath);
361 if (chmod (dirpath, mode))
363 error (0, errno, "%s", dirpath);