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