find_one.c 31.4 KB
Newer Older
Kern Sibbald's avatar
Kern Sibbald committed
1
/*
Kern Sibbald's avatar
Kern Sibbald committed
2
   Bacula(R) - The Network Backup Solution
Kern Sibbald's avatar
Kern Sibbald committed
3

Eric Bollengier's avatar
Eric Bollengier committed
4
   Copyright (C) 2000-2022 Kern Sibbald
Kern Sibbald's avatar
Kern Sibbald committed
5

Kern Sibbald's avatar
Kern Sibbald committed
6
7
   The original author of Bacula is Kern Sibbald, with contributions
   from many others, a complete list can be found in the file AUTHORS.
Kern Sibbald's avatar
Kern Sibbald committed
8

Kern Sibbald's avatar
Kern Sibbald committed
9
10
11
12
   You may use this file and others of this release according to the
   license defined in the LICENSE file, which includes the Affero General
   Public License, v3.0 ("AGPLv3") and some additional permissions and
   terms pursuant to its AGPLv3 Section 7.
Kern Sibbald's avatar
Kern Sibbald committed
13

14
   This notice must be preserved when any source code is
Kern Sibbald's avatar
Kern Sibbald committed
15
16
17
   conveyed and/or propagated.

   Bacula(R) is a registered trademark of Kern Sibbald.
Kern Sibbald's avatar
Kern Sibbald committed
18
*/
19
20
21
22
23
24
25
26
27
28
/*

   This file was derived from GNU TAR source code. Except for a few key
   ideas, it has been entirely rewritten for Bacula.

      Kern Sibbald, MM

   Thanks to the TAR programmers.

 */
Kern Sibbald's avatar
Kern Sibbald committed
29
30
31

#include "bacula.h"
#include "find.h"
32
33
34
35
#if defined(HAVE_LINUX_OS) && defined(HAVE_NODUMP)
  #include <linux/ioctl.h>
  #include <linux/fs.h>
#endif
36
#ifdef HAVE_DARWIN_OS
37
38
#include <sys/param.h>
#include <sys/mount.h>
39
40
#include <sys/attr.h>
#endif
Kern Sibbald's avatar
Kern Sibbald committed
41

42
43
int breaddir(DIR *dirp, POOLMEM *&d_name);

Kern Sibbald's avatar
   
Kern Sibbald committed
44
45
extern int32_t name_max;              /* filename max length */
extern int32_t path_max;              /* path name max length */
Kern Sibbald's avatar
Kern Sibbald committed
46
47

/*
48
 * Structure for keeping track of hard linked files, we
49
50
51
52
 *   keep an entry for each hardlinked file that we save,
 *   which is the first one found. For all the other files that
 *   are linked to this one, we save only the directory
 *   entry so we can link it.
Kern Sibbald's avatar
Kern Sibbald committed
53
54
55
 */
struct f_link {
    struct f_link *next;
Kern Sibbald's avatar
   
Kern Sibbald committed
56
57
    dev_t dev;                        /* device */
    ino_t ino;                        /* inode with device is unique */
58
    int32_t FileIndex;                /* Bacula FileIndex of this file */
59
60
61
    int32_t digest_stream;            /* Digest type if needed */
    uint32_t digest_len;              /* Digest len if needed */
    char *digest;                     /* Checksum of the file if needed */
Kern Sibbald's avatar
   
Kern Sibbald committed
62
    char name[1];                     /* The name */
Kern Sibbald's avatar
Kern Sibbald committed
63
64
};

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
typedef struct f_link link_t;
#define LINK_HASHTABLE_BITS 16
#define LINK_HASHTABLE_SIZE (1<<LINK_HASHTABLE_BITS)
#define LINK_HASHTABLE_MASK (LINK_HASHTABLE_SIZE-1)

static inline int LINKHASH(const struct stat &info)
{
    int hash = info.st_dev;
    unsigned long long i = info.st_ino;
    hash ^= i;
    i >>= 16;
    hash ^= i;
    i >>= 16;
    hash ^= i;
    i >>= 16;
    hash ^= i;
    return hash & LINK_HASHTABLE_MASK;
}

84
85
86
/*
 * Create a new directory Find File packet, but copy
 *   some of the essential info from the current packet.
Kern Sibbald's avatar
Kern Sibbald committed
87
 *   However, be careful to zero out the rest of the
88
89
90
91
92
 *   packet.
 */
static FF_PKT *new_dir_ff_pkt(FF_PKT *ff_pkt)
{
   FF_PKT *dir_ff_pkt = (FF_PKT *)bmalloc(sizeof(FF_PKT));
Eric Bollengier's avatar
Eric Bollengier committed
93
   memcpy((void*)dir_ff_pkt, (void*)ff_pkt, sizeof(FF_PKT));
94
95

   /* Do not duplicate pointers */
96
   dir_ff_pkt->fname = bstrdup(ff_pkt->fname);
97
   dir_ff_pkt->snap_fname = bstrdup(ff_pkt->snap_fname);
98
   dir_ff_pkt->link = bstrdup(ff_pkt->link);
Kern Sibbald's avatar
Kern Sibbald committed
99

100
   if (ff_pkt->fname_save) {
Kern Sibbald's avatar
Kern Sibbald committed
101
102
      dir_ff_pkt->fname_save = get_pool_memory(PM_FNAME);
      pm_strcpy(dir_ff_pkt->fname_save, ff_pkt->fname_save);
103
104
105
   }
   if (ff_pkt->link_save) {
      dir_ff_pkt->link_save = get_pool_memory(PM_FNAME);
Kern Sibbald's avatar
Kern Sibbald committed
106
107
108
      pm_strcpy(dir_ff_pkt->link_save, ff_pkt->link_save);
   }

109
110
111
112
   dir_ff_pkt->included_files_list = NULL;
   dir_ff_pkt->excluded_files_list = NULL;
   dir_ff_pkt->excluded_paths_list = NULL;
   dir_ff_pkt->linkhash = NULL;
113
   dir_ff_pkt->ignoredir_fname = NULL;
114
115
116
117
118
119
   return dir_ff_pkt;
}

