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