]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/mkpath.c
884ba10083590a583456cd2151299715f08a8332
[bacula/bacula] / bacula / src / findlib / mkpath.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2011 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28
29 /*
30  *  Kern Sibbald, September MMVII
31  * 
32  *  This is tricky code, especially when writing from scratch. Fortunately,
33  *    a non-copyrighted version of mkdir was available to consult.
34  *
35  * ***FIXME*** the mkpath code could be significantly optimized by
36  *   walking up the path chain from the bottom until it either gets
37  *   to the top or finds an existing directory then walk back down
38  *   creating the path components.  Currently, it always starts at
39  *   the top, which can be rather inefficient for long path names.
40  *
41  */
42 #include "bacula.h"
43 #include "jcr.h"
44
45 #define dbglvl 50
46
47 typedef struct PrivateCurDir {
48    hlink link;
49    char fname[1];
50 } CurDir;
51
52 /* Initialize the path hash table */
53 static bool path_list_init(JCR *jcr)
54 {
55    CurDir *elt = NULL;
56    jcr->path_list = (htable *)malloc(sizeof(htable));
57
58    /* Hard to know in advance how many directories will
59     * be stored in this hash
60     */
61    jcr->path_list->init(elt, &elt->link, 10000);
62    return true;
63 }
64
65 /* Add a path to the hash when we create a directory
66  * with the replace=NEVER option
67  */
68 bool path_list_add(JCR *jcr, uint32_t len, char *fname)
69 {
70    bool ret = true;
71    CurDir *item;
72
73    if (!jcr->path_list) {
74       path_list_init(jcr);
75    }
76
77    /* we store CurDir, fname in the same chunk */
78    item = (CurDir *)jcr->path_list->hash_malloc(sizeof(CurDir)+len+1);
79    
80    memset(item, 0, sizeof(CurDir));
81    memcpy(item->fname, fname, len+1);
82
83    jcr->path_list->insert(item->fname, item); 
84
85    Dmsg1(dbglvl, "add fname=<%s>\n", fname);
86    return ret;
87 }
88
89 void free_path_list(JCR *jcr)
90 {
91    if (jcr->path_list) {
92       jcr->path_list->destroy();
93       free(jcr->path_list);
94       jcr->path_list = NULL;
95    }
96 }
97
98 bool path_list_lookup(JCR *jcr, char *fname)
99 {
100    bool found=false;
101    char bkp;
102
103    if (!jcr->path_list) {
104       return false;
105    }
106
107    /* Strip trailing / */
108    int len = strlen(fname);
109    if (len == 0) {
110       return false;
111    }
112    len--;
113    bkp = fname[len];
114    if (fname[len] == '/') {       /* strip any trailing slash */
115       fname[len] = 0;
116    }
117
118    CurDir *temp = (CurDir *)jcr->path_list->lookup(fname);
119    if (temp) {
120       found=true;
121    }
122
123    Dmsg2(dbglvl, "lookup <%s> %s\n", fname, found?"ok":"not ok");
124
125    fname[len] = bkp;            /* restore last / */
126    return found;
127 }
128
129 static bool makedir(JCR *jcr, char *path, mode_t mode, int *created)
130 {
131    struct stat statp;
132
133    if (mkdir(path, mode) != 0) {
134       berrno be;
135       *created = false;
136       if (stat(path, &statp) != 0) {
137          Jmsg2(jcr, M_ERROR, 0, _("Cannot create directory %s: ERR=%s\n"),
138               path, be.bstrerror());
139          return false;
140       } else if (!S_ISDIR(statp.st_mode)) {
141          Jmsg1(jcr, M_ERROR, 0, _("%s exists but is not a directory.\n"), path);
142          return false;
143       }
144       return true;                 /* directory exists */
145    }
146
147    if (jcr->keep_path_list) {
148       /* When replace=NEVER, we keep track of all directories newly created */
149       path_list_add(jcr, strlen(path), path);
150    }
151
152    *created = true;
153    return true;
154 }
155
156 static void set_own_mod(ATTR *attr, char *path, uid_t owner, gid_t group, mode_t mode)
157 {
158    if (chown(path, owner, group) != 0 && attr->uid == 0
159 #ifdef AFS
160         && errno != EPERM
161 #endif
162    ) {
163       berrno be;
164       Jmsg2(attr->jcr, M_WARNING, 0, _("Cannot change owner and/or group of %s: ERR=%s\n"),
165            path, be.bstrerror());
166    }
167    if (chmod(path, mode) != 0 && attr->uid == 0) {
168       berrno be;
169       Jmsg2(attr->jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"),
170            path, be.bstrerror());
171    }
172 }
173
174 /*
175  * mode is the mode bits to use in creating a new directory
176  *
177  * parent_mode are the parent's modes if we need to create parent 
178  *    directories.
179  *
180  * owner and group are to set on any created dirs
181  *
182  * keep_dir_modes if set means don't change mode bits if dir exists
183  */
184 bool makepath(ATTR *attr, const char *apath, mode_t mode, mode_t parent_mode,
185             uid_t owner, gid_t group, int keep_dir_modes)
186 {
187    struct stat statp;
188    mode_t omask, tmode;
189    char *path = (char *)apath;
190    char *p;
191    int len;
192    bool ok = false;
193    int created;
194    char new_dir[5000];
195    int ndir = 0;
196    int i = 0;
197    int max_dirs = (int)sizeof(new_dir);
198    JCR *jcr = attr->jcr;
199
200    if (stat(path, &statp) == 0) {     /* Does dir exist? */
201       if (!S_ISDIR(statp.st_mode)) {
202          Jmsg1(jcr, M_ERROR, 0, _("%s exists but is not a directory.\n"), path);
203          return false;
204       }
205       /* Full path exists */
206       if (keep_dir_modes) {
207          return true;
208       }
209       set_own_mod(attr, path, owner, group, mode);
210       return true;
211    }
212    omask = umask(0);
213    umask(omask);
214    len = strlen(apath);
215    path = (char *)alloca(len+1);
216    bstrncpy(path, apath, len+1);
217    strip_trailing_slashes(path);
218    /*
219     * Now for one of the complexities. If we are not running as root,
220     *  then if the parent_mode does not have wx user perms, or we are
221     *  setting the userid or group, and the parent_mode has setuid, setgid,
222     *  or sticky bits, we must create the dir with open permissions, then
223     *  go back and patch all the dirs up with the correct perms.
224     * Solution, set everything to 0777, then go back and reset them at the
225     *  end.
226     */
227    tmode = 0777;
228
229 #if defined(HAVE_WIN32)
230    /* Validate drive letter */
231    if (path[1] == ':') {
232       char drive[4] = "X:\\";
233
234       drive[0] = path[0];
235
236       UINT drive_type = GetDriveType(drive);
237
238       if (drive_type == DRIVE_UNKNOWN || drive_type == DRIVE_NO_ROOT_DIR) {
239          Jmsg1(jcr, M_ERROR, 0, _("%c: is not a valid drive.\n"), path[0]);
240          goto bail_out;
241       }
242
243       if (path[2] == '\0') {          /* attempt to create a drive */
244          ok = true;
245          goto bail_out;               /* OK, it is already there */
246       }
247
248       p = &path[3];
249    } else {
250       p = path;
251    }
252 #else
253    p = path;
254 #endif
255
256    /* Skip leading slash(es) */
257    while (IsPathSeparator(*p)) {
258       p++;
259    }
260    while ((p = first_path_separator(p))) {
261       char save_p;
262       save_p = *p;
263       *p = 0;
264       if (!makedir(jcr, path, tmode, &created)) {
265          goto bail_out;
266       }
267       if (ndir < max_dirs) {
268          new_dir[ndir++] = created;
269       }
270       *p = save_p;
271       while (IsPathSeparator(*p)) {
272          p++;
273       }
274    }
275    /* Create final component */
276    if (!makedir(jcr, path, tmode, &created)) {
277       goto bail_out;
278    }
279    if (ndir < max_dirs) {
280       new_dir[ndir++] = created;
281    }
282    if (ndir >= max_dirs) {
283       Jmsg0(jcr, M_WARNING, 0, _("Too many subdirectories. Some permissions not reset.\n"));
284    }
285
286    /* Now set the proper owner and modes */
287 #if defined(HAVE_WIN32)
288    if (path[1] == ':') {
289       p = &path[3];
290    } else {
291       p = path;
292    }
293 #else
294    p = path;
295 #endif
296    /* Skip leading slash(es) */
297    while (IsPathSeparator(*p)) {
298       p++;
299    }
300    while ((p = first_path_separator(p))) {
301       char save_p;
302       save_p = *p;
303       *p = 0;
304       if (i < ndir && new_dir[i++] && !keep_dir_modes) {
305          set_own_mod(attr, path, owner, group, parent_mode);
306       }
307       *p = save_p;
308       while (IsPathSeparator(*p)) {
309          p++;
310       }
311    }
312
313    /* Set for final component */
314    if (i < ndir && new_dir[i++]) {
315       set_own_mod(attr, path, owner, group, mode);
316    }
317
318    ok = true;
319 bail_out:
320    umask(omask);
321    return ok;
322 }