/*
 * Free the temp directory ff_pkt
 */
120
121
122
static void free_dir_ff_pkt(FF_PKT *dir_ff_pkt)
{
   free(dir_ff_pkt->fname);
123
   free(dir_ff_pkt->snap_fname);
124
   free(dir_ff_pkt->link);
125
126
127
128
129
130
   if (dir_ff_pkt->fname_save) {
      free_pool_memory(dir_ff_pkt->fname_save);
   }
   if (dir_ff_pkt->link_save) {
      free_pool_memory(dir_ff_pkt->link_save);
   }
131
132
133
   free(dir_ff_pkt);
}

134
135
136
137
138
139
/*
 * Check to see if we allow the file system type of a file or directory.
 * If we do not have a list of file system types, we accept anything.
 */
static int accept_fstype(FF_PKT *ff, void *dummy) {
   int i;
Kern Sibbald's avatar
Kern Sibbald committed
140
   char fs[1000];
141
142
   bool accept = true;

143
   if (ff->fstypes.size()) {
144
      accept = false;
Kern Sibbald's avatar
Kern Sibbald committed
145
      if (!fstype(ff, fs, sizeof(fs))) {
Kern Sibbald's avatar
Kern Sibbald committed
146
         Dmsg1(50, "Cannot determine file system type for \"%s\"\n", ff->fname);
147
      } else {
Kern Sibbald's avatar
   
Kern Sibbald committed
148
149
         for (i = 0; i < ff->fstypes.size(); ++i) {
            if (strcmp(fs, (char *)ff->fstypes.get(i)) == 0) {
Kern Sibbald's avatar
Kern Sibbald committed
150
               Dmsg2(100, "Accepting fstype %s for \"%s\"\n", fs, ff->fname);
Kern Sibbald's avatar
   
Kern Sibbald committed
151
152
153
               accept = true;
               break;
            }
Kern Sibbald's avatar
Kern Sibbald committed
154
            Dmsg3(200, "fstype %s for \"%s\" does not match %s\n", fs,
Kern Sibbald's avatar
   
Kern Sibbald committed
155
156
                  ff->fname, ff->fstypes.get(i));
         }
157
158
159
160
161
      }
   }
   return accept;
}

162
163
164
165
166
167
168
169
170
171
172
/*
 * Check to see if we allow the drive type of a file or directory.
 * If we do not have a list of drive types, we accept anything.
 */
static int accept_drivetype(FF_PKT *ff, void *dummy) {
   int i;
   char dt[100];
   bool accept = true;

   if (ff->drivetypes.size()) {
      accept = false;
173
      /* fname is a better choice here than snap_fname */
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
      if (!drivetype(ff->fname, dt, sizeof(dt))) {
         Dmsg1(50, "Cannot determine drive type for \"%s\"\n", ff->fname);
      } else {
         for (i = 0; i < ff->drivetypes.size(); ++i) {
            if (strcmp(dt, (char *)ff->drivetypes.get(i)) == 0) {
               Dmsg2(100, "Accepting drive type %s for \"%s\"\n", dt, ff->fname);
               accept = true;
               break;
            }
            Dmsg3(200, "drive type %s for \"%s\" does not match %s\n", dt,
                  ff->fname, ff->drivetypes.get(i));
         }
      }
   }
   return accept;
}

191
192
193
194
195
196
197
198
199
200
201
202
203
/*
 * This function determines whether we can use getattrlist()
 * It's odd, but we have to use the function to determine that...
 * Also, the man pages talk about things as if they were implemented.
 *
 * On Mac OS X, this succesfully differentiates between HFS+ and UFS
 * volumes, which makes me trust it is OK for others, too.
 */
static bool volume_has_attrlist(const char *fname)
{
#ifdef HAVE_DARWIN_OS
   struct statfs st;
   struct volinfo_struct {
Kern Sibbald's avatar
   
Kern Sibbald committed
204
205
      unsigned long length;               /* Mandatory field */
      vol_capabilities_attr_t info;       /* Volume capabilities */
206
207
208
209
210
211
212
213
214
   } vol;
   struct attrlist attrList;

   memset(&attrList, 0, sizeof(attrList));
   attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
   attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES;
   if (statfs(fname, &st) == 0) {
      /* We need to check on the mount point */
      if (getattrlist(st.f_mntonname, &attrList, &vol, sizeof(vol), FSOPT_NOFOLLOW) == 0
Kern Sibbald's avatar
   
Kern Sibbald committed
215
216
217
            && (vol.info.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)
            && (vol.info.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)) {
         return true;
218
219
220
221
222
223
      }
   }
#endif
   return false;
}

224
225
226
227
228
/*
 * check for BSD nodump flag
 */
static bool no_dump(JCR *jcr, FF_PKT *ff_pkt)
{
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
#ifdef HAVE_NODUMP
   bool ret = false; /* do backup */
   int attr;
   int fd = -1;
   if (ff_pkt->flags & FO_HONOR_NODUMP) {
      int fd = open(ff_pkt->fname, O_RDONLY);
      if (fd < 0) {
         Dmsg2(50, "Failed to open file: %s err: %d\n",
               ff_pkt->fname, errno);
         goto bail_out;
      }

      int ioctl_ret = ioctl(fd, FS_IOC_GETFLAGS, &attr);
      if (ioctl_ret < 0) {
         if (errno == ENOTTY) {
244
            Dmsg2(50, "Failed to check for NODUMP flag for file: %s errno: %d, "
245
                      "probably because of wrong filesystem used.\n",
246
                  ff_pkt->fname, errno);
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
         } else {
            Dmsg2(50, "Failed to send Getflags IOCTL for: %s err: %d\n",
                  ff_pkt->fname, errno);
         }
         goto bail_out;
      }

      /* Check if file has NODUMP flag set */
      ret = attr & FS_NODUMP_FL;
   }

bail_out:
   if (fd > 0) {
      close(fd);
   }
   Dmsg2(500, "fname: %s nodump flag: %d\n", ff_pkt->fname, ret);
   return ret;

#elif defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
266
267
268
269
270
271
272
   if ( (ff_pkt->flags & FO_HONOR_NODUMP) &&
        (ff_pkt->statp.st_flags & UF_NODUMP) ) {
      Jmsg(jcr, M_INFO, 1, _("     NODUMP flag set - will not process %s\n"),
           ff_pkt->fname);
      return true;                    /* do not backup this file */
   }
#endif
Eric Bollengier's avatar
Eric Bollengier committed
273
   return false; /* do backup */
274
275
}

