Remove trailing whitespace.
[dragonfly.git] / contrib / cpio / 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
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, 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 #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
38
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
48
49 #include <errno.h>
50 #ifdef STDC_HEADERS
51 #include <stdlib.h>
52 #else
53 extern int errno;
54 #endif
55
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
64
65 #if defined(__MSDOS__) && !defined(__GNUC__)
66 typedef int uid_t;
67 typedef int gid_t;
68 #endif
69
70 void error ();
71
72 /* Ensure that the directory ARGPATH exists.
73    Remove any trailing slashes from ARGPATH before calling this function.
74
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.
84
85    Return 0 if ARGPATH exists as a directory with the proper
86    ownership and permissions when done, otherwise 1.  */
87
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;
96 {
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);
103
104   if (stat (dirpath, &stats))
105     {
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
110       {
111         char *dirname_end;
112         struct ptr_list *next;
113       };
114       struct ptr_list *p, *leading_dirs = NULL;
115
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))
122         {
123           tmp_mode = 0700;
124           re_protect = 1;
125         }
126       else
127         {
128           tmp_mode = parent_mode;
129           re_protect = 0;
130         }
131
132       slash = dirpath;
133       while (*slash == '/')
134         slash++;
135       while ((slash = index (slash, '/')))
136         {
137 #ifdef HPUX_CDF
138           int   iscdf;
139           iscdf = 0;
140 #endif
141           *slash = '\0';
142           if (stat (dirpath, &stats))
143             {
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) == '+') )
150                 { 
151                   iscdf = 1;
152                   *(slash -1) = '\0';
153                 }
154 #endif
155               if (mkdir (dirpath, tmp_mode))
156                 {
157                   error (0, errno, "cannot make directory `%s'", dirpath);
158                   umask (oldmask);
159                   return 1;
160                 }
161               else
162                 {
163                   if (verbose_fmt_string != NULL)
164                     error (0, 0, verbose_fmt_string, dirpath);
165
166                   if (owner != (uid_t) -1 && group != (gid_t) -1
167                       && chown (dirpath, owner, group)
168 #ifdef AFS
169                       && errno != EPERM
170 #endif
171                       )
172                     {
173                       error (0, errno, "%s", dirpath);
174                       retval = 1;
175                     }
176                   if (re_protect)
177                     {
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;
183                     }
184 #ifdef HPUX_CDF
185                   if (iscdf)
186                     {
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) = '+';
193                     }
194 #endif
195                 }
196             }
197           else if (!S_ISDIR (stats.st_mode))
198             {
199               error (0, 0, "`%s' exists but is not a directory", dirpath);
200               umask (oldmask);
201               return 1;
202             }
203
204           *slash++ = '/';
205
206           /* Avoid unnecessary calls to `stat' when given
207              pathnames containing multiple adjacent slashes.  */
208           while (*slash == '/')
209             slash++;
210         }
211
212       /* We're done making leading directories.
213          Make the final component of the path. */
214
215       if (mkdir (dirpath, mode))
216         {
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) ) )
223             {
224               error (0, errno, "cannot make directory `%s'", dirpath);
225               umask (oldmask);
226               return 1;
227             }
228         }
229       if (verbose_fmt_string != NULL)
230         error (0, 0, verbose_fmt_string, dirpath);
231
232       if (owner != (uid_t) -1 && group != (gid_t) -1)
233         {
234           if (chown (dirpath, owner, group)
235 #ifdef AFS
236               && errno != EPERM
237 #endif
238               )
239             {
240               error (0, errno, "%s", dirpath);
241               retval = 1;
242             }
243         }
244           /* chown may have turned off some permission bits we wanted.  */
245           if ((mode & 07000) != 0 && chmod (dirpath, mode))
246             {
247               error (0, errno, "%s", dirpath);
248               retval = 1;
249             }
250
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)
255         {
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))
263             {
264               error (0, errno, "%s", dirpath);
265               retval = 1;
266             }
267 #endif
268
269         }
270     }
271   else
272     {
273       /* We get here if the entire path already exists.  */
274
275       if (!S_ISDIR (stats.st_mode))
276         {
277           error (0, 0, "`%s' exists but is not a directory", dirpath);
278           umask (oldmask);
279           return 1;
280         }
281
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.  */
287
288       if (owner != (uid_t) -1 && group != (gid_t) -1
289           && chown (dirpath, owner, group)
290 #ifdef AFS
291           && errno != EPERM
292 #endif
293           )
294         {
295           error (0, errno, "%s", dirpath);
296           retval = 1;
297         }
298       if (chmod (dirpath, mode))
299         {
300           error (0, errno, "%s", dirpath);
301           retval = 1;
302         }
303     }
304
305   umask (oldmask);
306   return retval;
307 }