(libfu_a_SOURCES): Add oa-hash.c.
[gnulib.git] / lib / makepath.c
1 /* makepath.c -- Ensure that a directory path exists.
2    Copyright (C) 1990, 1997 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 Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> and Jim Meyering.  */
19
20 #if HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #if __GNUC__
25 # define alloca __builtin_alloca
26 #else
27 # if HAVE_ALLOCA_H
28 #  include <alloca.h>
29 # else
30 #  ifdef _AIX
31  #  pragma alloca
32 #  else
33 char *alloca ();
34 #  endif
35 # endif
36 #endif
37
38 #include <stdio.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #if HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44
45 #if STAT_MACROS_BROKEN
46 # undef S_ISDIR
47 #endif
48
49 #if !defined(S_ISDIR) && defined(S_IFDIR)
50 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
51 #endif
52
53 #if STDC_HEADERS
54 # include <stdlib.h>
55 #endif
56
57 #if HAVE_ERRNO_H
58 # include <errno.h>
59 #endif
60
61 #ifndef errno
62 extern int errno;
63 #endif
64
65 #if HAVE_STRING_H
66 # include <string.h>
67 #else
68 # include <strings.h>
69 # ifndef strchr
70 #  define strchr index
71 # endif
72 #endif
73
74 #ifdef __MSDOS__
75 typedef int uid_t;
76 typedef int gid_t;
77 #endif
78
79 #include "save-cwd.h"
80 #include "makepath.h"
81 #include "error.h"
82
83 void strip_trailing_slashes ();
84
85 #define CLEANUP_CWD                                     \
86   do                                                    \
87     {                                                   \
88       /* We're done operating on basename_dir.          \
89          Restore working directory.  */                 \
90       if (do_chdir)                                     \
91         {                                               \
92           int fail = restore_cwd (&cwd, NULL, NULL);    \
93           free_cwd (&cwd);                              \
94           if (fail)                                     \
95             return 1;                                   \
96         }                                               \
97     }                                                   \
98   while (0)
99
100 #define CLEANUP                                         \
101   do                                                    \
102     {                                                   \
103       umask (oldmask);                                  \
104       CLEANUP_CWD;                                      \
105     }                                                   \
106   while (0)
107
108 /* Ensure that the directory ARGPATH exists.
109    Remove any trailing slashes from ARGPATH before calling this function.
110
111    Create any leading directories that don't already exist, with
112    permissions PARENT_MODE.
113    If the last element of ARGPATH does not exist, create it as
114    a new directory with permissions MODE.
115    If OWNER and GROUP are non-negative, use them to set the UID and GID of
116    any created directories.
117    If VERBOSE_FMT_STRING is nonzero, use it as a printf format
118    string for printing a message after successfully making a directory,
119    with the name of the directory that was just made as an argument.
120    If PRESERVE_EXISTING is non-zero and ARGPATH is an existing directory,
121    then do not attempt to set its permissions and ownership.
122
123    Return 0 if ARGPATH exists as a directory with the proper
124    ownership and permissions when done, otherwise 1.  */
125
126 #if __STDC__
127 int
128 make_path (const char *argpath,
129            int mode,
130            int parent_mode,
131            uid_t owner,
132            gid_t group,
133            int preserve_existing,
134            const char *verbose_fmt_string)
135 #else
136 int
137 make_path (argpath, mode, parent_mode, owner, group, preserve_existing,
138            verbose_fmt_string)
139      const char *argpath;
140      int mode;
141      int parent_mode;
142      uid_t owner;
143      gid_t group;
144      int preserve_existing;
145      const char *verbose_fmt_string;
146 #endif
147 {
148   struct stat stats;
149   int retval = 0;
150
151   if (stat (argpath, &stats))
152     {
153       char *slash;
154       int tmp_mode;             /* Initial perms for leading dirs.  */
155       int re_protect;           /* Should leading dirs be unwritable? */
156       struct ptr_list
157       {
158         char *dirname_end;
159         struct ptr_list *next;
160       };
161       struct ptr_list *p, *leading_dirs = NULL;
162       int do_chdir;             /* Whether to chdir before each mkdir.  */
163       struct saved_cwd cwd;
164       char *basename_dir;
165       char *dirpath;
166
167       /* Temporarily relax umask in case it's overly restrictive.  */
168       int oldmask = umask (0);
169
170       /* Make a copy of ARGPATH that we can scribble NULs on.  */
171       dirpath = (char *) alloca (strlen (argpath) + 1);
172       strcpy (dirpath, argpath);
173       strip_trailing_slashes (dirpath);
174
175       /* If leading directories shouldn't be writable or executable,
176          or should have set[ug]id or sticky bits set and we are setting
177          their owners, we need to fix their permissions after making them.  */
178       if (((parent_mode & 0300) != 0300)
179           || (owner != (uid_t) -1 && group != (gid_t) -1
180               && (parent_mode & 07000) != 0))
181         {
182           tmp_mode = 0700;
183           re_protect = 1;
184         }
185       else
186         {
187           tmp_mode = parent_mode;
188           re_protect = 0;
189         }
190
191       /* If we can record the current working directory, we may be able
192          to do the chdir optimization.  */
193       do_chdir = !save_cwd (&cwd);
194
195       /* If we've saved the cwd and DIRPATH is an absolute pathname,
196          we must chdir to `/' in order to enable the chdir optimization.
197          So if chdir ("/") fails, turn off the optimization.  */
198       if (do_chdir && *dirpath == '/' && chdir ("/") < 0)
199         do_chdir = 0;
200
201       slash = dirpath;
202
203       /* Skip over leading slashes.  */
204       while (*slash == '/')
205         slash++;
206
207       while (1)
208         {
209           /* slash points to the leftmost unprocessed component of dirpath.  */
210           basename_dir = slash;
211
212           slash = strchr (slash, '/');
213           if (slash == NULL)
214             break;
215
216           /* If we're *not* doing chdir before each mkdir, then we have to refer
217              to the target using the full (multi-component) directory name.  */
218           if (!do_chdir)
219             basename_dir = dirpath;
220
221           *slash = '\0';
222           if (mkdir (basename_dir, tmp_mode))
223             {
224               if (stat (basename_dir, &stats))
225                 {
226                   error (0, errno, "cannot create directory `%s'", dirpath);
227                   CLEANUP;
228                   return 1;
229                 }
230               else if (!S_ISDIR (stats.st_mode))
231                 {
232                   error (0, 0, "`%s' exists but is not a directory", dirpath);
233                   CLEANUP;
234                   return 1;
235                 }
236               else
237                 {
238                   /* DIRPATH already exists and is a directory. */
239                 }
240             }
241
242           if (verbose_fmt_string != NULL)
243             error (0, 0, verbose_fmt_string, dirpath);
244
245           if (owner != (uid_t) -1 && group != (gid_t) -1
246               && chown (basename_dir, owner, group)
247 #if defined(AFS) && defined (EPERM)
248               && errno != EPERM
249 #endif
250               )
251             {
252               error (0, errno, "%s", dirpath);
253               CLEANUP;
254               return 1;
255             }
256
257           if (re_protect)
258             {
259               struct ptr_list *new = (struct ptr_list *)
260                 alloca (sizeof (struct ptr_list));
261               new->dirname_end = slash;
262               new->next = leading_dirs;
263               leading_dirs = new;
264             }
265
266           /* If we were able to save the initial working directory,
267              then we can use chdir to change into each directory before
268              creating an entry in that directory.  This avoids making
269              stat and mkdir process O(n^2) file name components.  */
270           if (do_chdir && chdir (basename_dir) < 0)
271             {
272               error (0, errno, "cannot chdir to directory, %s", dirpath);
273               CLEANUP;
274               return 1;
275             }
276
277           *slash++ = '/';
278
279           /* Avoid unnecessary calls to `stat' when given
280              pathnames containing multiple adjacent slashes.  */
281           while (*slash == '/')
282             slash++;
283         }
284
285       if (!do_chdir)
286         basename_dir = dirpath;
287
288       /* We're done making leading directories.
289          Create the final component of the path.  */
290
291       /* The path could end in "/." or contain "/..", so test
292          if we really have to create the directory.  */
293
294       if (stat (basename_dir, &stats) && mkdir (basename_dir, mode))
295         {
296           error (0, errno, "cannot create directory `%s'", dirpath);
297           CLEANUP;
298           return 1;
299         }
300
301       /* Done creating directories.  Restore original umask.  */
302       umask (oldmask);
303
304       if (verbose_fmt_string != NULL)
305         error (0, 0, verbose_fmt_string, dirpath);
306
307       if (owner != (uid_t) -1 && group != (gid_t) -1)
308         {
309           if (chown (basename_dir, owner, group)
310 #ifdef AFS
311               && errno != EPERM
312 #endif
313               )
314             {
315               error (0, errno, "cannot chown %s", dirpath);
316               retval = 1;
317             }
318           /* chown may have turned off some permission bits we wanted.  */
319           if ((mode & 07000) != 0 && chmod (basename_dir, mode))
320             {
321               error (0, errno, "cannot chmod %s", dirpath);
322               retval = 1;
323             }
324         }
325
326       CLEANUP_CWD;
327
328       /* If the mode for leading directories didn't include owner "wx"
329          privileges, we have to reset their protections to the correct
330          value.  */
331       for (p = leading_dirs; p != NULL; p = p->next)
332         {
333           *(p->dirname_end) = '\0';
334           if (chmod (dirpath, parent_mode))
335             {
336               error (0, errno, "%s", dirpath);
337               retval = 1;
338             }
339         }
340     }
341   else
342     {
343       /* We get here if the entire path already exists.  */
344
345       const char *dirpath = argpath;
346
347       if (!S_ISDIR (stats.st_mode))
348         {
349           error (0, 0, "`%s' exists but is not a directory", dirpath);
350           return 1;
351         }
352
353       if (!preserve_existing)
354         {
355           /* chown must precede chmod because on some systems,
356              chown clears the set[ug]id bits for non-superusers,
357              resulting in incorrect permissions.
358              On System V, users can give away files with chown and then not
359              be able to chmod them.  So don't give files away.  */
360
361           if (owner != (uid_t) -1 && group != (gid_t) -1
362               && chown (dirpath, owner, group)
363 #ifdef AFS
364               && errno != EPERM
365 #endif
366               )
367             {
368               error (0, errno, "%s", dirpath);
369               retval = 1;
370             }
371           if (chmod (dirpath, mode))
372             {
373               error (0, errno, "%s", dirpath);
374               retval = 1;
375             }
376         }
377     }
378
379   return retval;
380 }