276
277
278
279
280
281
/* check if a file have changed during backup and display an error */
bool has_file_changed(JCR *jcr, FF_PKT *ff_pkt)
{
   struct stat statp;
   Dmsg1(500, "has_file_changed fname=%s\n",ff_pkt->fname);

282
283
284
285
286
287
288
   if (ff_pkt->snapshot_convert_fct) {
      /* their is no reason to check for a file change inside a snapshot */
      /* but this has already highlighted some bugs in the past */
      /* if you want to optimize, uncomment the "return below" */
      // return false;
   }

289
290
291
292
   if (ff_pkt->type != FT_REG) { /* not a regular file */
      return false;
   }

293
   if (lstat(ff_pkt->snap_fname, &statp) != 0) {
294
      berrno be;
Kern Sibbald's avatar
Kern Sibbald committed
295
      Jmsg(jcr, M_WARNING, 0,
296
           _("Cannot stat file %s: ERR=%s\n"),ff_pkt->fname,be.bstrerror());
297
298
299
300
301
      return true;
   }

   if (statp.st_mtime != ff_pkt->statp.st_mtime) {
      Jmsg(jcr, M_ERROR, 0, _("%s mtime changed during backup.\n"), ff_pkt->fname);
Kern Sibbald's avatar
Kern Sibbald committed
302
303
      Dmsg3(50, "%s mtime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
            (int64_t)ff_pkt->statp.st_mtime, (int64_t)statp.st_mtime);
304
305
306
307
308
      return true;
   }

   if (statp.st_ctime != ff_pkt->statp.st_ctime) {
      Jmsg(jcr, M_ERROR, 0, _("%s ctime changed during backup.\n"), ff_pkt->fname);
Kern Sibbald's avatar
Kern Sibbald committed
309
310
      Dmsg3(50, "%s ctime (%lld) changed during backup (%lld).\n", ff_pkt->fname,
            (int64_t)ff_pkt->statp.st_ctime, (int64_t)statp.st_ctime);
311
312
313
      return true;
   }

Kern Sibbald's avatar
Kern Sibbald committed
314
   if ((int64_t)statp.st_size != (int64_t)ff_pkt->statp.st_size) {
315
      Jmsg(jcr, M_ERROR, 0, _("%s size of %lld changed during backup to %lld.\n"),ff_pkt->fname,
Kern Sibbald's avatar
Kern Sibbald committed
316
         (int64_t)ff_pkt->statp.st_size, (int64_t)statp.st_size);
Kern Sibbald's avatar
Kern Sibbald committed
317
      Dmsg3(50, "%s size (%lld) changed during backup (%lld).\n", ff_pkt->fname,
Kern Sibbald's avatar
Kern Sibbald committed
318
            (int64_t)ff_pkt->statp.st_size, (int64_t)statp.st_size);
319
320
321
322
323
324
      return true;
   }

   return false;
}

ricozz's avatar
ricozz committed
325
/*
326
 * For incremental/differential or accurate backups, we
327
 *   determine if the current file has changed.
ricozz's avatar
ricozz committed
328
 */
329
bool check_changes(JCR *jcr, FF_PKT *ff_pkt)
ricozz's avatar
ricozz committed
330
{
Kern Sibbald's avatar
Kern Sibbald committed
331
   /* in special mode (like accurate backup), the programmer can
ricozz's avatar
ricozz committed
332
333
334
335
336
337
    * choose his comparison function.
    */
   if (ff_pkt->check_fct) {
      return ff_pkt->check_fct(jcr, ff_pkt);
   }

338
   /* For normal backups (incr/diff), we use this default
ricozz's avatar
ricozz committed
339
340
341
342
    * behaviour
    */
   if (ff_pkt->incremental &&
       (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
343
        ((ff_pkt->flags & FO_MTIMEONLY) ||
Kern Sibbald's avatar
Kern Sibbald committed
344
         ff_pkt->statp.st_ctime < ff_pkt->save_time)))
ricozz's avatar
ricozz committed
345
346
   {
      return false;
Kern Sibbald's avatar
Kern Sibbald committed
347
   }
ricozz's avatar
ricozz committed
348
349
350
351

   return true;
}

352
353
354
static bool have_ignoredir(FF_PKT *ff_pkt)
{
   struct stat sb;
Kern Sibbald's avatar
Kern Sibbald committed
355
356
357
358
359
360
361
   char *ignoredir;

   /* Ensure that pointers are defined */
   if (!ff_pkt->fileset || !ff_pkt->fileset->incexe) {
      return false;
   }
   ignoredir = ff_pkt->fileset->incexe->ignoredir;
Kern Sibbald's avatar
Kern Sibbald committed
362

363
   if (ignoredir) {
364
365
      if (!ff_pkt->ignoredir_fname) {
         ff_pkt->ignoredir_fname = get_pool_memory(PM_FNAME);
366
      }
367
368
      Mmsg(ff_pkt->ignoredir_fname, "%s/%s", ff_pkt->fname, ignoredir);
      if (stat(ff_pkt->ignoredir_fname, &sb) == 0) {
369
370
371
         Dmsg2(100, "Directory '%s' ignored (found %s)\n",
               ff_pkt->fname, ignoredir);
         return true;      /* Just ignore this directory */
Kern Sibbald's avatar
Kern Sibbald committed
372
      }
373
374
375
376
   }
   return false;
}

