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