]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/cpio/src/makepath.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / cpio / src / makepath.c
1 /* makepath.c -- Ensure that a directory path exists.
2    Copyright (C) 1990 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 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.  */
17
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> and
19    Jim Meyering <meyering@cs.utexas.edu>.  */
20
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.  */
24
25 #include <system.h>
26
27 #ifdef __GNUC__
28 #define alloca __builtin_alloca
29 #else
30 #ifdef HAVE_ALLOCA_H
31 #include <alloca.h>
32 #else
33 #ifdef _AIX
34  #pragma alloca
35 #else
36 char *alloca ();
37 #endif
38 #endif
39 #endif
40
41 #include <stdio.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47 #if !defined(S_ISDIR) && defined(S_IFDIR)
48 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
49 #endif
50
51 #include <errno.h>
52 #ifdef STDC_HEADERS
53 #include <stdlib.h>
54 #else
55 extern int errno;
56 #endif
57
58 #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
59 #include <string.h>
60 #ifndef index
61 #define index strchr
62 #endif
63 #else
64 #include <strings.h>
65 #endif
66
67 /* Ensure that the directory ARGPATH exists.
68    Remove any trailing slashes from ARGPATH before calling this function.
69
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
75    created directories.
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.
79
80    Return 0 if ARGPATH exists as a directory with the proper
81    ownership and permissions when done, otherwise 1.  */
82
83 int
84 make_path (char *argpath,
85            int mode,
86            int parent_mode,
87            uid_t owner,
88            gid_t group,
89            char *verbose_fmt_string)
90 {
91   char *dirpath;                /* A copy we can scribble NULs on.  */
92   struct stat stats;
93   int retval = 0;
94   int oldmask = umask (0);
95   dirpath = alloca (strlen (argpath) + 1);
96   strcpy (dirpath, argpath);
97
98   if (stat (dirpath, &stats))
99     {
100       char *slash;
101       int tmp_mode;             /* Initial perms for leading dirs.  */
102       int re_protect;           /* Should leading dirs be unwritable? */
103       struct ptr_list
104       {
105         char *dirname_end;
106         struct ptr_list *next;
107       };
108       struct ptr_list *p, *leading_dirs = NULL;
109
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))
116         {
117           tmp_mode = 0700;
118           re_protect = 1;
119         }
120       else
121         {
122           tmp_mode = parent_mode;
123           re_protect = 0;
124         }
125
126       slash = dirpath;
127       while (*slash == '/')
128         slash++;
129       while ((slash = index (slash, '/')))
130         {
131 #ifdef HPUX_CDF
132           int   iscdf;
133           iscdf = 0;
134 #endif
135           *slash = '\0';
136           if (stat (dirpath, &stats))
137             {
138 #ifdef HPUX_CDF
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) == '+') )
144                 { 
145                   iscdf = 1;
146                   *(slash -1) = '\0';
147                 }
148 #endif
149               if (mkdir (dirpath, tmp_mode))
150                 {
151                   error (0, errno, _("cannot make directory `%s'"), dirpath);
152                   umask (oldmask);
153                   return 1;
154                 }
155               else
156                 {
157                   if (verbose_fmt_string != NULL)
158                     error (0, 0, verbose_fmt_string, dirpath);
159
160                   if (owner != (uid_t) -1 && group != (gid_t) -1
161                       && chown (dirpath, owner, group)
162 #ifdef AFS
163                       && errno != EPERM
164 #endif
165                       )
166                     {
167                       error (0, errno, "%s", dirpath);
168                       retval = 1;
169                     }
170                   if (re_protect)
171                     {
172                       struct ptr_list *new = (struct ptr_list *)
173                         alloca (sizeof (struct ptr_list));
174                       new->dirname_end = slash;
175                       new->next = leading_dirs;
176                       leading_dirs = new;
177                     }
178 #ifdef HPUX_CDF
179                   if (iscdf)
180                     {
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);
186                       *(slash - 1) = '+';
187                     }
188 #endif
189                 }
190             }
191           else if (!S_ISDIR (stats.st_mode))
192             {
193               error (0, 0, _("`%s' exists but is not a directory"), dirpath);
194               umask (oldmask);
195               return 1;
196             }
197
198           *slash++ = '/';
199
200           /* Avoid unnecessary calls to `stat' when given
201              pathnames containing multiple adjacent slashes.  */
202           while (*slash == '/')
203             slash++;
204         }
205
206       /* We're done making leading directories.
207          Make the final component of the path. */
208
209       if (mkdir (dirpath, mode))
210         {
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) ) )
217             {
218               error (0, errno, _("cannot make directory `%s'"), dirpath);
219               umask (oldmask);
220               return 1;
221             }
222         }
223       if (verbose_fmt_string != NULL)
224         error (0, 0, verbose_fmt_string, dirpath);
225
226       if (owner != (uid_t) -1 && group != (gid_t) -1)
227         {
228           if (chown (dirpath, owner, group)
229 #ifdef AFS
230               && errno != EPERM
231 #endif
232               )
233             {
234               error (0, errno, "%s", dirpath);
235               retval = 1;
236             }
237         }
238           /* chown may have turned off some permission bits we wanted.  */
239           if ((mode & 07000) != 0 && chmod (dirpath, mode))
240             {
241               error (0, errno, "%s", dirpath);
242               retval = 1;
243             }
244
245       /* If the mode for leading directories didn't include owner "wx"
246          privileges, we have to reset their protections to the correct
247          value.  */
248       for (p = leading_dirs; p != NULL; p = p->next)
249         {
250           *(p->dirname_end) = '\0';
251 #if 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))
257             {
258               error (0, errno, "%s", dirpath);
259               retval = 1;
260             }
261 #endif
262
263         }
264     }
265   else
266     {
267       /* We get here if the entire path already exists.  */
268
269       if (!S_ISDIR (stats.st_mode))
270         {
271           error (0, 0, _("`%s' exists but is not a directory"), dirpath);
272           umask (oldmask);
273           return 1;
274         }
275
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.  */
281
282       if (owner != (uid_t) -1 && group != (gid_t) -1
283           && chown (dirpath, owner, group)
284 #ifdef AFS
285           && errno != EPERM
286 #endif
287           )
288         {
289           error (0, errno, "%s", dirpath);
290           retval = 1;
291         }
292       if (chmod (dirpath, mode))
293         {
294           error (0, errno, "%s", dirpath);
295           retval = 1;
296         }
297     }
298
299   umask (oldmask);
300   return retval;
301 }