Kern Sibbald's avatar
Kern Sibbald committed
377
/*
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
 * When the current file is a hardlink, the backup code can compute
 * the checksum and store it into the link_t structure.
 */
void
ff_pkt_set_link_digest(FF_PKT *ff_pkt,
                       int32_t digest_stream, const char *digest, uint32_t len)
{
   if (ff_pkt->linked && !ff_pkt->linked->digest) {     /* is a hardlink */
      ff_pkt->linked->digest = (char *) bmalloc(len);
      memcpy(ff_pkt->linked->digest, digest, len);
      ff_pkt->linked->digest_len = len;
      ff_pkt->linked->digest_stream = digest_stream;
   }
}

Kern Sibbald's avatar
Kern Sibbald committed
393
/*
394
 * Find a single file.
Kern Sibbald's avatar
Kern Sibbald committed
395
396
 * handle_file is the callback for handling the file.
 * p is the filename
397
398
 * parent_device is the device we are currently on
 * top_level is 1 when not recursing or 0 when
399
 *  descending into a directory.
Kern Sibbald's avatar
Kern Sibbald committed
400
401
 */
int
Kern Sibbald's avatar
Kern Sibbald committed
402
find_one_file(JCR *jcr, FF_PKT *ff_pkt,
403
               int handle_file(JCR *jcr, FF_PKT *ff, bool top_level),
404
               char *fname, char *snap_fname, dev_t parent_device, bool top_level)
Kern Sibbald's avatar
Kern Sibbald committed
405
{
Kern Sibbald's avatar
Kern Sibbald committed
406
407
   struct utimbuf restore_times;
   int rtn_stat;
Kern Sibbald's avatar
Kern Sibbald committed
408
   int len;
Kern Sibbald's avatar
Kern Sibbald committed
409

Kern Sibbald's avatar
Kern Sibbald committed
410
   ff_pkt->fname = ff_pkt->link = fname;
411
   ff_pkt->snap_fname = snap_fname;
Kern Sibbald's avatar
Kern Sibbald committed
412

413
   if (lstat(snap_fname, &ff_pkt->statp) != 0) {
Kern Sibbald's avatar
Kern Sibbald committed
414
415
416
       /* Cannot stat file */
       ff_pkt->type = FT_NOSTAT;
       ff_pkt->ff_errno = errno;
417
       return handle_file(jcr, ff_pkt, top_level);
Kern Sibbald's avatar
Kern Sibbald committed
418
419
   }

Kern Sibbald's avatar
Kern Sibbald committed
420
   Dmsg1(300, "File ----: %s\n", fname);
Kern Sibbald's avatar
Kern Sibbald committed
421

Kern Sibbald's avatar
Kern Sibbald committed
422
423
424
425
426
427
   /* Save current times of this directory in case we need to
    * reset them because the user doesn't want them changed.
    */
   restore_times.actime = ff_pkt->statp.st_atime;
   restore_times.modtime = ff_pkt->statp.st_mtime;

428
   /*
429
    * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
430
    */
431
432
   if (top_level) {
      if (!accept_fstype(ff_pkt, NULL)) {
Kern Sibbald's avatar
   
Kern Sibbald committed
433
         ff_pkt->type = FT_INVALIDFS;
434
435
         if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
            utime(snap_fname, &restore_times);  /* snap_fname == fname */
Kern Sibbald's avatar
   
Kern Sibbald committed
436
         }
437
438
439

         char fs[100];

Kern Sibbald's avatar
Kern Sibbald committed
440
         if (!fstype(ff_pkt, fs, sizeof(fs))) {
441
442
443
444
445
446
447
448
             bstrncpy(fs, "unknown", sizeof(fs));
         }

         Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
         return 1;      /* Just ignore this error - or the whole backup is cancelled */
      }
      if (!accept_drivetype(ff_pkt, NULL)) {
         ff_pkt->type = FT_INVALIDDT;
449
450
         if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
            utime(snap_fname, &restore_times); /* snap_fname == fname */
451
452
453
454
455
456
457
458
459
         }

         char dt[100];

         if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
             bstrncpy(dt, "unknown", sizeof(dt));
         }

         Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
Kern Sibbald's avatar
   
Kern Sibbald committed
460
         return 1;      /* Just ignore this error - or the whole backup is cancelled */
461
      }
462
      ff_pkt->volhas_attrlist = volume_has_attrlist(snap_fname);
463
   }
464
465
466
467
468

   /*
    * Ignore this entry if no_dump() returns true
    */
   if (no_dump(jcr, ff_pkt)) {
Eric Bollengier's avatar
Eric Bollengier committed
469
470
471
           Dmsg1(100, "'%s' ignored (NODUMP flag set)\n",
                 ff_pkt->fname);
           return 1;
472
473
   }

474
   /*
Kern Sibbald's avatar
Kern Sibbald committed
475
476
477
478
    * If this is an Incremental backup, see if file was modified
    * since our last "save_time", presumably the last Full save
    * or Incremental.
    */
Kern Sibbald's avatar
Kern Sibbald committed
479
480
   if (   !S_ISDIR(ff_pkt->statp.st_mode)
       && !check_changes(jcr, ff_pkt))
ricozz's avatar
ricozz committed
481
482
483
484
   {
      Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
      ff_pkt->type = FT_NOCHG;
      return handle_file(jcr, ff_pkt, top_level);
Kern Sibbald's avatar
Kern Sibbald committed
485
   }
Kern Sibbald's avatar
Kern Sibbald committed
486

487
#ifdef HAVE_DARWIN_OS
488
   if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
Kern Sibbald's avatar
   
