2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
17 * Directory tree build/traverse routines
19 * Kern Sibbald, June MMII
25 #include "findlib/find.h"
27 #define B_PAGE_SIZE 4096
28 #define MAX_PAGES 2400
29 #define MAX_BUF_SIZE (MAX_PAGES * B_PAGE_SIZE) /* approx 10MB */
31 /* Forward referenced subroutines */
32 static TREE_NODE *search_and_insert_tree_node(char *fname, int type,
33 TREE_ROOT *root, TREE_NODE *parent);
34 static char *tree_alloc(TREE_ROOT *root, int size);
37 * NOTE !!!!! we turn off Debug messages for performance reasons.
45 #define Dmsg2(n,f,a1,a2)
46 #define Dmsg3(n,f,a1,a2,a3)
49 * This subroutine gets a big buffer.
51 static void malloc_buf(TREE_ROOT *root, int size)
55 mem = (struct s_mem *)malloc(size);
56 root->total_size += size;
58 mem->next = root->mem;
60 mem->mem = mem->first;
61 mem->rem = (char *)mem + size - mem->mem;
62 Dmsg2(200, "malloc buf size=%d rem=%d\n", size, mem->rem);
67 * Note, we allocate a big buffer in the tree root
68 * from which we allocate nodes. This runs more
69 * than 100 times as fast as directly using malloc()
70 * for each of the nodes.
72 TREE_ROOT *new_tree(int count)
77 if (count < 1000) { /* minimum tree size */
80 root = (TREE_ROOT *)malloc(sizeof(TREE_ROOT));
81 memset(root, 0, sizeof(TREE_ROOT));
82 /* Assume filename + node = 40 characters average length */
83 size = count * (BALIGN(sizeof(TREE_NODE)) + 40);
84 if (count > 1000000 || size > (MAX_BUF_SIZE / 2)) {
87 Dmsg2(400, "count=%d size=%d\n", count, size);
88 malloc_buf(root, size);
89 root->cached_path_len = -1;
90 root->cached_path = get_pool_memory(PM_FNAME);
93 HL_ENTRY* entry = NULL;
94 root->hardlinks.init(entry, &entry->link, 0, 1);
99 * Create a new tree node.
101 static TREE_NODE *new_tree_node(TREE_ROOT *root)
104 int size = sizeof(TREE_NODE);
105 node = (TREE_NODE *)tree_alloc(root, size);
106 memset(node, 0, size);
107 node->delta_seq = -1;
112 * This routine can be called to release the
113 * previously allocated tree node.
115 static void free_tree_node(TREE_ROOT *root)
117 int asize = BALIGN(sizeof(TREE_NODE));
118 root->mem->rem += asize;
119 root->mem->mem -= asize;
122 void tree_remove_node(TREE_ROOT *root, TREE_NODE *node)
124 int asize = BALIGN(sizeof(TREE_NODE));
125 node->parent->child.remove(node);
126 if ((root->mem->mem - asize) == (char *)node) {
127 free_tree_node(root);
129 Dmsg0(0, "Can't release tree node\n");
134 * Allocate bytes for filename in tree structure.
135 * Keep the pointers properly aligned by allocating
136 * sizes that are aligned.
138 static char *tree_alloc(TREE_ROOT *root, int size)
141 int asize = BALIGN(size);
143 if (root->mem->rem < asize) {
145 if (root->total_size >= (MAX_BUF_SIZE / 2)) {
146 mb_size = MAX_BUF_SIZE;
148 mb_size = MAX_BUF_SIZE / 2;
150 malloc_buf(root, mb_size);
152 root->mem->rem -= asize;
153 buf = root->mem->mem;
154 root->mem->mem += asize;
159 /* This routine frees the whole tree */
160 void free_tree(TREE_ROOT *root)
162 struct s_mem *mem, *rel;
163 uint32_t freed_blocks = 0;
165 root->hardlinks.destroy();
166 for (mem=root->mem; mem; ) {
172 if (root->cached_path) {
173 free_pool_memory(root->cached_path);
174 root->cached_path = NULL;
176 Dmsg3(100, "Total size=%u blocks=%u freed_blocks=%u\n", root->total_size, root->blocks, freed_blocks);
178 garbage_collect_memory();
182 /* Add Delta part for this node */
183 void tree_add_delta_part(TREE_ROOT *root, TREE_NODE *node,
184 JobId_t JobId, int32_t FileIndex)
186 struct delta_list *elt =
187 (struct delta_list*) tree_alloc(root, sizeof(struct delta_list));
189 elt->next = node->delta_list;
191 elt->FileIndex = FileIndex;
192 node->delta_list = elt;
196 * Insert a node in the tree. This is the main subroutine
197 * called when building a tree.
200 TREE_NODE *insert_tree_node(char *path, char *fname, int type,
201 TREE_ROOT *root, TREE_NODE *parent)
204 int path_len = strlen(path);
207 Dmsg1(100, "insert_tree_node: %s\n", path);
209 * If trailing slash on path, strip it
212 q = path + path_len - 1;
213 if (IsPathSeparator(*q)) {
214 *q = 0; /* strip trailing slash */
216 q = NULL; /* no trailing slash */
219 q = NULL; /* no trailing slash */
221 /* If no filename, strip last component of path as "filename" */
223 p = (char *)last_path_separator(path); /* separate path and filename */
225 fname = p + 1; /* set new filename */
226 *p = '\0'; /* terminate new path */
232 if (!parent) { /* if no parent, we need to make one */
233 Dmsg1(100, "make_tree_path for %s\n", path);
234 path_len = strlen(path); /* get new length */
235 if (path_len == root->cached_path_len &&
236 strcmp(path, root->cached_path) == 0) {
237 parent = root->cached_parent;
239 root->cached_path_len = path_len;
240 pm_strcpy(&root->cached_path, path);
241 parent = make_tree_path(path, root);
242 root->cached_parent = parent;
244 Dmsg1(100, "parent=%s\n", parent->fname);
249 parent = (TREE_NODE *)root;
252 Dmsg1(100, "No / found: %s\n", path);
255 node = search_and_insert_tree_node(fname, 0, root, parent);
256 if (q) { /* if trailing slash on entry */
257 *q = '/'; /* restore it */
259 if (p) { /* if slash in path trashed */
260 *p = '/'; /* restore full path */
266 * Ensure that all appropriate nodes for a full path exist in
269 TREE_NODE *make_tree_path(char *path, TREE_ROOT *root)
271 TREE_NODE *parent, *node;
273 int type = TN_NEWDIR;
275 Dmsg1(100, "make_tree_path: %s\n", path);
277 Dmsg0(100, "make_tree_path: parent=*root*\n");
278 return (TREE_NODE *)root;
280 p = (char *)last_path_separator(path); /* get last dir component of path */
283 *p = 0; /* terminate path */
284 parent = make_tree_path(path, root);
285 *p = '/'; /* restore full name */
288 parent = (TREE_NODE *)root;
291 node = search_and_insert_tree_node(fname, type, root, parent);
295 static int node_compare(void *item1, void *item2)
297 TREE_NODE *tn1 = (TREE_NODE *)item1;
298 TREE_NODE *tn2 = (TREE_NODE *)item2;
299 if (tn1->fname[0] > tn2->fname[0]) {
301 } else if (tn1->fname[0] < tn2->fname[0]) {
304 return strcmp(tn1->fname, tn2->fname);
308 * See if the fname already exists. If not insert a new node for it.
310 static TREE_NODE *search_and_insert_tree_node(char *fname, int type,
311 TREE_ROOT *root, TREE_NODE *parent)
313 TREE_NODE *node, *found_node;
314 node = new_tree_node(root);
316 found_node = (TREE_NODE *)parent->child.insert(node, node_compare);
317 if (found_node != node) { /* already in list */
318 free_tree_node(root); /* free node allocated above */
319 found_node->inserted = false;
322 /* It was not found, but is now inserted */
323 node->fname_len = strlen(fname);
324 node->fname = tree_alloc(root, node->fname_len + 1);
325 strcpy(node->fname, fname);
326 node->parent = parent;
329 /* Maintain a linear chain of nodes */
334 root->last->next = node;
337 node->inserted = true; /* inserted into tree */
342 int tree_getpath(TREE_NODE *node, char *buf, int buf_size)
348 tree_getpath(node->parent, buf, buf_size);
350 * Fixup for Win32. If we have a Win32 directory and
351 * there is only a / in the buffer, remove it since
352 * win32 names don't generally start with /
354 if (node->type == TN_DIR_NLS && IsPathSeparator(buf[0]) && buf[1] == '\0') {
357 bstrncat(buf, node->fname, buf_size);
358 /* Add a slash for all directories unless we are at the root,
359 * also add a slash to a soft linked file if it has children
360 * i.e. it is linked to a directory.
362 if ((node->type != TN_FILE && !(IsPathSeparator(buf[0]) && buf[1] == '\0')) ||
363 (node->soft_link && tree_node_has_child(node))) {
364 bstrncat(buf, "/", buf_size);
370 * Change to specified directory
372 TREE_NODE *tree_cwd(char *path, TREE_ROOT *root, TREE_NODE *node)
374 if (path[0] == '.' && path[1] == '\0') {
377 /* Handle relative path */
378 if (path[0] == '.' && path[1] == '.' && (IsPathSeparator(path[2]) || path[2] == '\0')) {
379 TREE_NODE *parent = node->parent ? node->parent : node;
383 return tree_cwd(path+3, root, parent);
386 if (IsPathSeparator(path[0])) {
387 Dmsg0(100, "Doing absolute lookup.\n");
388 return tree_relcwd(path+1, root, (TREE_NODE *)root);
390 Dmsg0(100, "Doing relative lookup.\n");
391 return tree_relcwd(path, root, node);
396 * Do a relative cwd -- i.e. relative to current node rather than root node
398 TREE_NODE *tree_relcwd(char *path, TREE_ROOT *root, TREE_NODE *node)
409 /* Check the current segment only */
410 if ((p = first_path_separator(path)) != NULL) {
415 Dmsg2(100, "tree_relcwd: len=%d path=%s\n", len, path);
416 foreach_child(cd, node) {
417 Dmsg1(100, "tree_relcwd: test cd=%s\n", cd->fname);
418 if (cd->fname[0] == path[0] && len == (int)strlen(cd->fname)
419 && strncmp(cd->fname, path, len) == 0) {
422 /* fnmatch has no len in call so we truncate the string */
423 save_char = path[len];
425 match = fnmatch(path, cd->fname, 0) == 0;
426 path[len] = save_char;
431 if (!cd || (cd->type == TN_FILE && !tree_node_has_child(cd))) {
435 Dmsg0(100, "tree_relcwd: no more to lookup. found.\n");
438 Dmsg2(100, "recurse tree_relcwd with path=%s, cd=%s\n", p+1, cd->fname);
439 /* Check the next segment if any */
440 return tree_relcwd(p+1, root, cd);
445 #ifdef BUILD_TEST_PROGRAM
447 void FillDirectoryTree(char *path, TREE_ROOT *root, TREE_NODE *parent);
449 static uint32_t FileIndex = 0;
451 * Simple test program for tree routines
453 int main(int argc, char *argv[])
457 char buf[MAXPATHLEN];
460 root->fname = tree_alloc(root, 1);
464 FillDirectoryTree("/home/kern/bacula/k", root, NULL);
466 for (node = first_tree_node(root); node; node=next_tree_node(node)) {
467 tree_getpath(node, buf, sizeof(buf));
468 Dmsg2(100, "%d: %s\n", node->FileIndex, buf);
471 node = (TREE_NODE *)root;
472 Pmsg0(000, "doing cd /home/kern/bacula/k/techlogs\n");
473 node = tree_cwd("/home/kern/bacula/k/techlogs", root, node);
475 tree_getpath(node, buf, sizeof(buf));
476 Dmsg2(100, "findex=%d: cwd=%s\n", node->FileIndex, buf);
479 Pmsg0(000, "doing cd /home/kern/bacula/k/src/testprogs\n");
480 node = tree_cwd("/home/kern/bacula/k/src/testprogs", root, node);
482 tree_getpath(node, buf, sizeof(buf));
483 Dmsg2(100, "findex=%d: cwd=%s\n", node->FileIndex, buf);
485 Dmsg0(100, "testprogs not found.\n");
488 free_tree((TREE_NODE *)root);
493 void FillDirectoryTree(char *path, TREE_ROOT *root, TREE_NODE *parent)
495 TREE_NODE *newparent = NULL;
500 char pathbuf[MAXPATHLEN];
501 char file[MAXPATHLEN];
505 Dmsg1(100, "FillDirectoryTree: %s\n", path);
510 while ((dir = readdir(dp))) {
511 if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) {
514 bstrncpy(file, dir->d_name, sizeof(file));
515 snprintf(pathbuf, MAXPATHLEN-1, "%s/%s", path, file);
516 if (lstat(pathbuf, &statbuf) < 0) {
518 printf("lstat() failed. ERR=%s\n", be.bstrerror(errno));
521 // printf("got file=%s, pathbuf=%s\n", file, pathbuf);
523 if (S_ISLNK(statbuf.st_mode))
524 type = TN_FILE; /* link */
525 else if (S_ISREG(statbuf.st_mode))
527 else if (S_ISDIR(statbuf.st_mode)) {
529 } else if (S_ISCHR(statbuf.st_mode))
530 type = TN_FILE; /* char dev */
531 else if (S_ISBLK(statbuf.st_mode))
532 type = TN_FILE; /* block dev */
533 else if (S_ISFIFO(statbuf.st_mode))
534 type = TN_FILE; /* fifo */
535 else if (S_ISSOCK(statbuf.st_mode))
536 type = TN_FILE; /* sock */
539 printf("Unknown file type: 0x%x\n", statbuf.st_mode);
542 Dmsg2(100, "Doing: %d %s\n", type, pathbuf);
543 node = new_tree_node(root);
544 node->FileIndex = ++FileIndex;
545 parent = insert_tree_node(pathbuf, node, root, parent);
546 if (S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) {
547 Dmsg2(100, "calling fill. pathbuf=%s, file=%s\n", pathbuf, file);
548 FillDirectoryTree(pathbuf, root, node);
555 #define MAXPATHLEN 2000
558 void print_tree(char *path, TREE_NODE *tree)
560 char buf[MAXPATHLEN];
566 switch (tree->type) {
578 Pmsg3(-1, "%s/%s%s\n", path, tree->fname, termchr);
579 switch (tree->type) {
584 bsnprintf(buf, sizeof(buf), "%s/%s", path, tree->fname);
585 print_tree(buf, first_child(tree));
588 print_tree(path, first_child(tree));
591 Pmsg1(000, "Unknown node type %d\n", tree->type);
593 print_tree(path, tree->sibling_);