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