Kern Sibbald committed
489
         && S_ISREG(ff_pkt->statp.st_mode)) {
490
491
492
493
494
495
       /* TODO: initialise attrList once elsewhere? */
       struct attrlist attrList;
       memset(&attrList, 0, sizeof(attrList));
       attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
       attrList.commonattr = ATTR_CMN_FNDRINFO;
       attrList.fileattr = ATTR_FILE_RSRCLENGTH;
496
       if (getattrlist(snap_fname, &attrList, &ff_pkt->hfsinfo,
Kern Sibbald's avatar
   
Kern Sibbald committed
497
498
499
                sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
          ff_pkt->type = FT_NOSTAT;
          ff_pkt->ff_errno = errno;
500
          return handle_file(jcr, ff_pkt, top_level);
501
       }
Kern Sibbald's avatar
Kern Sibbald committed
502
       return -1; /* ignore */
503
504
505
   }
#endif

506
   ff_pkt->LinkFI = 0;
507
   /*
Kern Sibbald's avatar
Kern Sibbald committed
508
509
510
    * Handle hard linked files
    *
    * Maintain a list of hard linked files already backed up. This
511
    *  allows us to ensure that the data of each file gets backed
Kern Sibbald's avatar
Kern Sibbald committed
512
513
    *  up only once.
    */
514
515
   if (!(ff_pkt->flags & FO_NO_HARDLINK)
       && ff_pkt->statp.st_nlink > 1
Kern Sibbald's avatar
Kern Sibbald committed
516
       && (S_ISREG(ff_pkt->statp.st_mode)
Kern Sibbald's avatar
   
Kern Sibbald committed
517
518
519
520
           || S_ISCHR(ff_pkt->statp.st_mode)
           || S_ISBLK(ff_pkt->statp.st_mode)
           || S_ISFIFO(ff_pkt->statp.st_mode)
           || S_ISSOCK(ff_pkt->statp.st_mode))) {
Kern Sibbald's avatar
Kern Sibbald committed
521
522

       struct f_link *lp;
523
       if (ff_pkt->linkhash == NULL) {
524
525
           ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
           memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
526
527
       }
       const int linkhash = LINKHASH(ff_pkt->statp);
Kern Sibbald's avatar
Kern Sibbald committed
528

529
      /* Search link list of hard linked files */
530
       for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
Kern Sibbald's avatar
   
Kern Sibbald committed
531
532
         if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
             lp->dev == (dev_t)ff_pkt->statp.st_dev) {
Kern Sibbald's avatar
Kern Sibbald committed
533
             /* If we have already backed up the hard linked file don't do it again */
Kern Sibbald's avatar
   
Kern Sibbald committed
534
             if (strcmp(lp->name, fname) == 0) {
535
                Dmsg2(400, "== Name identical skip FI=%d file=%s\n", lp->FileIndex, fname);
Kern Sibbald's avatar
   
Kern Sibbald committed
536
537
538
539
540
                return 1;             /* ignore */
             }
             ff_pkt->link = lp->name;
             ff_pkt->type = FT_LNKSAVED;       /* Handle link, file already saved */
             ff_pkt->LinkFI = lp->FileIndex;
541
             ff_pkt->linked = 0;
542
543
544
             ff_pkt->digest = lp->digest;
             ff_pkt->digest_stream = lp->digest_stream;
             ff_pkt->digest_len = lp->digest_len;
545
             rtn_stat = handle_file(jcr, ff_pkt, top_level);
Kern Sibbald's avatar
Kern Sibbald committed
546
             Dmsg3(400, "FT_LNKSAVED FI=%d LinkFI=%d file=%s\n",
547
548
                ff_pkt->FileIndex, lp->FileIndex, lp->name);
             return rtn_stat;
Kern Sibbald's avatar
   
Kern Sibbald committed
549
         }
550
551

      /* File not previously dumped. Chain it into our list. */
Kern Sibbald's avatar
Kern Sibbald committed
552
553
      len = strlen(fname) + 1;
      lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
554
555
556
      lp->digest = NULL;                /* set later */
      lp->digest_stream = 0;            /* set later */
      lp->digest_len = 0;               /* set later */
557
558
      lp->ino = ff_pkt->statp.st_ino;
      lp->dev = ff_pkt->statp.st_dev;
559
      lp->FileIndex = 0;                  /* set later */
Kern Sibbald's avatar
Kern Sibbald committed
560
      bstrncpy(lp->name, fname, len);
561
562
      lp->next = ff_pkt->linkhash[linkhash];
      ff_pkt->linkhash[linkhash] = lp;
Kern Sibbald's avatar
   
Kern Sibbald committed
563
      ff_pkt->linked = lp;            /* mark saved link */
564
      Dmsg2(400, "added to hash FI=%d file=%s\n", ff_pkt->FileIndex, lp->name);
565
566
   } else {
      ff_pkt->linked = NULL;
Kern Sibbald's avatar
Kern Sibbald committed
567
568
569
570
   }

   /* This is not a link to a previously dumped file, so dump it.  */
   if (S_ISREG(ff_pkt->statp.st_mode)) {
571
      boffset_t sizeleft;
572
573
574
575

      sizeleft = ff_pkt->statp.st_size;

      /* Don't bother opening empty, world readable files.  Also do not open
Kern Sibbald's avatar
   
Kern Sibbald committed
576
         files when archive is meant for /dev/null.  */
577
      if (ff_pkt->null_output_device || (sizeleft == 0
Kern Sibbald's avatar
   
Kern Sibbald committed
578
579
              && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
         ff_pkt->type = FT_REGE;
580
581
582
583
         if (ff_pkt->stat_update) {
            /* No need do the metadata update for empty files, perform usual backup */
            ff_pkt->stat_update = 0;
         }
584
      } else {
Kern Sibbald's avatar
   
Kern Sibbald committed
585
         ff_pkt->type = FT_REG;
586
      }
587
      rtn_stat = handle_file(jcr, ff_pkt, top_level);
588
      if (ff_pkt->linked) {
Kern Sibbald's avatar
   
Kern Sibbald committed
589
         ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
590
      }
Kern Sibbald's avatar
Kern Sibbald committed
591
      Dmsg3(400, "FT_REG FI=%d linked=%d file=%s\n", ff_pkt->FileIndex,
592
         ff_pkt->linked ? 1 : 0, fname);
593
594
      if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
         utime(snap_fname, &restore_times); /* snap_fname == fname */
Kern Sibbald's avatar
Kern Sibbald committed
595
      }
596
597
      return rtn_stat;

Kern Sibbald's avatar
Kern Sibbald committed
598

599
   } else if (S_ISLNK(ff_pkt->statp.st_mode)) {  /* soft link */
600
      int size;
601
      char *buffer = (char *)alloca(path_max + name_max + 102);
602

603
      size = readlink(snap_fname, buffer, path_max + name_max + 101);
604
      if (size < 0) {
Kern Sibbald's avatar
   
Kern Sibbald committed
605
606
607
         /* Could not follow link */
         ff_pkt->type = FT_NOFOLLOW;
         ff_pkt->ff_errno = errno;
608
         rtn_stat = handle_file(jcr, ff_pkt, top_level);
Kern Sibbald's avatar
   
Kern Sibbald committed
609
610
611
612
         if (ff_pkt->linked) {
            ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
         }
         return rtn_stat;
613
614
      }
      buffer[size] = 0;
Kern Sibbald's avatar
   
Kern Sibbald committed
615
616
      ff_pkt->link = buffer;          /* point to link */
      ff_pkt->type = FT_LNK;          /* got a real link */
617
      rtn_stat = handle_file(jcr, ff_pkt, top_level);
618
      if (ff_pkt->linked) {
Kern Sibbald's avatar
   
Kern Sibbald committed
619
         ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
620
621
      }
      return rtn_stat;
Kern Sibbald's avatar
Kern Sibbald committed
622
623

   } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
