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