1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2015-2015 Planets Communications B.V.
5 Copyright (C) 2015-2015 Bareos GmbH & Co. KG
6
7 This program is Free Software; you can redistribute it and/or
8 modify it under the terms of version three of the GNU Affero General Public
9 License as published by the Free Software Foundation and included
10 in the file LICENSE.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Affero General Public License for more details.
16
17 You should have received a copy of the GNU Affero General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21 */
22 /*
23 * Marco van Wieringen, May 2015
24 */
25 /**
26 * @file
27 * In memory FHDB for NDMP Data Management Application (DMA)
28 */
29
30 #include "include/bareos.h"
31 #include "dird.h"
32
33 #if HAVE_NDMP
34 #include "ndmp/ndmagents.h"
35 #include "ndmp_dma_priv.h"
36 #endif /* HAVE_NDMP */
37
38 namespace directordaemon {
39
40 #if HAVE_NDMP
41
42 #define B_PAGE_SIZE 4096
43 #define MIN_PAGES 128
44 #define MAX_PAGES 2400
45 #define MAX_BUF_SIZE (MAX_PAGES * B_PAGE_SIZE) /* approx 10MB */
46
47 /*
48 * Lightweight version of Bareos tree layout for holding the NDMP
49 * filehandle index database. See lib/tree.[ch] for the full version.
50 */
51 struct ndmp_fhdb_mem {
52 struct ndmp_fhdb_mem *next; /* Next buffer */
53 int rem; /* Remaining bytes */
54 char *mem; /* Memory pointer */
55 char first[1]; /* First byte */
56 };
57
58 struct ndmp_fhdb_node {
59 /*
60 * KEEP sibling as the first member to avoid having to do initialization of child
61 */
62 rblink sibling;
63 rblist child;
64 char *fname; /* File name */
65 char *attr; /* Encoded stat struct */
66 int8_t FileType; /* Type of File */
67 int32_t FileIndex; /* File index */
68 uint64_t Offset; /* File Offset in NDMP stream */
69 uint64_t inode; /* Inode nr */
70 uint16_t fname_len; /* Filename length */
71 ndmp_fhdb_node *next;
72 ndmp_fhdb_node *parent;
73 };
74 typedef struct ndmp_fhdb_node N_TREE_NODE;
75
76 struct ndmp_fhdb_root {
77 /*
78 * KEEP sibling as the first member to avoid having to do initialization of child
79 */
80 rblink sibling;
81 rblist child;
82 char *fname; /**< File name */
83 char *attr; /**< Encoded stat struct */
84 int8_t FileType; /**< Type of File */
85 int32_t FileIndex; /**< File index */
86 uint64_t Offset; /**< File Offset in NDMP stream */
87 uint64_t inode; /**< Inode nr */
88 uint16_t fname_len; /**< Filename length */
89 ndmp_fhdb_node *next;
90 ndmp_fhdb_node *parent;
91
92 /*
93 * The above ^^^ must be identical to a ndmp_fhdb_node structure
94 * The below vvv is only for the root of the tree.
95 */
96 ndmp_fhdb_node *first; /**< first entry in the tree */
97 ndmp_fhdb_node *last; /**< last entry in the tree */
98 ndmp_fhdb_mem *mem; /**< tree memory */
99 uint32_t total_size; /**< total bytes allocated */
100 uint32_t blocks; /**< total mallocs */
101 ndmp_fhdb_node *cached_parent; /**< cached parent */
102 };
103 typedef struct ndmp_fhdb_root N_TREE_ROOT;
104
105 struct ooo_metadata {
106 hlink link;
107 uint64_t dir_node;
108 N_TREE_NODE *nt_node;
109 };
110 typedef struct ooo_metadata OOO_MD;
111
112 struct fhdb_state {
113 N_TREE_ROOT *fhdb_root;
114 htable *out_of_order_metadata;
115 };
116
117 /**
118 * Lightweight version of Bareos tree functions for holding the NDMP
119 * filehandle index database. See lib/tree.[ch] for the full version.
120 */
MallocBuf(N_TREE_ROOT * root,int size)121 static void MallocBuf(N_TREE_ROOT *root, int size)
122 {
123 struct ndmp_fhdb_mem *mem;
124
125 mem = (struct ndmp_fhdb_mem *)malloc(size);
126 root->total_size += size;
127 root->blocks++;
128 mem->next = root->mem;
129 root->mem = mem;
130 mem->mem = mem->first;
131 mem->rem = (char *)mem + size - mem->mem;
132 }
133
134 /*
135 * Note, we allocate a big buffer in the tree root from which we
136 * allocate nodes. This runs more than 100 times as fast as directly
137 * using malloc() for each of the nodes.
138 */
ndmp_fhdb_new_tree()139 static inline N_TREE_ROOT *ndmp_fhdb_new_tree()
140 {
141 int count = 512;
142 N_TREE_ROOT *root;
143 uint32_t size;
144
145 root = (N_TREE_ROOT *)malloc(sizeof(N_TREE_ROOT));
146 memset(root, 0, sizeof(N_TREE_ROOT));
147
148 /*
149 * Assume filename + node = 40 characters average length
150 */
151 size = count * (BALIGN(sizeof(N_TREE_ROOT)) + 40);
152 if (size > (MAX_BUF_SIZE / 2)) {
153 size = MAX_BUF_SIZE;
154 }
155
156 Dmsg2(400, "count=%d size=%d\n", count, size);
157
158 MallocBuf(root, size);
159
160 return root;
161 }
162
163 /*
164 * Allocate bytes for filename in tree structure.
165 * Keep the pointers properly aligned by allocating sizes that are aligned.
166 */
ndmp_fhdb_tree_alloc(N_TREE_ROOT * root,int size)167 static inline char *ndmp_fhdb_tree_alloc(N_TREE_ROOT *root, int size)
168 {
169 char *buf;
170 int asize = BALIGN(size);
171
172 if (root->mem->rem < asize) {
173 uint32_t mb_size;
174
175 if (root->total_size >= (MAX_BUF_SIZE / 2)) {
176 mb_size = MAX_BUF_SIZE;
177 } else {
178 mb_size = MAX_BUF_SIZE / 2;
179 }
180 MallocBuf(root, mb_size);
181 }
182
183 root->mem->rem -= asize;
184 buf = root->mem->mem;
185 root->mem->mem += asize;
186
187 return buf;
188 }
189
190 /*
191 * This routine can be called to release the previously allocated tree node.
192 */
NdmpFhdbFreeTreeNode(N_TREE_ROOT * root)193 static inline void NdmpFhdbFreeTreeNode(N_TREE_ROOT *root)
194 {
195 int asize = BALIGN(sizeof(N_TREE_NODE));
196
197 root->mem->rem += asize;
198 root->mem->mem -= asize;
199 }
200
201 /*
202 * Create a new tree node.
203 */
ndmp_fhdb_new_tree_node(N_TREE_ROOT * root)204 static N_TREE_NODE *ndmp_fhdb_new_tree_node(N_TREE_ROOT *root)
205 {
206 N_TREE_NODE *node;
207 int size = sizeof(N_TREE_NODE);
208
209 node = (N_TREE_NODE *)ndmp_fhdb_tree_alloc(root, size);
210 memset(node, 0, size);
211
212 return node;
213 }
214
215 /*
216 * This routine frees the whole tree
217 */
NdmpFhdbFreeTree(N_TREE_ROOT * root)218 static inline void NdmpFhdbFreeTree(N_TREE_ROOT *root)
219 {
220 struct ndmp_fhdb_mem *mem, *rel;
221 uint32_t freed_blocks = 0;
222
223 for (mem = root->mem; mem; ) {
224 rel = mem;
225 mem = mem->next;
226 free(rel);
227 freed_blocks++;
228 }
229
230 Dmsg3(100, "Total size=%u blocks=%u freed_blocks=%u\n", root->total_size, root->blocks, freed_blocks);
231
232 free(root);
233 GarbageCollectMemory();
234
235 return;
236 }
237
NodeCompareByName(void * item1,void * item2)238 static int NodeCompareByName(void *item1, void *item2)
239 {
240 N_TREE_NODE *tn1 = (N_TREE_NODE *)item1;
241 N_TREE_NODE *tn2 = (N_TREE_NODE *)item2;
242
243 if (tn1->fname[0] > tn2->fname[0]) {
244 return 1;
245 } else if (tn1->fname[0] < tn2->fname[0]) {
246 return -1;
247 }
248 return strcmp(tn1->fname, tn2->fname);
249 }
250
NodeCompareById(void * item1,void * item2)251 static int NodeCompareById(void *item1, void *item2)
252 {
253 N_TREE_NODE *tn1 = (N_TREE_NODE *)item1;
254 N_TREE_NODE *tn2 = (N_TREE_NODE *)item2;
255
256 if (tn1->inode > tn2->inode) {
257 return 1;
258 } else if (tn1->inode < tn2->inode) {
259 return -1;
260 } else {
261 return 0;
262 }
263 }
264
search_and_insert_tree_node(char * fname,int32_t FileIndex,uint64_t inode,N_TREE_ROOT * root,N_TREE_NODE * parent)265 static N_TREE_NODE *search_and_insert_tree_node(char *fname, int32_t FileIndex, uint64_t inode,
266 N_TREE_ROOT *root, N_TREE_NODE *parent)
267 {
268 N_TREE_NODE *node, *found_node;
269
270 node = ndmp_fhdb_new_tree_node(root);
271 if (inode) {
272 node->inode = inode;
273 found_node = (N_TREE_NODE *)parent->child.insert(node, NodeCompareById);
274 } else {
275 node->fname = fname;
276 found_node = (N_TREE_NODE *)parent->child.insert(node, NodeCompareByName);
277 }
278
279 /*
280 * Already in list ?
281 */
282 if (found_node != node) {
283 /*
284 * Free node allocated above.
285 */
286 NdmpFhdbFreeTreeNode(root);
287 return found_node;
288 }
289
290 /*
291 * Its was not found, but now inserted.
292 */
293 node->parent = parent;
294 node->FileIndex = FileIndex;
295 node->fname_len = strlen(fname);
296 /*
297 * Allocate a new entry with 2 bytes extra e.g. the extra slash
298 * needed for directories and the \0.
299 */
300 node->fname = ndmp_fhdb_tree_alloc(root, node->fname_len + 2);
301 bstrncpy(node->fname, fname, node->fname_len + 1);
302
303 /*
304 * Maintain a linear chain of nodes.
305 */
306 if (!root->first) {
307 root->first = node;
308 root->last = node;
309 } else {
310 root->last->next = node;
311 root->last = node;
312 }
313
314 return node;
315 }
316
search_and_insert_tree_node(N_TREE_NODE * node,N_TREE_ROOT * root,N_TREE_NODE * parent)317 static N_TREE_NODE *search_and_insert_tree_node(N_TREE_NODE *node, N_TREE_ROOT *root, N_TREE_NODE *parent)
318 {
319 N_TREE_NODE *found_node;
320
321 /*
322 * Insert the node into the right parent. We should always insert
323 * this node and never get back a found_node that is not the same
324 * as the original node but if we do we better return as then there
325 * is nothing todo.
326 */
327 found_node = (N_TREE_NODE *)parent->child.insert(node, NodeCompareById);
328 if (found_node != node) {
329 return found_node;
330 }
331
332 node->parent = parent;
333
334 /*
335 * Maintain a linear chain of nodes.
336 */
337 if (!root->first) {
338 root->first = node;
339 root->last = node;
340 } else {
341 root->last->next = node;
342 root->last = node;
343 }
344
345 return node;
346 }
347
348 /*
349 * Recursively search the tree for a certain inode number.
350 */
find_tree_node(N_TREE_NODE * node,uint64_t inode)351 static N_TREE_NODE *find_tree_node(N_TREE_NODE *node, uint64_t inode)
352 {
353 N_TREE_NODE match_node;
354 N_TREE_NODE *found_node, *walker;
355
356 match_node.inode = inode;
357
358 /*
359 * Start searching in the children of this node.
360 */
361 found_node = (N_TREE_NODE *)node->child.search(&match_node, NodeCompareById);
362 if (found_node) {
363 return found_node;
364 }
365
366 /*
367 * The node we are searching for is not one of the top nodes so need to search deeper.
368 */
369 foreach_rblist(walker, &node->child) {
370 /*
371 * See if the node has any children otherwise no need to search it.
372 */
373 if (walker->child.empty()) {
374 continue;
375 }
376
377 found_node = find_tree_node(walker, inode);
378 if (found_node) {
379 return found_node;
380 }
381 }
382
383 return (N_TREE_NODE *)NULL;
384 }
385
386 /*
387 * Recursively search the tree for a certain inode number.
388 */
find_tree_node(N_TREE_ROOT * root,uint64_t inode)389 static N_TREE_NODE *find_tree_node(N_TREE_ROOT *root, uint64_t inode)
390 {
391 N_TREE_NODE match_node;
392 N_TREE_NODE *found_node, *walker;
393
394 /*
395 * See if this is a request for the root of the tree.
396 */
397 if (root->inode == inode) {
398 return (N_TREE_NODE *)root;
399 }
400
401 match_node.inode = inode;
402
403 /*
404 * First do the easy lookup e.g. is this inode part of the parent of the current parent.
405 */
406 if (root->cached_parent && root->cached_parent->parent) {
407 found_node = (N_TREE_NODE *)root->cached_parent->parent->child.search(&match_node, NodeCompareById);
408 if (found_node) {
409 return found_node;
410 }
411 }
412
413 /*
414 * Start searching from the root node.
415 */
416 found_node = (N_TREE_NODE *)root->child.search(&match_node, NodeCompareById);
417 if (found_node) {
418 return found_node;
419 }
420
421 /*
422 * The node we are searching for is not one of the top nodes so need to search deeper.
423 */
424 foreach_rblist(walker, &root->child) {
425 /*
426 * See if the node has any children otherwise no need to search it.
427 */
428 if (walker->child.empty()) {
429 continue;
430 }
431
432 found_node = find_tree_node(walker, inode);
433 if (found_node) {
434 return found_node;
435 }
436 }
437
438 return (N_TREE_NODE *)NULL;
439 }
440
441
442 /*
443 * This inserts a piece of meta data we receive out or order in a hash table
444 * for later processing. Most NDMP DMAs send things kind of in order some do not
445 * and for those we have this workaround.
446 */
add_out_of_order_metadata(NIS * nis,N_TREE_ROOT * fhdb_root,const char * raw_name,ndmp9_u_quad dir_node,ndmp9_u_quad node)447 static inline void add_out_of_order_metadata(NIS *nis, N_TREE_ROOT *fhdb_root,
448 const char *raw_name, ndmp9_u_quad dir_node, ndmp9_u_quad node)
449 {
450 N_TREE_NODE *nt_node;
451 OOO_MD *md_entry = NULL;
452 htable *meta_data = ((struct fhdb_state *)nis->fhdb_state)->out_of_order_metadata;
453
454 nt_node = ndmp_fhdb_new_tree_node(fhdb_root);
455 nt_node->inode = node;
456 nt_node->FileIndex = fhdb_root->FileIndex;
457 nt_node->fname_len = strlen(raw_name);
458 /*
459 * Allocate a new entry with 2 bytes extra e.g. the extra slash
460 * needed for directories and the \0.
461 */
462 nt_node->fname = ndmp_fhdb_tree_alloc(fhdb_root, nt_node->fname_len + 2);
463 bstrncpy(nt_node->fname, raw_name, nt_node->fname_len + 1);
464
465 /*
466 * See if we already allocated the htable.
467 */
468 if (!meta_data) {
469 uint32_t nr_pages,
470 nr_items,
471 item_size;
472
473 nr_pages = MIN_PAGES;
474 item_size = sizeof(OOO_MD);
475 nr_items = (nr_pages * B_PAGE_SIZE) / item_size;
476
477 meta_data = (htable *)malloc(sizeof(htable));
478 meta_data->init(md_entry, &md_entry->link, nr_items, nr_pages);
479 ((struct fhdb_state *)nis->fhdb_state)->out_of_order_metadata = meta_data;
480 }
481
482 /*
483 * Create a new entry and insert it into the hash with the node number as key.
484 */
485 md_entry = (OOO_MD *)meta_data->hash_malloc(sizeof(OOO_MD));
486 md_entry->dir_node = dir_node;
487 md_entry->nt_node = nt_node;
488
489 meta_data->insert((uint64_t)node, (void *)md_entry);
490
491 Dmsg2(100, "bndmp_fhdb_mem_add_dir: Added out of order metadata entry for node %llu with parent %llu\n",
492 node, dir_node);
493 }
494
bndmp_fhdb_mem_add_dir(struct ndmlog * ixlog,int tagc,char * raw_name,ndmp9_u_quad dir_node,ndmp9_u_quad node)495 extern "C" int bndmp_fhdb_mem_add_dir(struct ndmlog *ixlog, int tagc, char *raw_name,
496 ndmp9_u_quad dir_node, ndmp9_u_quad node)
497 {
498 NIS *nis = (NIS *)ixlog->ctx;
499
500 /*
501 * Ignore . and .. directory entries.
502 */
503 if (bstrcmp(raw_name, ".") || bstrcmp(raw_name, "..")) {
504 return 0;
505 }
506
507 if (nis->save_filehist) {
508 N_TREE_ROOT *fhdb_root;
509
510 Dmsg3(100, "bndmp_fhdb_mem_add_dir: New filename ==> %s [%llu] - [%llu]\n", raw_name, dir_node, node);
511
512 fhdb_root = ((struct fhdb_state *)nis->fhdb_state)->fhdb_root;
513 if (!fhdb_root) {
514 Jmsg(nis->jcr, M_FATAL, 0, _("NDMP protocol error, FHDB add_dir call before add_dirnode_root.\n"));
515 return 1;
516 }
517
518 /*
519 * See if this entry is in the cached parent.
520 */
521 if (fhdb_root->cached_parent &&
522 fhdb_root->cached_parent->inode == dir_node) {
523 search_and_insert_tree_node(raw_name, fhdb_root->FileIndex, node,
524 fhdb_root, fhdb_root->cached_parent);
525 } else {
526 /*
527 * Not the cached parent search the tree where it need to be put.
528 */
529 fhdb_root->cached_parent = find_tree_node(fhdb_root, dir_node);
530 if (fhdb_root->cached_parent) {
531 search_and_insert_tree_node(raw_name, fhdb_root->FileIndex, node,
532 fhdb_root, fhdb_root->cached_parent);
533 } else {
534 add_out_of_order_metadata(nis, fhdb_root, raw_name, dir_node, node);
535 }
536 }
537 }
538
539 return 0;
540 }
541
542 /*
543 * This tries recursivly to add the missing parents to the tree.
544 */
insert_metadata_parent_node(htable * meta_data,N_TREE_ROOT * fhdb_root,uint64_t dir_node)545 static N_TREE_NODE *insert_metadata_parent_node(htable *meta_data, N_TREE_ROOT *fhdb_root, uint64_t dir_node)
546 {
547 N_TREE_NODE *parent;
548 OOO_MD *md_entry;
549
550 Dmsg1(100, "bndmp_fhdb_mem_add_dir: Inserting node for parent %llu into tree\n", dir_node);
551
552 /*
553 * lookup the dir_node
554 */
555 md_entry = (OOO_MD *)meta_data->lookup(dir_node);
556 if (!md_entry || !md_entry->nt_node) {
557 /*
558 * If we got called the parent node is not in the current tree if we
559 * also cannot find it in the metadata things are inconsistent so give up.
560 */
561 return (N_TREE_NODE *)NULL;
562 }
563
564 /*
565 * Lookup the parent of this new node we are about to insert.
566 */
567 parent = find_tree_node(fhdb_root, md_entry->dir_node);
568 if (!parent) {
569 /*
570 * If our parent doesn't exist try finding it and inserting it.
571 */
572 parent = insert_metadata_parent_node(meta_data, fhdb_root, md_entry->dir_node);
573 if (!parent) {
574 /*
575 * If by recursive calling insert_metadata_parent_node we cannot create linked
576 * set of parent nodes our metadata is really inconsistent so give up.
577 */
578 return (N_TREE_NODE *)NULL;
579 }
580 }
581
582 /*
583 * Now we have a working parent in the current tree so we can add the this parent node.
584 */
585 parent = search_and_insert_tree_node(md_entry->nt_node, fhdb_root, parent);
586
587 /*
588 * Keep track we used this entry.
589 */
590 md_entry->nt_node = (N_TREE_NODE *)NULL;
591
592 return parent;
593 }
594
595 /*
596 * This processes all saved out of order metadata and adds these entries to the tree.
597 * Only used for NDMP DMAs which are sending their metadata fully at random.
598 */
ProcessOutOfOrderMetadata(htable * meta_data,N_TREE_ROOT * fhdb_root)599 static inline bool ProcessOutOfOrderMetadata(htable *meta_data, N_TREE_ROOT *fhdb_root)
600 {
601 OOO_MD *md_entry;
602
603 foreach_htable(md_entry, meta_data) {
604 /*
605 * Alread visited ?
606 */
607 if (!md_entry->nt_node) {
608 continue;
609 }
610
611 Dmsg1(100, "bndmp_fhdb_mem_add_dir: Inserting node for %llu into tree\n", md_entry->nt_node->inode);
612
613 /*
614 * See if this entry is in the cached parent.
615 */
616 if (fhdb_root->cached_parent &&
617 fhdb_root->cached_parent->inode == md_entry->dir_node) {
618 search_and_insert_tree_node(md_entry->nt_node, fhdb_root, fhdb_root->cached_parent);
619 } else {
620 /*
621 * See if parent exists in tree.
622 */
623 fhdb_root->cached_parent = find_tree_node(fhdb_root, md_entry->dir_node);
624 if (fhdb_root->cached_parent) {
625 search_and_insert_tree_node(md_entry->nt_node, fhdb_root, fhdb_root->cached_parent);
626 } else {
627 fhdb_root->cached_parent = insert_metadata_parent_node(meta_data, fhdb_root, md_entry->dir_node);
628 if (!fhdb_root->cached_parent) {
629 /*
630 * The metadata seems to be fully inconsistent.
631 */
632 Dmsg0(100, "bndmp_fhdb_mem_add_dir: Inconsistent metadata, giving up\n");
633 return false;
634 }
635
636 search_and_insert_tree_node(md_entry->nt_node, fhdb_root, fhdb_root->cached_parent);
637 }
638 }
639 }
640
641 return true;
642 }
643
bndmp_fhdb_mem_add_node(struct ndmlog * ixlog,int tagc,ndmp9_u_quad node,ndmp9_file_stat * fstat)644 extern "C" int bndmp_fhdb_mem_add_node(struct ndmlog *ixlog, int tagc,
645 ndmp9_u_quad node, ndmp9_file_stat *fstat)
646 {
647 NIS *nis = (NIS *)ixlog->ctx;
648
649 nis->jcr->lock();
650 nis->jcr->JobFiles++;
651 nis->jcr->unlock();
652
653 if (nis->save_filehist) {
654 int attr_size;
655 int8_t FileType = 0;
656 N_TREE_ROOT *fhdb_root;
657 N_TREE_NODE *wanted_node;
658 PoolMem attribs(PM_FNAME);
659 htable *meta_data = ((struct fhdb_state *)nis->fhdb_state)->out_of_order_metadata;
660
661 Dmsg1(100, "bndmp_fhdb_mem_add_node: New node [%llu]\n", node);
662
663 fhdb_root = ((struct fhdb_state *)nis->fhdb_state)->fhdb_root;
664 if (!fhdb_root) {
665 Jmsg(nis->jcr, M_FATAL, 0, _("NDMP protocol error, FHDB add_node call before add_dir.\n"));
666 return 1;
667 }
668
669 if (meta_data) {
670 if (!ProcessOutOfOrderMetadata(meta_data, fhdb_root)) {
671 Jmsg(nis->jcr, M_FATAL, 0, _("NDMP protocol error, FHDB unable to process out of order metadata.\n"));
672 meta_data->destroy();
673 free(meta_data);
674 ((struct fhdb_state *)nis->fhdb_state)->out_of_order_metadata = NULL;
675 return 1;
676 }
677
678 meta_data->destroy();
679 free(meta_data);
680 ((struct fhdb_state *)nis->fhdb_state)->out_of_order_metadata = NULL;
681 }
682
683 wanted_node = find_tree_node(fhdb_root, node);
684 if (!wanted_node) {
685 Jmsg(nis->jcr, M_FATAL, 0, _("NDMP protocol error, FHDB add_node request for unknown node %llu.\n"), node);
686 return 1;
687 }
688
689 NdmpConvertFstat(fstat, nis->FileIndex, &FileType, attribs);
690 attr_size = strlen(attribs.c_str()) + 1;
691
692 wanted_node->attr = ndmp_fhdb_tree_alloc(fhdb_root, attr_size);
693 bstrncpy(wanted_node->attr, attribs, attr_size);
694 wanted_node->FileType = FileType;
695 if (fstat->fh_info.valid == NDMP9_VALIDITY_VALID) {
696 wanted_node->Offset = fstat->fh_info.value;
697 }
698
699 if (FileType == FT_DIREND) {
700 /*
701 * A directory needs to end with a slash.
702 */
703 strcat(wanted_node->fname, "/");
704 }
705 }
706
707 return 0;
708 }
709
bndmp_fhdb_mem_add_dirnode_root(struct ndmlog * ixlog,int tagc,ndmp9_u_quad root_node)710 extern "C" int bndmp_fhdb_mem_add_dirnode_root(struct ndmlog *ixlog, int tagc,
711 ndmp9_u_quad root_node)
712 {
713 NIS *nis = (NIS *)ixlog->ctx;
714
715 if (nis->save_filehist) {
716 N_TREE_ROOT *fhdb_root;
717 struct fhdb_state *fhdb_state;
718
719 Dmsg1(100, "bndmp_fhdb_mem_add_dirnode_root: New root node [%llu]\n", root_node);
720
721 fhdb_state = ((struct fhdb_state *)nis->fhdb_state);
722 fhdb_root = fhdb_state->fhdb_root;
723 if (fhdb_root) {
724 Jmsg(nis->jcr, M_FATAL, 0, _("NDMP protocol error, FHDB add_dirnode_root call more then once.\n"));
725 return 1;
726 }
727
728 fhdb_state->fhdb_root = ndmp_fhdb_new_tree();
729
730 fhdb_root = fhdb_state->fhdb_root;
731 fhdb_root->inode = root_node;
732 fhdb_root->FileIndex = nis->FileIndex;
733 fhdb_root->fname_len = strlen(nis->filesystem);
734 /*
735 * Allocate a new entry with 2 bytes extra e.g. the extra slash
736 * needed for directories and the \0.
737 */
738 fhdb_root->fname = ndmp_fhdb_tree_alloc(fhdb_root, fhdb_root->fname_len + 2);
739 bstrncpy(fhdb_root->fname, nis->filesystem, fhdb_root->fname_len + 1);
740
741 fhdb_root->cached_parent = (N_TREE_NODE *)fhdb_root;
742 }
743
744 return 0;
745 }
746
747 /*
748 * This glues the NDMP File Handle DB with internal code.
749 */
NdmpFhdbMemRegister(struct ndmlog * ixlog)750 void NdmpFhdbMemRegister(struct ndmlog *ixlog)
751 {
752 NIS *nis = (NIS *)ixlog->ctx;
753 struct ndm_fhdb_callbacks fhdb_callbacks;
754
755 /*
756 * Register the FileHandleDB callbacks.
757 */
758 fhdb_callbacks.add_file = BndmpFhdbAddFile;
759 fhdb_callbacks.add_dir = bndmp_fhdb_mem_add_dir;
760 fhdb_callbacks.add_node = bndmp_fhdb_mem_add_node;
761 fhdb_callbacks.add_dirnode_root = bndmp_fhdb_mem_add_dirnode_root;
762 ndmfhdb_register_callbacks(ixlog, &fhdb_callbacks);
763
764 nis->fhdb_state = malloc(sizeof(struct fhdb_state));
765 memset(nis->fhdb_state, 0, sizeof(struct fhdb_state));
766 }
767
NdmpFhdbMemUnregister(struct ndmlog * ixlog)768 void NdmpFhdbMemUnregister(struct ndmlog *ixlog)
769 {
770 NIS *nis = (NIS *)ixlog->ctx;
771 if (nis && nis->fhdb_state) {
772 N_TREE_ROOT *fhdb_root;
773
774 fhdb_root = ((struct fhdb_state *)nis->fhdb_state)->fhdb_root;
775 if (fhdb_root) {
776 NdmpFhdbFreeTree(fhdb_root);
777 }
778
779 free(nis->fhdb_state);
780 nis->fhdb_state = NULL;
781 }
782
783 ndmfhdb_unregister_callbacks(ixlog);
784 }
785
NdmpFhdbMemProcessDb(struct ndmlog * ixlog)786 void NdmpFhdbMemProcessDb(struct ndmlog *ixlog)
787 {
788 N_TREE_ROOT *fhdb_root;
789 NIS *nis = (NIS *)ixlog->ctx;
790 struct fhdb_state *fhdb_state;
791
792 fhdb_state = ((struct fhdb_state *)nis->fhdb_state);
793 fhdb_root = fhdb_state->fhdb_root;
794 if (fhdb_root) {
795 if (nis->jcr->ar) {
796 N_TREE_NODE *node, *parent;
797 PoolMem fname, tmp;
798
799 /*
800 * Store the toplevel entry of the tree.
801 */
802 Dmsg2(100, "==> %s [%s]\n", fhdb_root->fname, fhdb_root->attr);
803 NdmpStoreAttributeRecord(nis->jcr, fhdb_root->fname, nis->virtual_filename,
804 fhdb_root->attr, fhdb_root->FileType,
805 fhdb_root->inode, fhdb_root->Offset);
806
807 /*
808 * Store all the other entries in the tree.
809 */
810 for (node = fhdb_root->first; node; node = node->next) {
811 PmStrcpy(fname, node->fname);
812
813 /*
814 * Walk up the parent until we hit the head of the list.
815 * As directories are store including there trailing slash we
816 * can just concatenate the two parts.
817 */
818 for (parent = node->parent; parent; parent = parent->parent) {
819 PmStrcpy(tmp, fname.c_str());
820 PmStrcpy(fname, parent->fname);
821 PmStrcat(fname, tmp.c_str());
822 }
823
824 /*
825 * Now we have the full pathname of the file in fname.
826 * Store the entry as a hardlinked entry to the original NDMP archive.
827 *
828 * Handling of incremental/differential backups:
829 * During incremental backups, NDMP4_FH_ADD_DIR is sent for ALL files
830 * Only for files being backed up, we also get NDMP4_FH_ADD_NODE
831 * So we skip entries that do not have any attribute
832 */
833 if (node->attr) {
834 Dmsg2(100, "==> %s [%s]\n", fname.c_str(), node->attr);
835 NdmpStoreAttributeRecord(nis->jcr, fname.c_str(), nis->virtual_filename,
836 node->attr, node->FileType, node->inode, node->Offset);
837 } else {
838 Dmsg1(100, "Skipping %s because it has no attributes\n", fname.c_str());
839 }
840 }
841 }
842
843 /*
844 * Destroy the tree.
845 */
846 NdmpFhdbFreeTree(fhdb_root);
847 fhdb_state->fhdb_root = NULL;
848 }
849 }
850
851 #endif /* #if HAVE_NDMP */
852 } /* namespace directordaemon */
853