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