624
      DIR *directory;
625
      POOL_MEM dname(PM_FNAME);
626
627
      char *link;
      int link_len;
628
      int len;
629
630
      int status;
      dev_t our_device = ff_pkt->statp.st_dev;
631
      bool recurse = true;
Kern Sibbald's avatar
   
Kern Sibbald committed
632
      bool volhas_attrlist = ff_pkt->volhas_attrlist;    /* Remember this if we recurse */
633

634
635
636
637
      /*
       * Ignore this directory and everything below if the file .nobackup
       * (or what is defined for IgnoreDir in this fileset) exists
       */
638
639
      if (have_ignoredir(ff_pkt)) {
         return 1; /* Just ignore this directory */
640
641
      }

642
      /* Build a canonical directory name with a trailing slash in link var */
643
644
645
646
647
      len = strlen(fname);
      link_len = len + 200;
      link = (char *)bmalloc(link_len + 2);
      bstrncpy(link, fname, link_len);
      /* Strip all trailing slashes */
648
      while (len >= 1 && IsPathSeparator(link[len - 1]))
Kern Sibbald's avatar
   
Kern Sibbald committed
649
        len--;
650
651
652
653
      link[len++] = '/';             /* add back one */
      link[len] = 0;

      ff_pkt->link = link;
ricozz's avatar
ricozz committed
654
655
      if (!check_changes(jcr, ff_pkt)) {
         /* Incremental/Full+Base option, directory entry not changed */
Kern Sibbald's avatar
   
Kern Sibbald committed
656
         ff_pkt->type = FT_DIRNOCHG;
657
      } else {
Kern Sibbald's avatar
   
Kern Sibbald committed
658
         ff_pkt->type = FT_DIRBEGIN;
Kern Sibbald's avatar
Kern Sibbald committed
659
      }
660
661
      /*
       * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
Kern Sibbald's avatar
Kern Sibbald committed
662
       *  if st_rdev is 2, it is a mount point
663
       */
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
#if defined(HAVE_WIN32)
      /*
       * A reparse point (WIN32_REPARSE_POINT)
       *  is something special like one of the following:
       *  IO_REPARSE_TAG_DFS              0x8000000A
       *  IO_REPARSE_TAG_DFSR             0x80000012
       *  IO_REPARSE_TAG_HSM              0xC0000004
       *  IO_REPARSE_TAG_HSM2             0x80000006
       *  IO_REPARSE_TAG_SIS              0x80000007
       *  IO_REPARSE_TAG_SYMLINK          0xA000000C
       *
       * A junction point is a:
       *  IO_REPARSE_TAG_MOUNT_POINT      0xA0000003
       * which can be either a link to a Volume (WIN32_MOUNT_POINT)
       * or a link to a directory (WIN32_JUNCTION_POINT)
       *
       * Ignore WIN32_REPARSE_POINT and WIN32_JUNCTION_POINT
       */
      if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
         ff_pkt->type = FT_REPARSE;
684
685
686
      } else if (ff_pkt->statp.st_rdev == WIN32_JUNCTION_POINT ||
            (ff_pkt->statp.st_rdev == WIN32_SYMLINK_POINT && S_ISDIR(ff_pkt->statp.st_mode))) {
         // Handle SYMLINK DIRECTORY like a Junction, BackupRead() does all the work
687
688
689
         ff_pkt->type = FT_JUNCTION;
      }
#endif
690
      /*
691
       * Note, we return the directory to the calling program (handle_file)
692
       * when we first see the directory (FT_DIRBEGIN.
693
694
695
696
697
       * This allows the program to apply matches and make a
       * choice whether or not to accept it.  If it is accepted, we
       * do not immediately save it, but do so only after everything
       * in the directory is seen (i.e. the FT_DIREND).
       */
698
      rtn_stat = handle_file(jcr, ff_pkt, top_level);
699
700
      if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE ||
          ff_pkt->type == FT_JUNCTION) {   /* ignore or error status */
Kern Sibbald's avatar
   
Kern Sibbald committed
701
702
         free(link);
         return rtn_stat;
Kern Sibbald's avatar
Kern Sibbald committed
703
      }
