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