pnpinfo(8): Oops, add missing semicolons.
[dragonfly.git] / contrib / cpio / makepath.c
blobfc47871868c61fb39a34cb2c26fd06bc14193b3e
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)
7 any later version.
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. */
25 #ifdef __GNUC__
26 #define alloca __builtin_alloca
27 #else
28 #ifdef HAVE_ALLOCA_H
29 #include <alloca.h>
30 #else
31 #ifdef _AIX
32 #pragma alloca
33 #else
34 char *alloca ();
35 #endif
36 #endif
37 #endif
39 #include <stdio.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #ifdef HAVE_UNISTD_H
43 #include <unistd.h>
44 #endif
45 #if !defined(S_ISDIR) && defined(S_IFDIR)
46 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
47 #endif
49 #include <errno.h>
50 #ifdef STDC_HEADERS
51 #include <stdlib.h>
52 #else
53 extern int errno;
54 #endif
56 #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
57 #include <string.h>
58 #ifndef index
59 #define index strchr
60 #endif
61 #else
62 #include <strings.h>
63 #endif
65 #if defined(__MSDOS__) && !defined(__GNUC__)
66 typedef int uid_t;
67 typedef int gid_t;
68 #endif
70 void error ();
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
80 created directories.
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. */
88 int
89 make_path (argpath, mode, parent_mode, owner, group, verbose_fmt_string)
90 char *argpath;
91 int mode;
92 int parent_mode;
93 uid_t owner;
94 gid_t group;
95 char *verbose_fmt_string;
97 char *dirpath; /* A copy we can scribble NULs on. */
98 struct stat stats;
99 int retval = 0;
100 int oldmask = umask (0);
101 dirpath = alloca (strlen (argpath) + 1);
102 strcpy (dirpath, argpath);
104 if (stat (dirpath, &stats))
106 char *slash;
107 int tmp_mode; /* Initial perms for leading dirs. */
108 int re_protect; /* Should leading dirs be unwritable? */
109 struct ptr_list
111 char *dirname_end;
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))
123 tmp_mode = 0700;
124 re_protect = 1;
126 else
128 tmp_mode = parent_mode;
129 re_protect = 0;
132 slash = dirpath;
133 while (*slash == '/')
134 slash++;
135 while ((slash = index (slash, '/')))
137 #ifdef HPUX_CDF
138 int iscdf;
139 iscdf = 0;
140 #endif
141 *slash = '\0';
142 if (stat (dirpath, &stats))
144 #ifdef HPUX_CDF
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) == '+') )
151 iscdf = 1;
152 *(slash -1) = '\0';
154 #endif
155 if (mkdir (dirpath, tmp_mode))
157 error (0, errno, "cannot make directory `%s'", dirpath);
158 umask (oldmask);
159 return 1;
161 else
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)
168 #ifdef AFS
169 && errno != EPERM
170 #endif
173 error (0, errno, "%s", dirpath);
174 retval = 1;
176 if (re_protect)
178 struct ptr_list *new = (struct ptr_list *)
179 alloca (sizeof (struct ptr_list));
180 new->dirname_end = slash;
181 new->next = leading_dirs;
182 leading_dirs = new;
184 #ifdef HPUX_CDF
185 if (iscdf)
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);
192 *(slash - 1) = '+';
194 #endif
197 else if (!S_ISDIR (stats.st_mode))
199 error (0, 0, "`%s' exists but is not a directory", dirpath);
200 umask (oldmask);
201 return 1;
204 *slash++ = '/';
206 /* Avoid unnecessary calls to `stat' when given
207 pathnames containing multiple adjacent slashes. */
208 while (*slash == '/')
209 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);
225 umask (oldmask);
226 return 1;
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)
235 #ifdef AFS
236 && errno != EPERM
237 #endif
240 error (0, errno, "%s", dirpath);
241 retval = 1;
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);
248 retval = 1;
251 /* If the mode for leading directories didn't include owner "wx"
252 privileges, we have to reset their protections to the correct
253 value. */
254 for (p = leading_dirs; p != NULL; p = p->next)
256 *(p->dirname_end) = '\0';
257 #if 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);
265 retval = 1;
267 #endif
271 else
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);
278 umask (oldmask);
279 return 1;
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)
290 #ifdef AFS
291 && errno != EPERM
292 #endif
295 error (0, errno, "%s", dirpath);
296 retval = 1;
298 if (chmod (dirpath, mode))
300 error (0, errno, "%s", dirpath);
301 retval = 1;
305 umask (oldmask);
306 return retval;