704
      /* Done with DIRBEGIN, next call will be DIREND */
Kern Sibbald's avatar
Kern Sibbald committed
705
      if (ff_pkt->type == FT_DIRBEGIN) {
Kern Sibbald's avatar
   
Kern Sibbald committed
706
         ff_pkt->type = FT_DIREND;
707
      }
708
709
710
711
712
713
714
715
716

      /*
       * Create a temporary ff packet for this directory
       *   entry, and defer handling the directory until
       *   we have recursed into it.  This saves the
       *   directory after all files have been processed, and
       *   during the restore, the directory permissions will
       *   be reset after all the files have been restored.
       */
717
      Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
718
      FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
719
720
721
722
723
724
725
726
727
728
#if defined(HAVE_WIN32)
      if (dir_ff_pkt->root_of_volume || (ff_pkt->fname[3]=='\0' && ff_pkt->fname[1]==':')) {
         /* tell "restore" that this is a root directory and that it should not
          * restore the Windows hidden and system attribute
          */
         dir_ff_pkt->statp.st_rdev = WIN32_ROOT_POINT;
         // don't "propagate" the "root_of_volume" property to sub-directories
         ff_pkt->root_of_volume = false;
      }
#endif
729

730
      /*
731
       * Do not descend into subdirectories (recurse) if the
732
       * user has turned it off for this directory.
733
734
735
736
       *
       * If we are crossing file systems, we are either not allowed
       * to cross, or we may be restricted by a list of permitted
       * file systems.
737
       */
Kern Sibbald's avatar
Kern Sibbald committed
738
      bool is_win32_mount_point = false;
739
740
741
#if defined(HAVE_WIN32)
      is_win32_mount_point = ff_pkt->statp.st_rdev == WIN32_MOUNT_POINT;
#endif
Kern Sibbald's avatar
04Jul05    
Kern Sibbald committed
742
      if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
Kern Sibbald's avatar
   
Kern Sibbald committed
743
744
         ff_pkt->type = FT_NORECURSE;
         recurse = false;
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
      } else if (!top_level &&
            (parent_device != ff_pkt->statp.st_dev || is_win32_mount_point)) {
            /* Nested mountpoint in a Windows snapshot works like the original
             * mountpoint and redirect to the Live filesystem (not a snapshot)
             *
             * In Linux the directory is not a mountpoint anymore and we backup
             * the content of the directory that is normally empty.
             */
         if (is_win32_mount_point && ff_pkt->flags & FO_MULTIFS) {
            return 1;           /* Ignore this dir, it will backed up later */

         } else if (is_win32_mount_point) {
            recurse = false;    /* We backup the mount point itself, but nothing more */

         } else if(!(ff_pkt->flags & FO_MULTIFS)) {
Kern Sibbald's avatar
   
Kern Sibbald committed
760
761
            ff_pkt->type = FT_NOFSCHG;
            recurse = false;
762

Kern Sibbald's avatar
   
Kern Sibbald committed
763
764
765
         } else if (!accept_fstype(ff_pkt, NULL)) {
            ff_pkt->type = FT_INVALIDFS;
            recurse = false;
766

Kern Sibbald's avatar
   
Kern Sibbald committed
767
         } else {
768
            ff_pkt->volhas_attrlist = volume_has_attrlist(snap_fname);
Kern Sibbald's avatar
   
Kern Sibbald committed
769
         }
770
      }
771
      /* If not recursing, just backup dir and return */
772
      if (!recurse) {
773
         rtn_stat = handle_file(jcr, ff_pkt, top_level);
Kern Sibbald's avatar
   
Kern Sibbald committed
774
775
776
777
778
         if (ff_pkt->linked) {
            ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
         }
         free(link);
         free_dir_ff_pkt(dir_ff_pkt);
Kern Sibbald's avatar
Kern Sibbald committed
779
         ff_pkt->link = ff_pkt->fname;     /* reset "link" */
780
781
         if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
            utime(snap_fname, &restore_times); /* snap_fname==fname */
Kern Sibbald's avatar
   
Kern Sibbald committed
782
783
         }
         return rtn_stat;
784
      }
785
786
787

      ff_pkt->link = ff_pkt->fname;     /* reset "link" */

788
      /*
789
       * Descend into or "recurse" into the directory to read
790
       *   all the files in it.
791
792
       */
      errno = 0;
793
      if ((directory = opendir(snap_fname)) == NULL) {
Kern Sibbald's avatar
   
Kern Sibbald committed
794
795
         ff_pkt->type = FT_NOOPEN;
         ff_pkt->ff_errno = errno;
796
         rtn_stat = handle_file(jcr, ff_pkt, top_level);
Kern Sibbald's avatar
   
Kern Sibbald committed
797
798
799
800
801
802
         if (ff_pkt->linked) {
            ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
         }
         free(link);
         free_dir_ff_pkt(dir_ff_pkt);
         return rtn_stat;
803
804
      }

805
806
807
808
809
810
811
812
813
814
815
816
      /* Build a canonical directory name of the file inside the snapshot,
       * with a trailing slash in link var */
      int slen = strlen(snap_fname);
      int snap_len = slen + 200;
      char *snap_link = (char *)bmalloc(snap_len + 2);
      bstrncpy(snap_link, snap_fname, snap_len);
      /* Strip all trailing slashes */
      while (slen >= 1 && IsPathSeparator(snap_link[slen - 1]))
        slen--;
      snap_link[slen++] = '/';             /* add back one */
      snap_link[slen] = 0;

817
      /*
818
819
820
       * Process all files in this directory entry (recursing).
       *    This would possibly run faster if we chdir to the directory
       *    before traversing it.
821
822
       */
      rtn_stat = 1;
