1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2007-2011 Free Software Foundation Europe e.V.
5 Copyright (C) 2016-2016 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22
23 /*
24 * Kern Sibbald, September MMVII
25 */
26
27 /*
28 * This is tricky code, especially when writing from scratch. Fortunately,
29 * a non-copyrighted version of mkdir was available to consult.
30 *
31 * ***FIXME*** the mkpath code could be significantly optimized by
32 * walking up the path chain from the bottom until it either gets
33 * to the top or finds an existing directory then walk back down
34 * creating the path components. Currently, it always starts at
35 * the top, which can be rather inefficient for long path names.
36 */
37 #include "include/bareos.h"
38 #include "include/jcr.h"
39 #include "lib/path_list.h"
40
41 #define debuglevel 50
42
43 /**
44 * * For old systems that don't have lchown() use chown()
45 * */
46 #ifndef HAVE_LCHOWN
47 #define lchown chown
48 #endif
49
50 /**
51 * * For old systems that don't have lchmod() use chmod()
52 * */
53 #ifndef HAVE_LCHMOD
54 #define lchmod chmod
55 #endif
56
makedir(JobControlRecord * jcr,char * path,mode_t mode,int * created)57 static bool makedir(JobControlRecord *jcr, char *path, mode_t mode, int *created)
58 {
59 struct stat statp;
60
61 if (mkdir(path, mode) != 0) {
62 BErrNo be;
63 *created = false;
64 if (stat(path, &statp) != 0) {
65 Jmsg2(jcr, M_ERROR, 0, _("Cannot create directory %s: ERR=%s\n"),
66 path, be.bstrerror());
67 return false;
68 } else if (!S_ISDIR(statp.st_mode)) {
69 Jmsg1(jcr, M_ERROR, 0, _("%s exists but is not a directory.\n"), path);
70 return false;
71 }
72 return true; /* directory exists */
73 }
74
75 if (jcr->keep_path_list) {
76 /*
77 * When replace = NEVER, we keep track of all directories newly created
78 */
79 if (!jcr->path_list) {
80 jcr->path_list = path_list_init();
81 }
82
83 PathListAdd(jcr->path_list, strlen(path), path);
84 }
85
86 *created = true;
87 return true;
88 }
89
SetOwnMod(Attributes * attr,char * path,uid_t owner,gid_t group,mode_t mode)90 static void SetOwnMod(Attributes *attr, char *path, uid_t owner, gid_t group, mode_t mode)
91 {
92 if (lchown(path, owner, group) != 0 && attr->uid == 0
93 #ifdef AFS
94 && errno != EPERM
95 #endif
96 ) {
97 BErrNo be;
98 Jmsg2(attr->jcr, M_WARNING, 0, _("Cannot change owner and/or group of %s: ERR=%s\n"),
99 path, be.bstrerror());
100 }
101 #if defined(HAVE_WIN32)
102 if (win32_chmod(path, mode, 0) != 0 && attr->uid == 0) {
103 #else
104 if (lchmod(path, mode) != 0 && attr->uid == 0) {
105 #endif
106 BErrNo be;
107 Jmsg2(attr->jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"),
108 path, be.bstrerror());
109 }
110 }
111
112 /**
113 * mode is the mode bits to use in creating a new directory
114 * parent_mode are the parent's modes if we need to create parent directories.
115 * owner and group are to set on any created dirs
116 * keep_dir_modes if set means don't change mode bits if dir exists
117 */
118 bool makepath(Attributes *attr, const char *apath, mode_t mode, mode_t parent_mode,
119 uid_t owner, gid_t group, bool keep_dir_modes)
120 {
121 struct stat statp;
122 mode_t omask, tmode;
123 char *path = (char *)apath;
124 char *p;
125 int len;
126 bool ok = false;
127 int created;
128 char new_dir[5000];
129 int ndir = 0;
130 int i = 0;
131 int max_dirs = (int)sizeof(new_dir);
132 JobControlRecord *jcr = attr->jcr;
133
134 if (stat(path, &statp) == 0) { /* Does dir exist? */
135 if (!S_ISDIR(statp.st_mode)) {
136 Jmsg1(jcr, M_ERROR, 0, _("%s exists but is not a directory.\n"), path);
137 return false;
138 }
139 /* Full path exists */
140 if (keep_dir_modes) {
141 return true;
142 }
143 SetOwnMod(attr, path, owner, group, mode);
144 return true;
145 }
146 omask = umask(0);
147 umask(omask);
148 len = strlen(apath);
149 path = (char *)alloca(len+1);
150 bstrncpy(path, apath, len+1);
151 StripTrailingSlashes(path);
152 /*
153 * Now for one of the complexities. If we are not running as root,
154 * then if the parent_mode does not have wx user perms, or we are
155 * setting the userid or group, and the parent_mode has setuid, setgid,
156 * or sticky bits, we must create the dir with open permissions, then
157 * go back and patch all the dirs up with the correct perms.
158 * Solution, set everything to 0777, then go back and reset them at the
159 * end.
160 */
161 tmode = 0777;
162
163 #if defined(HAVE_WIN32)
164 /*
165 * Validate drive letter
166 */
167 if (path[1] == ':') {
168 char drive[4] = "X:\\";
169
170 drive[0] = path[0];
171
172 UINT drive_type = GetDriveType(drive);
173
174 if (drive_type == DRIVE_UNKNOWN || drive_type == DRIVE_NO_ROOT_DIR) {
175 Jmsg1(jcr, M_ERROR, 0, _("%c: is not a valid drive.\n"), path[0]);
176 goto bail_out;
177 }
178
179 if (path[2] == '\0') { /* attempt to create a drive */
180 ok = true;
181 goto bail_out; /* OK, it is already there */
182 }
183
184 p = &path[3];
185 } else {
186 p = path;
187 }
188 #else
189 p = path;
190 #endif
191
192 /*
193 * Skip leading slash(es)
194 */
195 while (IsPathSeparator(*p)) {
196 p++;
197 }
198 while ((p = first_path_separator(p))) {
199 char save_p;
200 save_p = *p;
201 *p = 0;
202 if (!makedir(jcr, path, tmode, &created)) {
203 goto bail_out;
204 }
205 if (ndir < max_dirs) {
206 new_dir[ndir++] = created;
207 }
208 *p = save_p;
209 while (IsPathSeparator(*p)) {
210 p++;
211 }
212 }
213 /*
214 * Create final component if not a junction/symlink
215 */
216 if(attr->type != FT_JUNCTION ){
217 if (!makedir(jcr, path, tmode, &created)) {
218 goto bail_out;
219 }
220 }
221
222 if (ndir < max_dirs) {
223 new_dir[ndir++] = created;
224 }
225 if (ndir >= max_dirs) {
226 Jmsg0(jcr, M_WARNING, 0, _("Too many subdirectories. Some permissions not reset.\n"));
227 }
228
229 /*
230 * Now set the proper owner and modes
231 */
232 #if defined(HAVE_WIN32)
233
234 /*
235 * Don't propagate the hidden attribute to parent directories
236 */
237 parent_mode &= ~S_ISVTX;
238
239 if (path[1] == ':') {
240 p = &path[3];
241 } else {
242 p = path;
243 }
244 #else
245 p = path;
246 #endif
247 /*
248 * Skip leading slash(es)
249 */
250 while (IsPathSeparator(*p)) {
251 p++;
252 }
253 while ((p = first_path_separator(p))) {
254 char save_p;
255 save_p = *p;
256 *p = 0;
257 if (i < ndir && new_dir[i++] && !keep_dir_modes) {
258 SetOwnMod(attr, path, owner, group, parent_mode);
259 }
260 *p = save_p;
261 while (IsPathSeparator(*p)) {
262 p++;
263 }
264 }
265
266 /*
267 * Set for final component
268 */
269 if (i < ndir && new_dir[i++] && !keep_dir_modes) {
270 SetOwnMod(attr, path, owner, group, mode);
271 }
272
273 ok = true;
274 bail_out:
275 umask(omask);
276 return ok;
277 }
278