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