823
      while (!job_canceled(jcr)) {
824
         char *p, *q, *s;
825
         int l;
Kern Sibbald's avatar
   
Kern Sibbald committed
826
         int i;
827

828
829
830
831
         status = breaddir(directory, dname.addr());
         if (status != 0) {
            /* error or end of directory */
//          Dmsg1(99, "breaddir returned stat=%d\n", status);
Kern Sibbald's avatar
   
Kern Sibbald committed
832
833
            break;
         }
834
         p = dname.c_str();
Kern Sibbald's avatar
Kern Sibbald committed
835
836
837
         /* Skip `.', `..', and excluded file names.  */
         if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
             (p[1] == '.' && p[2] == '\0')))) {
Kern Sibbald's avatar
   
Kern Sibbald committed
838
839
            continue;
         }
840
841
842
         l = strlen(dname.c_str());
         if (l + len >= link_len) {
             link_len = len + l + 1;
Kern Sibbald's avatar
   
Kern Sibbald committed
843
844
             link = (char *)brealloc(link, link_len + 1);
         }
845
846
847
848
849
         if (l + slen >= snap_len) {
             snap_len = slen + l + 1;
             snap_link = (char *)brealloc(snap_link, snap_len + 1);
         }

Kern Sibbald's avatar
   
Kern Sibbald committed
850
         q = link + len;
851
         s = snap_link + slen;
852
         for (i=0; i < l; i++) {
853
            *q++ = *s++ =*p++;
Kern Sibbald's avatar
   
Kern Sibbald committed
854
         }
855
         *q = *s = 0;
Kern Sibbald's avatar
   
Kern Sibbald committed
856
         if (!file_is_excluded(ff_pkt, link)) {
857
            rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, snap_link, our_device, false);
Kern Sibbald's avatar
   
Kern Sibbald committed
858
859
860
861
            if (ff_pkt->linked) {
               ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
            }
         }
862

863
864
865
      }
      closedir(directory);
      free(link);
866
      free(snap_link);
867

868
869
870
871
872
873
874
      /*
       * Now that we have recursed through all the files in the
       *  directory, we "save" the directory so that after all
       *  the files are restored, this entry will serve to reset
       *  the directory modes and dates.  Temp directory values
       *  were used without this record.
       */
875
      handle_file(jcr, dir_ff_pkt, top_level);       /* handle directory entry */
876
      if (ff_pkt->linked) {
Kern Sibbald's avatar
   
Kern Sibbald committed
877
         ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
878
      }
879
      free_dir_ff_pkt(dir_ff_pkt);
880

881
882
      if (ff_pkt->flags & FO_KEEPATIME && !ff_pkt->snapshot_convert_fct) {
         utime(snap_fname, &restore_times);
883
      }
884
      ff_pkt->volhas_attrlist = volhas_attrlist;      /* Restore value in case it changed. */
885
      return rtn_stat;
Kern Sibbald's avatar
Kern Sibbald committed
886
887
   } /* end check for directory */

Kern Sibbald's avatar
Kern Sibbald committed
888
889
   /*
    * If it is explicitly mentioned (i.e. top_level) and is
Kern Sibbald's avatar
Kern Sibbald committed
890
891
    *  a block device, we do a raw backup of it or if it is
    *  a fifo, we simply read it.
Kern Sibbald's avatar
Kern Sibbald committed
892
    */
893
#ifdef HAVE_FREEBSD_OS
Kern Sibbald's avatar
Kern Sibbald committed
894
895
   /*
    * On FreeBSD, all block devices are character devices, so
Kern Sibbald's avatar
   
Kern Sibbald committed
896
897
    *   to be able to read a raw disk, we need the check for
    *   a character device.
898
899
    * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
    * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
Kern Sibbald's avatar
Kern Sibbald committed
900
901
902
    */
   if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
#else
Kern Sibbald's avatar
Kern Sibbald committed
903
   if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
Kern Sibbald's avatar
Kern Sibbald committed
904
#endif
Kern Sibbald's avatar
   
Kern Sibbald committed
905
      ff_pkt->type = FT_RAW;          /* raw partition */
Kern Sibbald's avatar
Kern Sibbald committed
906
   } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
Kern Sibbald's avatar
   
Kern Sibbald committed
907
              ff_pkt->flags & FO_READFIFO) {
Kern Sibbald's avatar
Kern Sibbald committed
908
      ff_pkt->type = FT_FIFO;
Kern Sibbald's avatar
Kern Sibbald committed
909
910
911
912
   } else {
      /* The only remaining types are special (character, ...) files */
      ff_pkt->type = FT_SPEC;
   }
913
   rtn_stat = handle_file(jcr, ff_pkt, top_level);
914
915
916
917
   if (ff_pkt->linked) {
      ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
   }
   return rtn_stat;
Kern Sibbald's avatar
Kern Sibbald committed
918
919
}

Kern Sibbald's avatar
Kern Sibbald committed
920
int term_find_one(FF_PKT *ff)
Kern Sibbald's avatar
Kern Sibbald committed
921
{
Kern Sibbald's avatar
Kern Sibbald committed
922
   struct f_link *lp, *lc;
Kern Sibbald's avatar
Kern Sibbald committed
923
   int count = 0;
924
   int i;
925

Kern Sibbald's avatar
Kern Sibbald committed
926

927
928
929
   if (ff->linkhash == NULL) return 0;

   for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
Kern Sibbald's avatar
Kern Sibbald committed
930
   /* Free up list of hard linked files */
931
932
933
934
935
936
937
938
939
940
941
      lp = ff->linkhash[i];
      while (lp) {
         lc = lp;
         lp = lp->next;
         if (lc) {
            if (lc->digest) {
               free(lc->digest);
            }
            free(lc);
            count++;
         }
Kern Sibbald's avatar
Kern Sibbald committed
942
      }
943
      ff->linkhash[i] = NULL;
944
945
946
   }
   free(ff->linkhash);
   ff->linkhash = NULL;
Kern Sibbald's avatar
Kern Sibbald committed
947
   return count;
Kern Sibbald's avatar
Kern Sibbald committed
948
}