source: trunk/essentials/app-arch/cpio/src/makepath.c

Last change on this file was 3332, checked in by bird, 18 years ago

cpio 2.7

File size: 7.8 KB
Line 
1/* makepath.c -- Ensure that a directory path exists.
2 Copyright (C) 1990, 2006 Free Software Foundation, Inc.
3
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.
8
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.
13
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. */
18
19/* Written by David MacKenzie <djm@gnu.ai.mit.edu> and
20 Jim Meyering <meyering@cs.utexas.edu>. */
21
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. */
25
26#include <system.h>
27
28#ifdef __GNUC__
29#define alloca __builtin_alloca
30#else
31#ifdef HAVE_ALLOCA_H
32#include <alloca.h>
33#else
34#ifdef _AIX
35 #pragma alloca
36#else
37char *alloca ();
38#endif
39#endif
40#endif
41
42#include <stdio.h>
43#include <sys/types.h>
44#include <sys/stat.h>
45#ifdef HAVE_UNISTD_H
46#include <unistd.h>
47#endif
48#if !defined(S_ISDIR) && defined(S_IFDIR)
49#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
50#endif
51
52#include <errno.h>
53#ifdef STDC_HEADERS
54#include <stdlib.h>
55#else
56extern int errno;
57#endif
58
59#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
60#include <string.h>
61#ifndef index
62#define index strchr
63#endif
64#else
65#include <strings.h>
66#endif
67
68/* Ensure that the directory ARGPATH exists.
69 Remove any trailing slashes from ARGPATH before calling this function.
70
71 Make any leading directories that don't already exist, with
72 permissions PARENT_MODE.
73 If the last element of ARGPATH does not exist, create it as
74 a new directory with permissions MODE.
75 If OWNER and GROUP are non-negative, make them the UID and GID of
76 created directories.
77 If VERBOSE_FMT_STRING is nonzero, use it as a printf format
78 string for printing a message after successfully making a directory,
79 with the name of the directory that was just made as an argument.
80
81 Return 0 if ARGPATH exists as a directory with the proper
82 ownership and permissions when done, otherwise 1. */
83
84int
85make_path (char *argpath,
86 int mode,
87 int parent_mode,
88 uid_t owner,
89 gid_t group,
90 char *verbose_fmt_string)
91{
92 char *dirpath; /* A copy we can scribble NULs on. */
93 struct stat stats;
94 int retval = 0;
95 int oldmask = umask (0);
96 dirpath = alloca (strlen (argpath) + 1);
97 strcpy (dirpath, argpath);
98
99 if (stat (dirpath, &stats))
100 {
101 char *slash;
102 int tmp_mode; /* Initial perms for leading dirs. */
103 int re_protect; /* Should leading dirs be unwritable? */
104 struct ptr_list
105 {
106 char *dirname_end;
107 struct ptr_list *next;
108 };
109 struct ptr_list *p, *leading_dirs = NULL;
110
111 /* If leading directories shouldn't be writable or executable,
112 or should have set[ug]id or sticky bits set and we are setting
113 their owners, we need to fix their permissions after making them. */
114 if (((parent_mode & 0300) != 0300)
115 || (owner != (uid_t) -1 && group != (gid_t) -1
116 && (parent_mode & 07000) != 0))
117 {
118 tmp_mode = 0700;
119 re_protect = 1;
120 }
121 else
122 {
123 tmp_mode = parent_mode;
124 re_protect = 0;
125 }
126
127 slash = dirpath;
128 while (*slash == '/')
129 slash++;
130 while ((slash = strchr (slash, '/')))
131 {
132#ifdef HPUX_CDF
133 int iscdf;
134 iscdf = 0;
135#endif
136 *slash = '\0';
137 if (stat (dirpath, &stats))
138 {
139#ifdef HPUX_CDF
140 /* If this component of the pathname ends in `+' and is
141 followed by 2 `/'s, then this is a CDF. We remove the
142 `+' from the name and create the directory. Later
143 we will "hide" the directory. */
144 if ( (*(slash +1) == '/') && (*(slash -1) == '+') )
145 {
146 iscdf = 1;
147 *(slash -1) = '\0';
148 }
149#endif
150 if (mkdir (dirpath, tmp_mode))
151 {
152 error (0, errno, _("cannot make directory `%s'"), dirpath);
153 umask (oldmask);
154 return 1;
155 }
156 else
157 {
158 if (verbose_fmt_string != NULL)
159 error (0, 0, verbose_fmt_string, dirpath);
160
161 if (owner != (uid_t) -1 && group != (gid_t) -1
162 && chown (dirpath, owner, group)
163#ifdef AFS
164 && errno != EPERM
165#endif
166 )
167 {
168 chown_error_details (dirpath, owner, group);
169 retval = 1;
170 }
171 if (re_protect)
172 {
173 struct ptr_list *new = (struct ptr_list *)
174 alloca (sizeof (struct ptr_list));
175 new->dirname_end = slash;
176 new->next = leading_dirs;
177 leading_dirs = new;
178 }
179#ifdef HPUX_CDF
180 if (iscdf)
181 {
182 /* If this is a CDF, "hide" the directory by setting
183 its hidden/setuid bit. Also add the `+' back to
184 its name (since once it's "hidden" we must refer
185 to as `name+' instead of `name'). */
186 chmod (dirpath, 04700);
187 *(slash - 1) = '+';
188 }
189#endif
190 }
191 }
192 else if (!S_ISDIR (stats.st_mode))
193 {
194 error (0, 0, _("`%s' exists but is not a directory"), dirpath);
195 umask (oldmask);
196 return 1;
197 }
198
199 *slash++ = '/';
200
201 /* Avoid unnecessary calls to `stat' when given
202 pathnames containing multiple adjacent slashes. */
203 while (*slash == '/')
204 slash++;
205 }
206
207 /* We're done making leading directories.
208 Make the final component of the path. */
209
210 if (mkdir (dirpath, mode))
211 {
212 /* In some cases, if the final component in dirpath was `.' then we
213 just got an EEXIST error from that last mkdir(). If that's
214 the case, ignore it. */
215 if ( (errno != EEXIST) ||
216 (stat (dirpath, &stats) != 0) ||
217 (!S_ISDIR (stats.st_mode) ) )
218 {
219 error (0, errno, _("cannot make directory `%s'"), dirpath);
220 umask (oldmask);
221 return 1;
222 }
223 }
224 if (verbose_fmt_string != NULL)
225 error (0, 0, verbose_fmt_string, dirpath);
226
227 if (owner != (uid_t) -1 && group != (gid_t) -1)
228 {
229 if (chown (dirpath, owner, group)
230#ifdef AFS
231 && errno != EPERM
232#endif
233 )
234 {
235 chown_error_details (dirpath, owner, group);
236 retval = 1;
237 }
238 }
239 /* chown may have turned off some permission bits we wanted. */
240 if ((mode & 07000) != 0 && chmod (dirpath, mode))
241 {
242 chmod_error_details (dirpath, mode);
243 retval = 1;
244 }
245
246 /* If the mode for leading directories didn't include owner "wx"
247 privileges, we have to reset their protections to the correct
248 value. */
249 for (p = leading_dirs; p != NULL; p = p->next)
250 {
251 *p->dirname_end = '\0';
252#if 0
253 /* cpio always calls make_path with parent mode 0700, so
254 we don't have to do this. If we ever do have to do this,
255 we have to stat the directory first to get the setuid
256 bit so we don't break HP CDF's. */
257 if (chmod (dirpath, parent_mode))
258 {
259 chmod_error_details (dirpath, parent_mode);
260 retval = 1;
261 }
262#endif
263
264 }
265 }
266 else
267 {
268 /* We get here if the entire path already exists. */
269
270 if (!S_ISDIR (stats.st_mode))
271 {
272 error (0, 0, _("`%s' exists but is not a directory"), dirpath);
273 umask (oldmask);
274 return 1;
275 }
276
277 /* chown must precede chmod because on some systems,
278 chown clears the set[ug]id bits for non-superusers,
279 resulting in incorrect permissions.
280 On System V, users can give away files with chown and then not
281 be able to chmod them. So don't give files away. */
282
283 if (owner != (uid_t) -1 && group != (gid_t) -1
284 && chown (dirpath, owner, group)
285#ifdef AFS
286 && errno != EPERM
287#endif
288 )
289 {
290 chown_error_details (dirpath, owner, group);
291 retval = 1;
292 }
293 if (chmod (dirpath, mode))
294 {
295 chmod_error_details (dirpath, mode);
296 retval = 1;
297 }
298 }
299
300 umask (oldmask);
301 return retval;
302}
Note: See TracBrowser for help on using the repository browser.