(compile_range): When casting to const unsigned char *, put const first.
[gnulib.git] / lib / 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 #ifdef HAVE_CONFIG_H
22 #if defined (CONFIG_BROKETS)
23 /* We use <config.h> instead of "config.h" so that a compilation
24    using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
25    (which it would do because it found this file in $srcdir).  */
26 #include <config.h>
27 #else
28 #include "config.h"
29 #endif
30 #endif
31
32 #ifdef __GNUC__
33 #define alloca __builtin_alloca
34 #else
35 #ifdef HAVE_ALLOCA_H
36 #include <alloca.h>
37 #else
38 #ifdef _AIX
39  #pragma alloca
40 #else
41 char *alloca ();
42 #endif
43 #endif
44 #endif
45
46 #include <stdio.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52
53 #ifdef STAT_MACROS_BROKEN
54 #undef S_ISDIR
55 #endif /* STAT_MACROS_BROKEN.  */
56
57 #if !defined(S_ISDIR) && defined(S_IFDIR)
58 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
59 #endif
60
61 #ifdef STDC_HEADERS
62 #include <stdlib.h>
63 #endif
64
65 #ifdef HAVE_ERRNO_H
66 #include <errno.h>
67 #endif
68
69 #ifndef STDC_HEADERS
70 extern int errno;
71 #endif
72
73 #if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
74 #include <string.h>
75 #ifndef index
76 #define index strchr
77 #endif
78 #else
79 #include <strings.h>
80 #endif
81
82 #ifdef __MSDOS__
83 typedef int uid_t;
84 typedef int gid_t;
85 #endif
86
87 #include "safe-stat.h"
88 void error ();
89
90 /* Ensure that the directory ARGPATH exists.
91    Remove any trailing slashes from ARGPATH before calling this function.
92
93    Make any leading directories that don't already exist, with
94    permissions PARENT_MODE.
95    If the last element of ARGPATH does not exist, create it as
96    a new directory with permissions MODE.
97    If OWNER and GROUP are non-negative, make them the UID and GID of
98    created directories.
99    If VERBOSE_FMT_STRING is nonzero, use it as a printf format
100    string for printing a message after successfully making a directory,
101    with the name of the directory that was just made as an argument.
102
103    Return 0 if ARGPATH exists as a directory with the proper
104    ownership and permissions when done, otherwise 1.  */
105
106 int
107 make_path (argpath, mode, parent_mode, owner, group, verbose_fmt_string)
108      char *argpath;
109      int mode;
110      int parent_mode;
111      uid_t owner;
112      gid_t group;
113      char *verbose_fmt_string;
114 {
115   char *dirpath;                /* A copy we can scribble NULs on.  */
116   struct stat stats;
117   int retval = 0;
118   int oldmask = umask (0);
119
120   dirpath = (char *) alloca (strlen (argpath) + 1);
121   strcpy (dirpath, argpath);
122
123   if (SAFE_STAT (dirpath, &stats))
124     {
125       char *slash;
126       int tmp_mode;             /* Initial perms for leading dirs.  */
127       int re_protect;           /* Should leading dirs be unwritable? */
128       struct ptr_list
129       {
130         char *dirname_end;
131         struct ptr_list *next;
132       };
133       struct ptr_list *p, *leading_dirs = NULL;
134
135       /* If leading directories shouldn't be writable or executable,
136          or should have set[ug]id or sticky bits set and we are setting
137          their owners, we need to fix their permissions after making them.  */
138       if (((parent_mode & 0300) != 0300)
139           || (owner != (uid_t) -1 && group != (gid_t) -1
140               && (parent_mode & 07000) != 0))
141         {
142           tmp_mode = 0700;
143           re_protect = 1;
144         }
145       else
146         {
147           tmp_mode = parent_mode;
148           re_protect = 0;
149         }
150
151       slash = dirpath;
152       while (*slash == '/')
153         slash++;
154       while ((slash = index (slash, '/')))
155         {
156           *slash = '\0';
157           if (SAFE_STAT (dirpath, &stats))
158             {
159               if (mkdir (dirpath, tmp_mode))
160                 {
161                   error (0, errno, "cannot make directory `%s'", dirpath);
162                   umask (oldmask);
163                   return 1;
164                 }
165               else
166                 {
167                   if (verbose_fmt_string != NULL)
168                     error (0, 0, verbose_fmt_string, dirpath);
169
170                   if (owner != (uid_t) -1 && group != (gid_t) -1
171                       && chown (dirpath, owner, group)
172 #if defined(AFS) && defined (EPERM)
173                       && errno != EPERM
174 #endif
175                       )
176                     {
177                       error (0, errno, "%s", dirpath);
178                       retval = 1;
179                     }
180                   if (re_protect)
181                     {
182                       struct ptr_list *new = (struct ptr_list *)
183                         alloca (sizeof (struct ptr_list));
184                       new->dirname_end = slash;
185                       new->next = leading_dirs;
186                       leading_dirs = new;
187                     }
188                 }
189             }
190           else if (!S_ISDIR (stats.st_mode))
191             {
192               error (0, 0, "`%s' exists but is not a directory", dirpath);
193               umask (oldmask);
194               return 1;
195             }
196
197           *slash++ = '/';
198
199           /* Avoid unnecessary calls to `stat' when given
200              pathnames containing multiple adjacent slashes.  */
201           while (*slash == '/')
202             slash++;
203         }
204
205       /* We're done making leading directories.
206          Make the final component of the path.  */
207
208       /* The path could end in "/." or contain "/..", so test
209          if we really have to create the directory.  */
210
211       if (SAFE_STAT (dirpath, &stats) && mkdir (dirpath, mode))
212         {
213           error (0, errno, "cannot make directory `%s'", dirpath);
214           umask (oldmask);
215           return 1;
216         }
217       if (verbose_fmt_string != NULL)
218         error (0, 0, verbose_fmt_string, dirpath);
219
220       if (owner != (uid_t) -1 && group != (gid_t) -1)
221         {
222           if (chown (dirpath, owner, group)
223 #ifdef AFS
224               && errno != EPERM
225 #endif
226               )
227             {
228               error (0, errno, "%s", dirpath);
229               retval = 1;
230             }
231           /* chown may have turned off some permission bits we wanted.  */
232           if ((mode & 07000) != 0 && chmod (dirpath, mode))
233             {
234               error (0, errno, "%s", dirpath);
235               retval = 1;
236             }
237         }
238
239       /* If the mode for leading directories didn't include owner "wx"
240          privileges, we have to reset their protections to the correct
241          value.  */
242       for (p = leading_dirs; p != NULL; p = p->next)
243         {
244           *(p->dirname_end) = '\0';
245           if (chmod (dirpath, parent_mode))
246             {
247               error (0, errno, "%s", dirpath);
248               retval = 1;
249             }
250         }
251     }
252   else
253     {
254       /* We get here if the entire path already exists.  */
255
256       if (!S_ISDIR (stats.st_mode))
257         {
258           error (0, 0, "`%s' exists but is not a directory", dirpath);
259           umask (oldmask);
260           return 1;
261         }
262
263       /* chown must precede chmod because on some systems,
264          chown clears the set[ug]id bits for non-superusers,
265          resulting in incorrect permissions.
266          On System V, users can give away files with chown and then not
267          be able to chmod them.  So don't give files away.  */
268
269       if (owner != (uid_t) -1 && group != (gid_t) -1
270           && chown (dirpath, owner, group)
271 #ifdef AFS
272           && errno != EPERM
273 #endif
274           )
275         {
276           error (0, errno, "%s", dirpath);
277           retval = 1;
278         }
279       if (chmod (dirpath, mode))
280         {
281           error (0, errno, "%s", dirpath);
282           retval = 1;
283         }
284     }
285
286   umask (oldmask);
287   return retval;
288 }