2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2015 Kern Sibbald
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.
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.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
21 * Kern Sibbald, September MMVII
23 * This is tricky code, especially when writing from scratch. Fortunately,
24 * a non-copyrighted version of mkdir was available to consult.
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.
39 * For old systems that don't have lchown() or lchmod()
49 typedef struct PrivateCurDir {
54 /* Initialize the path hash table */
55 static bool path_list_init(JCR *jcr)
58 jcr->path_list = (htable *)malloc(sizeof(htable));
60 /* Hard to know in advance how many directories will
61 * be stored in this hash
63 jcr->path_list->init(elt, &elt->link, 10000);
67 /* Add a path to the hash when we create a directory
68 * with the replace=NEVER option
70 bool path_list_add(JCR *jcr, uint32_t len, char *fname)
75 if (!jcr->path_list) {
79 /* we store CurDir, fname in the same chunk */
80 item = (CurDir *)jcr->path_list->hash_malloc(sizeof(CurDir)+len+1);
82 memset(item, 0, sizeof(CurDir));
83 memcpy(item->fname, fname, len+1);
85 jcr->path_list->insert(item->fname, item);
87 Dmsg1(dbglvl, "add fname=<%s>\n", fname);
91 void free_path_list(JCR *jcr)
94 jcr->path_list->destroy();
96 jcr->path_list = NULL;
100 bool path_list_lookup(JCR *jcr, char *fname)
105 if (!jcr->path_list) {
109 /* Strip trailing / */
110 int len = strlen(fname);
116 if (fname[len] == '/') { /* strip any trailing slash */
120 CurDir *temp = (CurDir *)jcr->path_list->lookup(fname);
125 Dmsg2(dbglvl, "lookup <%s> %s\n", fname, found?"ok":"not ok");
127 fname[len] = bkp; /* restore last / */
131 static bool makedir(JCR *jcr, char *path, mode_t mode, int *created)
135 if (mkdir(path, mode) != 0) {
138 if (lstat(path, &statp) != 0) {
139 Jmsg2(jcr, M_ERROR, 0, _("Cannot create directory %s: ERR=%s\n"),
140 path, be.bstrerror());
142 } else if (!S_ISDIR(statp.st_mode)) {
143 Jmsg1(jcr, M_ERROR, 0, _("%s exists but is not a directory.\n"), path);
146 return true; /* directory exists */
149 /* TODO: This code rely on statp that is not initialized, we need to do a stat() */
150 if (S_ISLNK(statp.st_mode)) {
152 * Note, we created a directory, not a link, so if we find a
153 * link, there is a security problem here.
155 Jmsg1(jcr, M_FATAL, 0, _("Security problem!! We created directory %s, but it is a link.\n"),
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);
170 * Restore the owner and permissions (mode) of a Directory.
171 * See attribs.c for the equivalent for files.
173 static void set_own_mod(ATTR *attr, char *path, uid_t owner, gid_t group, mode_t mode)
175 if (lchown(path, owner, group) != 0 && attr->uid == 0
181 Jmsg2(attr->jcr, M_WARNING, 0, _("Cannot change owner and/or group of %s: ERR=%s\n"),
182 path, be.bstrerror());
184 if (lchmod(path, mode) != 0 && attr->uid == 0) {
186 Jmsg2(attr->jcr, M_WARNING, 0, _("Cannot change permissions of %s: ERR=%s\n"),
187 path, be.bstrerror());
192 * mode is the mode bits to use in creating a new directory
194 * parent_mode are the parent's modes if we need to create parent
197 * owner and group are to set on any created dirs
199 * keep_dir_modes if set means don't change mode bits if dir exists
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)
206 char *path = (char *)apath;
214 int max_dirs = (int)sizeof(new_dir);
215 JCR *jcr = attr->jcr;
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);
222 /* Full path exists */
223 if (keep_dir_modes) {
226 set_own_mod(attr, path, owner, group, mode);
232 path = (char *)alloca(len+1);
233 bstrncpy(path, apath, len+1);
234 strip_trailing_slashes(path);
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
248 /* Skip leading slash(es) */
249 while (IsPathSeparator(*p)) {
252 while ((p = first_path_separator(p))) {
256 if (!makedir(jcr, path, tmode, &created)) {
259 if (ndir < max_dirs) {
260 new_dir[ndir++] = created;
263 while (IsPathSeparator(*p)) {
267 /* Create final component */
268 if (!makedir(jcr, path, tmode, &created)) {
271 if (ndir < max_dirs) {
272 new_dir[ndir++] = created;
274 if (ndir >= max_dirs) {
275 Jmsg0(jcr, M_WARNING, 0, _("Too many subdirectories. Some permissions not reset.\n"));
278 /* Now set the proper owner and modes */
280 /* Skip leading slash(es) */
281 while (IsPathSeparator(*p)) {
284 while ((p = first_path_separator(p))) {
288 if (i < ndir && new_dir[i++] && !keep_dir_modes) {
289 set_own_mod(attr, path, owner, group, parent_mode);
292 while (IsPathSeparator(*p)) {
297 /* Set for final component */
298 if (i < ndir && new_dir[i++]) {
299 set_own_mod(attr, path, owner, group, mode);