2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2016 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.
20 * Directory tree build/traverse routines
22 * Kern Sibbald, June MMII
28 #include "findlib/find.h"
30 #define B_PAGE_SIZE 4096
31 #define MAX_PAGES 2400
32 #define MAX_BUF_SIZE (MAX_PAGES * B_PAGE_SIZE) /* approx 10MB */
34 /* Forward referenced subroutines */
35 static TREE_NODE *search_and_insert_tree_node(char *fname, int type,
36 TREE_ROOT *root, TREE_NODE *parent);
37 static char *tree_alloc(TREE_ROOT *root, int size);
40 * NOTE !!!!! we turn off Debug messages for performance reasons.
48 #define Dmsg2(n,f,a1,a2)
49 #define Dmsg3(n,f,a1,a2,a3)
52 * This subroutine gets a big buffer.
54 static void malloc_buf(TREE_ROOT *root, int size)
58 mem = (struct s_mem *)malloc(size);
59 root->total_size += size;
61 mem->next = root->mem;
63 mem->mem = mem->first;
64 mem->rem = (char *)mem + size - mem->mem;
65 Dmsg2(200, "malloc buf size=%d rem=%d\n", size, mem->rem);
70 * Note, we allocate a big buffer in the tree root
71 * from which we allocate nodes. This runs more
72 * than 100 times as fast as directly using malloc()
73 * for each of the nodes.
75 TREE_ROOT *new_tree(int count)
80 if (count < 1000) { /* minimum tree size */
83 root = (TREE_ROOT *)malloc(sizeof(TREE_ROOT));
84 memset(root, 0, sizeof(TREE_ROOT));
85 /* Assume filename + node = 40 characters average length */
86 size = count * (BALIGN(sizeof(TREE_NODE)) + 40);
87 if (count > 1000000 || size > (MAX_BUF_SIZE / 2)) {
90 Dmsg2(400, "count=%d size=%d\n", count, size);
91 malloc_buf(root, size);
92 root->cached_path_len = -1;
93 root->cached_path = get_pool_memory(PM_FNAME);
96 HL_ENTRY* entry = NULL;
97 root->hardlinks.init(entry, &entry->link, 0);
102 * Create a new tree node.
104 static TREE_NODE *new_tree_node(TREE_ROOT *root)
107 int size = sizeof(TREE_NODE);
108 node = (TREE_NODE *)tree_alloc(root, size);
109 memset(node, 0, size);
110 node->delta_seq = -1;
115 * This routine can be called to release the
116 * previously allocated tree node.
118 static void free_tree_node(TREE_ROOT *root)
120 int asize = BALIGN(sizeof(TREE_NODE));
121 root->mem->rem += asize;
122 root->mem->mem -= asize;
125 void tree_remove_node(TREE_ROOT *root, TREE_NODE *node)
127 int asize = BALIGN(sizeof(TREE_NODE));
128 node->parent->child.remove(node);
129 if ((root->mem->mem - asize) == (char *)node) {
130 free_tree_node(root);
132 Dmsg0(0, "Can't release tree node\n");
137 * Allocate bytes for filename in tree structure.
138 * Keep the pointers properly aligned by allocating
139 * sizes that are aligned.
141 static char *tree_alloc(TREE_ROOT *root, int size)
144 int asize = BALIGN(size);
146 if (root->mem->rem < asize) {
148 if (root->total_size >= (MAX_BUF_SIZE / 2)) {
149 mb_size = MAX_BUF_SIZE;
151 mb_size = MAX_BUF_SIZE / 2;
153 malloc_buf(root, mb_size);
155 root->mem->rem -= asize;
156 buf = root->mem->mem;
157 root->mem->mem += asize;
162 /* This routine frees the whole tree */
163 void free_tree(TREE_ROOT *root)
165 struct s_mem *mem, *rel;
166 uint32_t freed_blocks = 0;
168 root->hardlinks.destroy();
169 for (mem=root->mem; mem; ) {
175 if (root->cached_path) {
176 free_pool_memory(root->cached_path);
177 root->cached_path = NULL;
179 Dmsg3(100, "Total size=%u blocks=%u freed_blocks=%u\n", root->total_size, root->blocks, freed_blocks);
181 garbage_collect_memory();
185 /* Add Delta part for this node */
186 void tree_add_delta_part(TREE_ROOT *root, TREE_NODE *node,
187 JobId_t JobId, int32_t FileIndex)
189 struct delta_list *elt =
190 (struct delta_list*) tree_alloc(root, sizeof(struct delta_list));
192 elt->next = node->delta_list;
194 elt->FileIndex = FileIndex;
195 node->delta_list = elt;
199 * Insert a node in the tree. This is the main subroutine
200 * called when building a tree.
203 TREE_NODE *insert_tree_node(char *path, char *fname, int type,
204 TREE_ROOT *root, TREE_NODE *parent)
207 int path_len = strlen(path);
210 Dmsg1(100, "insert_tree_node: %s\n", path);
212 * If trailing slash on path, strip it
215 q = path + path_len - 1;
216 if (IsPathSeparator(*q)) {
217 *q = 0; /* strip trailing slash */
219 q = NULL; /* no trailing slash */
222 q = NULL; /* no trailing slash */
224 /* If no filename, strip last component of path as "filename" */
226 p = (char *)last_path_separator(path); /* separate path and filename */
228 fname = p + 1; /* set new filename */
229 *p = '\0'; /* terminate new path */
235 if (!parent) { /* if no parent, we need to make one */
236 Dmsg1(100, "make_tree_path for %s\n", path);
237 path_len = strlen(path); /* get new length */
238 if (path_len == root->cached_path_len &&
239 strcmp(path, root->cached_path) == 0) {
240 parent = root->cached_parent;
242 root->cached_path_len = path_len;
243 pm_strcpy(&root->cached_path, path);
244 parent = make_tree_path(path, root);
245 root->cached_parent = parent;
247 Dmsg1(100, "parent=%s\n", parent->fname);
252 parent = (TREE_NODE *)root;
255 Dmsg1(100, "No / found: %s\n", path);
258 node = search_and_insert_tree_node(fname, 0, root, parent);
259 if (q) { /* if trailing slash on entry */
260 *q = '/'; /* restore it */
262 if (p) { /* if slash in path trashed */
263 *p = '/'; /* restore full path */
269 * Ensure that all appropriate nodes for a full path exist in
272 TREE_NODE *make_tree_path(char *path, TREE_ROOT *root)
274 TREE_NODE *parent, *node;
276 int type = TN_NEWDIR;
278 Dmsg1(100, "make_tree_path: %s\n", path);
280 Dmsg0(100, "make_tree_path: parent=*root*\n");
281 return (TREE_NODE *)root;
283 p = (char *)last_path_separator(path); /* get last dir component of path */
286 *p = 0; /* terminate path */
287 parent = make_tree_path(path, root);
288 *p = '/'; /* restore full name */
291 parent = (TREE_NODE *)root;
294 node = search_and_insert_tree_node(fname, type, root, parent);
298 static int node_compare(void *item1, void *item2)
300 TREE_NODE *tn1 = (TREE_NODE *)item1;
301 TREE_NODE *tn2 = (TREE_NODE *)item2;
302 if (tn1->fname[0] > tn2->fname[0]) {
304 } else if (tn1->fname[0] < tn2->fname[0]) {
307 return strcmp(tn1->fname, tn2->fname);
311 * See if the fname already exists. If not insert a new node for it.
313 static TREE_NODE *search_and_insert_tree_node(char *fname, int type,
314 TREE_ROOT *root, TREE_NODE *parent)
316 TREE_NODE *node, *found_node;
317 node = new_tree_node(root);
319 found_node = (TREE_NODE *)parent->child.insert(node, node_compare);
320 if (found_node != node) { /* already in list */
321 free_tree_node(root); /* free node allocated above */
322 found_node->inserted = false;
325 /* It was not found, but is now inserted */
326 node->fname_len = strlen(fname);
327 node->fname = tree_alloc(root, node->fname_len + 1);
328 strcpy(node->fname, fname);
329 node->parent = parent;
332 /* Maintain a linear chain of nodes */
337 root->last->next = node;
340 node->inserted = true; /* inserted into tree */
345 int tree_getpath(TREE_NODE *node, char *buf, int buf_size)
351 tree_getpath(node->parent, buf, buf_size);
353 * Fixup for Win32. If we have a Win32 directory and
354 * there is only a / in the buffer, remove it since
355 * win32 names don't generally start with /
357 if (node->type == TN_DIR_NLS && IsPathSeparator(buf[0]) && buf[1] == '\0') {
360 bstrncat(buf, node->fname, buf_size);
361 /* Add a slash for all directories unless we are at the root,
362 * also add a slash to a soft linked file if it has children
363 * i.e. it is linked to a directory.
365 if ((node->type != TN_FILE && !(IsPathSeparator(buf[0]) && buf[1] == '\0')) ||
366 (node->soft_link && tree_node_has_child(node))) {
367 bstrncat(buf, "/", buf_size);
373 * Change to specified directory
375 TREE_NODE *tree_cwd(char *path, TREE_ROOT *root, TREE_NODE *node)
377 if (path[0] == '.' && path[1] == '\0') {
380 /* Handle relative path */
381 if (path[0] == '.' && path[1] == '.' && (IsPathSeparator(path[2]) || path[2] == '\0')) {
382 TREE_NODE *parent = node->parent ? node->parent : node;
386 return tree_cwd(path+3, root, parent);
389 if (IsPathSeparator(path[0])) {
390 Dmsg0(100, "Doing absolute lookup.\n");
391 return tree_relcwd(path+1, root, (TREE_NODE *)root);
393 Dmsg0(100, "Doing relative lookup.\n");
394 return tree_relcwd(path, root, node);
399 * Do a relative cwd -- i.e. relative to current node rather than root node
401 TREE_NODE *tree_relcwd(char *path, TREE_ROOT *root, TREE_NODE *node)
412 /* Check the current segment only */
413 if ((p = first_path_separator(path)) != NULL) {
418 Dmsg2(100, "tree_relcwd: len=%d path=%s\n", len, path);
419 foreach_child(cd, node) {
420 Dmsg1(100, "tree_relcwd: test cd=%s\n", cd->fname);
421 if (cd->fname[0] == path[0] && len == (int)strlen(cd->fname)
422 && strncmp(cd->fname, path, len) == 0) {
425 /* fnmatch has no len in call so we truncate the string */
426 save_char = path[len];
428 match = fnmatch(path, cd->fname, 0) == 0;
429 path[len] = save_char;
434 if (!cd || (cd->type == TN_FILE && !tree_node_has_child(cd))) {
438 Dmsg0(100, "tree_relcwd: no more to lookup. found.\n");
441 Dmsg2(100, "recurse tree_relcwd with path=%s, cd=%s\n", p+1, cd->fname);
442 /* Check the next segment if any */
443 return tree_relcwd(p+1, root, cd);
448 #ifdef BUILD_TEST_PROGRAM
450 void FillDirectoryTree(char *path, TREE_ROOT *root, TREE_NODE *parent);
452 static uint32_t FileIndex = 0;
454 * Simple test program for tree routines
456 int main(int argc, char *argv[])
460 char buf[MAXPATHLEN];
463 root->fname = tree_alloc(root, 1);
467 FillDirectoryTree("/home/kern/bacula/k", root, NULL);
469 for (node = first_tree_node(root); node; node=next_tree_node(node)) {
470 tree_getpath(node, buf, sizeof(buf));
471 Dmsg2(100, "%d: %s\n", node->FileIndex, buf);
474 node = (TREE_NODE *)root;
475 Pmsg0(000, "doing cd /home/kern/bacula/k/techlogs\n");
476 node = tree_cwd("/home/kern/bacula/k/techlogs", root, node);
478 tree_getpath(node, buf, sizeof(buf));
479 Dmsg2(100, "findex=%d: cwd=%s\n", node->FileIndex, buf);
482 Pmsg0(000, "doing cd /home/kern/bacula/k/src/testprogs\n");
483 node = tree_cwd("/home/kern/bacula/k/src/testprogs", root, node);
485 tree_getpath(node, buf, sizeof(buf));
486 Dmsg2(100, "findex=%d: cwd=%s\n", node->FileIndex, buf);
488 Dmsg0(100, "testprogs not found.\n");
491 free_tree((TREE_NODE *)root);
496 void FillDirectoryTree(char *path, TREE_ROOT *root, TREE_NODE *parent)
498 TREE_NODE *newparent = NULL;
503 char pathbuf[MAXPATHLEN];
504 char file[MAXPATHLEN];
508 Dmsg1(100, "FillDirectoryTree: %s\n", path);
513 while ((dir = readdir(dp))) {
514 if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) {
517 bstrncpy(file, dir->d_name, sizeof(file));
518 snprintf(pathbuf, MAXPATHLEN-1, "%s/%s", path, file);
519 if (lstat(pathbuf, &statbuf) < 0) {
521 printf("lstat() failed. ERR=%s\n", be.bstrerror(errno));
524 // printf("got file=%s, pathbuf=%s\n", file, pathbuf);
526 if (S_ISLNK(statbuf.st_mode))
527 type = TN_FILE; /* link */
528 else if (S_ISREG(statbuf.st_mode))
530 else if (S_ISDIR(statbuf.st_mode)) {
532 } else if (S_ISCHR(statbuf.st_mode))
533 type = TN_FILE; /* char dev */
534 else if (S_ISBLK(statbuf.st_mode))
535 type = TN_FILE; /* block dev */
536 else if (S_ISFIFO(statbuf.st_mode))
537 type = TN_FILE; /* fifo */
538 else if (S_ISSOCK(statbuf.st_mode))
539 type = TN_FILE; /* sock */
542 printf("Unknown file type: 0x%x\n", statbuf.st_mode);
545 Dmsg2(100, "Doing: %d %s\n", type, pathbuf);
546 node = new_tree_node(root);
547 node->FileIndex = ++FileIndex;
548 parent = insert_tree_node(pathbuf, node, root, parent);
549 if (S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) {
550 Dmsg2(100, "calling fill. pathbuf=%s, file=%s\n", pathbuf, file);
551 FillDirectoryTree(pathbuf, root, node);
558 #define MAXPATHLEN 2000
561 void print_tree(char *path, TREE_NODE *tree)
563 char buf[MAXPATHLEN];
569 switch (tree->type) {
581 Pmsg3(-1, "%s/%s%s\n", path, tree->fname, termchr);
582 switch (tree->type) {
587 bsnprintf(buf, sizeof(buf), "%s/%s", path, tree->fname);
588 print_tree(buf, first_child(tree));
591 print_tree(path, first_child(tree));
594 Pmsg1(000, "Unknown node type %d\n", tree->type);
596 print_tree(path, tree->sibling_);