.
[gnulib.git] / lib / makepath.c
1 /* makepath.c -- Ensure that a directory path exists.
2    Copyright (C) 1990, 1997, 1998, 1999, 2000 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 #ifndef S_IRWXUGO
54 # define S_IRWXUGO (S_IRWXU | S_IRWXG | S_IRWXO)
55 #endif
56
57 #if STDC_HEADERS
58 # include <stdlib.h>
59 #endif
60
61 #if HAVE_ERRNO_H
62 # include <errno.h>
63 #endif
64
65 #ifndef errno
66 extern int errno;
67 #endif
68
69 #if HAVE_STRING_H
70 # include <string.h>
71 #else
72 # include <strings.h>
73 # ifndef strchr
74 #  define strchr index
75 # endif
76 #endif
77
78 #ifndef S_ISUID
79 # define S_ISUID 04000
80 #endif
81 #ifndef S_ISGID
82 # define S_ISGID 02000
83 #endif
84 #ifndef S_ISVTX
85 # define S_ISVTX 01000
86 #endif
87 #ifndef S_IRUSR
88 # define S_IRUSR 0200
89 #endif
90 #ifndef S_IWUSR
91 # define S_IWUSR 0200
92 #endif
93 #ifndef S_IXUSR
94 # define S_IXUSR 0100
95 #endif
96
97 #ifndef S_IRWXU
98 # define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
99 #endif
100
101 #define WX_USR (S_IWUSR | S_IXUSR)
102
103 /* Include this before libintl.h so we get our definition of PARAMS. */
104 #include "makepath.h"
105
106 #if HAVE_LOCALE_H
107 # include <locale.h>
108 #endif
109
110 #if ENABLE_NLS
111 # include <libintl.h>
112 # define _(Text) gettext (Text)
113 #else
114 # define _(Text) Text
115 #endif
116
117 #ifdef __MSDOS__
118 typedef int uid_t;
119 typedef int gid_t;
120 #endif
121
122 #include "save-cwd.h"
123 #include "error.h"
124
125 void strip_trailing_slashes ();
126
127 #define CLEANUP_CWD                                     \
128   do                                                    \
129     {                                                   \
130       /* We're done operating on basename_dir.          \
131          Restore working directory.  */                 \
132       if (do_chdir)                                     \
133         {                                               \
134           int _fail = restore_cwd (&cwd, NULL, NULL);   \
135           free_cwd (&cwd);                              \
136           if (_fail)                                    \
137             return 1;                                   \
138         }                                               \
139     }                                                   \
140   while (0)
141
142 #define CLEANUP                                         \
143   do                                                    \
144     {                                                   \
145       umask (oldmask);                                  \
146       CLEANUP_CWD;                                      \
147     }                                                   \
148   while (0)
149
150 /* Attempt to create directory DIR (aka DIRPATH) with the specified MODE.
151    If CREATED_DIR_P is non-NULL, set *CREATED_DIR_P to non-zero if this
152    function creates DIR and to zero otherwise.  Give a diagnostic and
153    return non-zero if DIR cannot be created or cannot be determined to
154    exist already.  Use DIRPATH in any diagnostic, not DIR.
155    Note that if DIR already exists, this function will return zero
156    (indicating success) and will set *CREATED_DIR_P to zero.  */
157
158 static int
159 make_dir (const char *dir, const char *dirpath, mode_t mode, int *created_dir_p)
160 {
161   int fail = 0;
162   int created_dir;
163
164   created_dir = (mkdir (dir, mode) == 0);
165
166   if (!created_dir)
167     {
168       struct stat stats;
169       int saved_errno = errno;
170
171       /* The mkdir and stat calls below may appear to be reversed.
172          They are not.  It is important to call mkdir first and then to
173          call stat (to distinguish the three cases) only if mkdir fails.
174          The alternative to this approach is to `stat' each directory,
175          then to call mkdir if it doesn't exist.  But if some other process
176          were to create the directory between the stat & mkdir, the mkdir
177          would fail with EEXIST.  */
178
179       if (stat (dir, &stats))
180         {
181           error (0, saved_errno, _("cannot create directory `%s'"), dirpath);
182           fail = 1;
183         }
184       else if (!S_ISDIR (stats.st_mode))
185         {
186           error (0, 0, _("`%s' exists but is not a directory"), dirpath);
187           fail = 1;
188         }
189       else
190         {
191           /* DIR (aka DIRPATH) already exists and is a directory. */
192         }
193     }
194
195   if (created_dir_p)
196     *created_dir_p = created_dir;
197
198   return fail;
199 }
200
201 /* Ensure that the directory ARGPATH exists.
202    Remove any trailing slashes from ARGPATH before calling this function.
203
204    Create any leading directories that don't already exist, with
205    permissions PARENT_MODE.
206    If the last element of ARGPATH does not exist, create it as
207    a new directory with permissions MODE.
208    If OWNER and GROUP are non-negative, use them to set the UID and GID of
209    any created directories.
210    If VERBOSE_FMT_STRING is nonzero, use it as a printf format
211    string for printing a message after successfully making a directory,
212    with the name of the directory that was just made as an argument.
213    If PRESERVE_EXISTING is non-zero and ARGPATH is an existing directory,
214    then do not attempt to set its permissions and ownership.
215
216    Return 0 if ARGPATH exists as a directory with the proper
217    ownership and permissions when done, otherwise 1.  */
218
219 int
220 make_path (const char *argpath,
221            int mode,
222            int parent_mode,
223            uid_t owner,
224            gid_t group,
225            int preserve_existing,
226            const char *verbose_fmt_string)
227 {
228   struct stat stats;
229   int retval = 0;
230
231   if (stat (argpath, &stats))
232     {
233       char *slash;
234       int tmp_mode;             /* Initial perms for leading dirs.  */
235       int re_protect;           /* Should leading dirs be unwritable? */
236       struct ptr_list
237       {
238         char *dirname_end;
239         struct ptr_list *next;
240       };
241       struct ptr_list *p, *leading_dirs = NULL;
242       int do_chdir;             /* Whether to chdir before each mkdir.  */
243       struct saved_cwd cwd;
244       char *basename_dir;
245       char *dirpath;
246
247       /* Temporarily relax umask in case it's overly restrictive.  */
248       mode_t oldmask = umask (0);
249
250       /* Make a copy of ARGPATH that we can scribble NULs on.  */
251       dirpath = (char *) alloca (strlen (argpath) + 1);
252       strcpy (dirpath, argpath);
253       strip_trailing_slashes (dirpath);
254
255       /* If leading directories shouldn't be writable or executable,
256          or should have set[ug]id or sticky bits set and we are setting
257          their owners, we need to fix their permissions after making them.  */
258       if (((parent_mode & WX_USR) != WX_USR)
259           || ((owner != (uid_t) -1 || group != (gid_t) -1)
260               && (parent_mode & (S_ISUID | S_ISGID | S_ISVTX)) != 0))
261         {
262           tmp_mode = S_IRWXU;
263           re_protect = 1;
264         }
265       else
266         {
267           tmp_mode = parent_mode;
268           re_protect = 0;
269         }
270
271       /* If we can record the current working directory, we may be able
272          to do the chdir optimization.  */
273       do_chdir = !save_cwd (&cwd);
274
275       /* If we've saved the cwd and DIRPATH is an absolute pathname,
276          we must chdir to `/' in order to enable the chdir optimization.
277          So if chdir ("/") fails, turn off the optimization.  */
278       if (do_chdir && *dirpath == '/' && chdir ("/") < 0)
279         do_chdir = 0;
280
281       slash = dirpath;
282
283       /* Skip over leading slashes.  */
284       while (*slash == '/')
285         slash++;
286
287       while (1)
288         {
289           int newly_created_dir;
290           int fail;
291
292           /* slash points to the leftmost unprocessed component of dirpath.  */
293           basename_dir = slash;
294
295           slash = strchr (slash, '/');
296           if (slash == NULL)
297             break;
298
299           /* If we're *not* doing chdir before each mkdir, then we have to refer
300              to the target using the full (multi-component) directory name.  */
301           if (!do_chdir)
302             basename_dir = dirpath;
303
304           *slash = '\0';
305           fail = make_dir (basename_dir, dirpath, tmp_mode, &newly_created_dir);
306           if (fail)
307             {
308               CLEANUP;
309               return 1;
310             }
311
312           if (newly_created_dir)
313             {
314               if (verbose_fmt_string)
315                 error (0, 0, verbose_fmt_string, dirpath);
316
317               if ((owner != (uid_t) -1 || group != (gid_t) -1)
318                   && chown (basename_dir, owner, group)
319 #if defined(AFS) && defined (EPERM)
320                   && errno != EPERM
321 #endif
322                   )
323                 {
324                   error (0, errno, "%s", dirpath);
325                   CLEANUP;
326                   return 1;
327                 }
328
329               if (re_protect)
330                 {
331                   struct ptr_list *new = (struct ptr_list *)
332                     alloca (sizeof (struct ptr_list));
333                   new->dirname_end = slash;
334                   new->next = leading_dirs;
335                   leading_dirs = new;
336                 }
337             }
338
339           /* If we were able to save the initial working directory,
340              then we can use chdir to change into each directory before
341              creating an entry in that directory.  This avoids making
342              stat and mkdir process O(n^2) file name components.  */
343           if (do_chdir && chdir (basename_dir) < 0)
344             {
345               error (0, errno, _("cannot chdir to directory, %s"), dirpath);
346               CLEANUP;
347               return 1;
348             }
349
350           *slash++ = '/';
351
352           /* Avoid unnecessary calls to `stat' when given
353              pathnames containing multiple adjacent slashes.  */
354           while (*slash == '/')
355             slash++;
356         }
357
358       if (!do_chdir)
359         basename_dir = dirpath;
360
361       /* We're done making leading directories.
362          Create the final component of the path.  */
363
364       if (make_dir (basename_dir, dirpath, mode, NULL))
365         {
366           CLEANUP;
367           return 1;
368         }
369
370       /* Done creating directories.  Restore original umask.  */
371       umask (oldmask);
372
373       if (verbose_fmt_string != NULL)
374         error (0, 0, verbose_fmt_string, dirpath);
375
376       if (owner != (uid_t) -1 || group != (gid_t) -1)
377         {
378           if (chown (basename_dir, owner, group)
379 #ifdef AFS
380               && errno != EPERM
381 #endif
382               )
383             {
384               error (0, errno, _("cannot chown %s"), dirpath);
385               retval = 1;
386             }
387         }
388
389       /* The above chown may have turned off some permission bits in MODE.
390          Another reason we may have to use chmod here is that mkdir(2) is
391          required to honor only the file permission bits.  In particular,
392          it need not honor the `special' bits, so if MODE includes any
393          special bits, set them here.  */
394       if ((mode & ~S_IRWXUGO)
395           && chmod (basename_dir, mode))
396         {
397           error (0, errno, _("cannot chmod %s"), dirpath);
398           retval = 1;
399         }
400
401       CLEANUP_CWD;
402
403       /* If the mode for leading directories didn't include owner "wx"
404          privileges, we have to reset their protections to the correct
405          value.  */
406       for (p = leading_dirs; p != NULL; p = p->next)
407         {
408           *(p->dirname_end) = '\0';
409           if (chmod (dirpath, parent_mode))
410             {
411               error (0, errno, "%s", dirpath);
412               retval = 1;
413             }
414         }
415     }
416   else
417     {
418       /* We get here if the entire path already exists.  */
419
420       const char *dirpath = argpath;
421
422       if (!S_ISDIR (stats.st_mode))
423         {
424           error (0, 0, _("`%s' exists but is not a directory"), dirpath);
425           return 1;
426         }
427
428       if (!preserve_existing)
429         {
430           /* chown must precede chmod because on some systems,
431              chown clears the set[ug]id bits for non-superusers,
432              resulting in incorrect permissions.
433              On System V, users can give away files with chown and then not
434              be able to chmod them.  So don't give files away.  */
435
436           if ((owner != (uid_t) -1 || group != (gid_t) -1)
437               && chown (dirpath, owner, group)
438 #ifdef AFS
439               && errno != EPERM
440 #endif
441               )
442             {
443               error (0, errno, "%s", dirpath);
444               retval = 1;
445             }
446           if (chmod (dirpath, mode))
447             {
448               error (0, errno, "%s", dirpath);
449               retval = 1;
450             }
451         }
452     }
453
454   return retval;
455 }