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
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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. */
26 #define alloca __builtin_alloca
40 #include <sys/types.h>
45 #if !defined(S_ISDIR) && defined(S_IFDIR)
46 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
56 #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
65 #if defined(__MSDOS__) && !defined(__GNUC__)
72 /* Ensure that the directory ARGPATH exists.
73 Remove any trailing slashes from ARGPATH before calling this function.
75 Make any leading directories that don't already exist, with
76 permissions PARENT_MODE.
77 If the last element of ARGPATH does not exist, create it as
78 a new directory with permissions MODE.
79 If OWNER and GROUP are non-negative, make them the UID and GID of
81 If VERBOSE_FMT_STRING is nonzero, use it as a printf format
82 string for printing a message after successfully making a directory,
83 with the name of the directory that was just made as an argument.
85 Return 0 if ARGPATH exists as a directory with the proper
86 ownership and permissions when done, otherwise 1. */
89 make_path (argpath
, mode
, parent_mode
, owner
, group
, verbose_fmt_string
)
95 char *verbose_fmt_string
;
97 char *dirpath
; /* A copy we can scribble NULs on. */
100 int oldmask
= umask (0);
101 dirpath
= alloca (strlen (argpath
) + 1);
102 strcpy (dirpath
, argpath
);
104 if (stat (dirpath
, &stats
))
107 int tmp_mode
; /* Initial perms for leading dirs. */
108 int re_protect
; /* Should leading dirs be unwritable? */
112 struct ptr_list
*next
;
114 struct ptr_list
*p
, *leading_dirs
= NULL
;
116 /* If leading directories shouldn't be writable or executable,
117 or should have set[ug]id or sticky bits set and we are setting
118 their owners, we need to fix their permissions after making them. */
119 if (((parent_mode
& 0300) != 0300)
120 || (owner
!= (uid_t
) -1 && group
!= (gid_t
) -1
121 && (parent_mode
& 07000) != 0))
128 tmp_mode
= parent_mode
;
133 while (*slash
== '/')
135 while ((slash
= index (slash
, '/')))
142 if (stat (dirpath
, &stats
))
145 /* If this component of the pathname ends in `+' and is
146 followed by 2 `/'s, then this is a CDF. We remove the
147 `+' from the name and create the directory. Later
148 we will "hide" the directory. */
149 if ( (*(slash
+1) == '/') && (*(slash
-1) == '+') )
155 if (mkdir (dirpath
, tmp_mode
))
157 error (0, errno
, "cannot make directory `%s'", dirpath
);
163 if (verbose_fmt_string
!= NULL
)
164 error (0, 0, verbose_fmt_string
, dirpath
);
166 if (owner
!= (uid_t
) -1 && group
!= (gid_t
) -1
167 && chown (dirpath
, owner
, group
)
173 error (0, errno
, "%s", dirpath
);
178 struct ptr_list
*new = (struct ptr_list
*)
179 alloca (sizeof (struct ptr_list
));
180 new->dirname_end
= slash
;
181 new->next
= leading_dirs
;
187 /* If this is a CDF, "hide" the directory by setting
188 its hidden/setuid bit. Also add the `+' back to
189 its name (since once it's "hidden" we must refer
190 to as `name+' instead of `name'). */
191 chmod (dirpath
, 04700);
197 else if (!S_ISDIR (stats
.st_mode
))
199 error (0, 0, "`%s' exists but is not a directory", dirpath
);
206 /* Avoid unnecessary calls to `stat' when given
207 pathnames containing multiple adjacent slashes. */
208 while (*slash
== '/')
212 /* We're done making leading directories.
213 Make the final component of the path. */
215 if (mkdir (dirpath
, mode
))
217 /* In some cases, if the final component in dirpath was `.' then we
218 just got an EEXIST error from that last mkdir(). If that's
219 the case, ignore it. */
220 if ( (errno
!= EEXIST
) ||
221 (stat (dirpath
, &stats
) != 0) ||
222 (!S_ISDIR (stats
.st_mode
) ) )
224 error (0, errno
, "cannot make directory `%s'", dirpath
);
229 if (verbose_fmt_string
!= NULL
)
230 error (0, 0, verbose_fmt_string
, dirpath
);
232 if (owner
!= (uid_t
) -1 && group
!= (gid_t
) -1)
234 if (chown (dirpath
, owner
, group
)
240 error (0, errno
, "%s", dirpath
);
244 /* chown may have turned off some permission bits we wanted. */
245 if ((mode
& 07000) != 0 && chmod (dirpath
, mode
))
247 error (0, errno
, "%s", dirpath
);
251 /* If the mode for leading directories didn't include owner "wx"
252 privileges, we have to reset their protections to the correct
254 for (p
= leading_dirs
; p
!= NULL
; p
= p
->next
)
256 *(p
->dirname_end
) = '\0';
258 /* cpio always calls make_path with parent mode 0700, so
259 we don't have to do this. If we ever do have to do this,
260 we have to stat the directory first to get the setuid
261 bit so we don't break HP CDF's. */
262 if (chmod (dirpath
, parent_mode
))
264 error (0, errno
, "%s", dirpath
);
273 /* We get here if the entire path already exists. */
275 if (!S_ISDIR (stats
.st_mode
))
277 error (0, 0, "`%s' exists but is not a directory", dirpath
);
282 /* chown must precede chmod because on some systems,
283 chown clears the set[ug]id bits for non-superusers,
284 resulting in incorrect permissions.
285 On System V, users can give away files with chown and then not
286 be able to chmod them. So don't give files away. */
288 if (owner
!= (uid_t
) -1 && group
!= (gid_t
) -1
289 && chown (dirpath
, owner
, group
)
295 error (0, errno
, "%s", dirpath
);
298 if (chmod (dirpath
, mode
))
300 error (0, errno
, "%s", dirpath
);