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