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