* mkdir-p.c: Include "dirchownmod.h", not "dirchownmod.c".
[gnulib.git] / lib / mkdir-p.c
1 /* mkdir-p.c -- Ensure that a directory and its parents exist.
2
3    Copyright (C) 1990, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006
4    Free 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19
20 /* Written by Paul Eggert, David MacKenzie, and Jim Meyering.  */
21
22 #include <config.h>
23
24 #include "mkdir-p.h"
25
26 #include <errno.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29
30 #include "gettext.h"
31 #define _(msgid) gettext (msgid)
32
33 #include "dirchownmod.h"
34 #include "dirname.h"
35 #include "error.h"
36 #include "quote.h"
37 #include "mkancesdirs.h"
38 #include "savewd.h"
39 #include "stat-macros.h"
40
41 /* Ensure that the directory DIR exists.
42
43    WD is the working directory, as in savewd.c.
44
45    If MAKE_ANCESTOR is not null, create any ancestor directories that
46    don't already exist, by invoking MAKE_ANCESTOR (ANCESTOR, OPTIONS).
47    This function should return zero if successful, -1 (setting errno)
48    otherwise.  In this case, DIR may be modified by storing '\0' bytes
49    into it, to access the ancestor directories, and this modification
50    is retained on return if the ancestor directories could not be
51    created.
52
53    Create DIR as a new directory with using mkdir with permissions
54    MODE.  It is also OK if MAKE_ANCESTOR is not null and a
55    directory DIR already exists.
56
57    Call ANNOUNCE (DIR, OPTIONS) just after successfully making DIR,
58    even if some of the following actions fail.
59
60    Set DIR's owner to OWNER and group to GROUP, but leave the owner
61    alone if OWNER is (uid_t) -1, and similarly for GROUP.
62
63    Set DIR's mode bits to MODE, except preserve any of the bits that
64    correspond to zero bits in MODE_BITS.  In other words, MODE_BITS is
65    a mask that specifies which of DIR's mode bits should be set or
66    cleared.  MODE should be a subset of MODE_BITS, which in turn
67    should be a subset of CHMOD_MODE_BITS.  Changing the mode in this
68    way is necessary if DIR already existed or if MODE and MODE_BITS
69    specify non-permissions bits like S_ISUID.
70
71    However, if PRESERVE_EXISTING is true and DIR already exists,
72    do not attempt to set DIR's ownership and file mode bits.
73
74    This implementation assumes the current umask is zero.
75
76    Return true if DIR exists as a directory with the proper ownership
77    and file mode bits when done, or if a child process has been
78    dispatched to do the real work (though the child process may not
79    have finished yet -- it is the caller's responsibility to handle
80    this).  Report a diagnostic and return false on failure, storing
81    '\0' into *DIR if an ancestor directory had problems.  */
82
83 bool
84 make_dir_parents (char *dir,
85                   struct savewd *wd,
86                   int (*make_ancestor) (char const *, void *),
87                   void *options,
88                   mode_t mode,
89                   void (*announce) (char const *, void *),
90                   mode_t mode_bits,
91                   uid_t owner,
92                   gid_t group,
93                   bool preserve_existing)
94 {
95   int mkdir_errno = (IS_ABSOLUTE_FILE_NAME (dir) ? 0 : savewd_errno (wd));
96
97   if (mkdir_errno == 0)
98     {
99       ptrdiff_t prefix_len = 0;
100       int savewd_chdir_options = (HAVE_FCHMOD ? SAVEWD_CHDIR_SKIP_READABLE : 0);
101
102       if (make_ancestor)
103         {
104           prefix_len = mkancesdirs (dir, wd, make_ancestor, options);
105           if (prefix_len < 0)
106             {
107               if (prefix_len < -1)
108                 return true;
109               mkdir_errno = errno;
110             }
111         }
112
113       if (0 <= prefix_len)
114         {
115           if (mkdir (dir + prefix_len, mode) == 0)
116             {
117               announce (dir, options);
118               preserve_existing =
119                 (owner == (uid_t) -1 && group == (gid_t) -1
120                  && ! ((mode_bits & (S_ISUID | S_ISGID)) | (mode & S_ISVTX)));
121               savewd_chdir_options |=
122                 (SAVEWD_CHDIR_NOFOLLOW
123                  | (mode & S_IRUSR ? SAVEWD_CHDIR_READABLE : 0));
124             }
125           else
126             mkdir_errno = errno;
127
128           if (preserve_existing)
129             {
130               struct stat st;
131               if (mkdir_errno == 0
132                   || (mkdir_errno != ENOENT && make_ancestor
133                       && stat (dir + prefix_len, &st) == 0
134                       && S_ISDIR (st.st_mode)))
135                 return true;
136             }
137           else
138             {
139               int open_result[2];
140               int chdir_result =
141                 savewd_chdir (wd, dir + prefix_len,
142                               savewd_chdir_options, open_result);
143               if (chdir_result < -1)
144                 return true;
145               else
146                 {
147                   bool chdir_ok = (chdir_result == 0);
148                   int chdir_errno = errno;
149                   int fd = open_result[0];
150                   bool chdir_failed_unexpectedly =
151                     (mkdir_errno == 0
152                      && ((! chdir_ok && (mode & S_IXUSR))
153                          || (fd < 0 && (mode & S_IRUSR))));
154
155                   if (chdir_failed_unexpectedly)
156                     {
157                       /* No need to save errno here; it's irrelevant.  */
158                       if (0 <= fd)
159                         close (fd);
160                     }
161                   else
162                     {
163                       mode_t mkdir_mode = (mkdir_errno == 0 ? mode : -1);
164                       char const *subdir = (chdir_ok ? "." : dir + prefix_len);
165                       if (dirchownmod (fd, subdir, mkdir_mode, owner, group,
166                                        mode, mode_bits)
167                           == 0)
168                         return true;
169                     }
170
171                   if (mkdir_errno == 0
172                       || (mkdir_errno != ENOENT && make_ancestor
173                           && errno != ENOTDIR))
174                     {
175                       error (0,
176                              (! chdir_failed_unexpectedly ? errno
177                               : ! chdir_ok && (mode & S_IXUSR) ? chdir_errno
178                               : open_result[1]),
179                              _(owner == (uid_t) -1 && group == (gid_t) -1
180                                ? "cannot change permissions of %s"
181                                : "cannot change owner and permissions of %s"),
182                              quote (dir));
183                       return false;
184                     }
185                 }
186             }
187         }
188     }
189
190   error (0, mkdir_errno, _("cannot create directory %s"), quote (dir));
191   return false;
192 }