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