2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 This file was derived from GNU TAR source code. Except for a few key
31 ideas, it has been entirely rewritten for Bacula.
35 Thanks to the TAR programmers.
44 #include <sys/param.h>
45 #include <sys/mount.h>
49 extern int32_t name_max; /* filename max length */
50 extern int32_t path_max; /* path name max length */
53 * Structure for keeping track of hard linked files, we
54 * keep an entry for each hardlinked file that we save,
55 * which is the first one found. For all the other files that
56 * are linked to this one, we save only the directory
57 * entry so we can link it.
61 dev_t dev; /* device */
62 ino_t ino; /* inode with device is unique */
64 uint32_t FileIndex; /* Bacula FileIndex of this file */
65 char name[1]; /* The name */
68 typedef struct f_link link_t;
69 #define LINK_HASHTABLE_BITS 16
70 #define LINK_HASHTABLE_SIZE (1<<LINK_HASHTABLE_BITS)
71 #define LINK_HASHTABLE_MASK (LINK_HASHTABLE_SIZE-1)
73 static inline int LINKHASH(const struct stat &info)
75 int hash = info.st_dev;
76 unsigned long long i = info.st_ino;
84 return hash & LINK_HASHTABLE_MASK;
88 * Create a new directory Find File packet, but copy
89 * some of the essential info from the current packet.
90 * However, be careful to zero out the rest of the
93 static FF_PKT *new_dir_ff_pkt(FF_PKT *ff_pkt)
95 FF_PKT *dir_ff_pkt = (FF_PKT *)bmalloc(sizeof(FF_PKT));
96 memcpy(dir_ff_pkt, ff_pkt, sizeof(FF_PKT));
97 dir_ff_pkt->fname = bstrdup(ff_pkt->fname);
98 dir_ff_pkt->link = bstrdup(ff_pkt->link);
99 dir_ff_pkt->sys_fname = get_pool_memory(PM_FNAME);
100 dir_ff_pkt->included_files_list = NULL;
101 dir_ff_pkt->excluded_files_list = NULL;
102 dir_ff_pkt->excluded_paths_list = NULL;
103 dir_ff_pkt->linkhash = NULL;
104 dir_ff_pkt->fname_save = NULL;
105 dir_ff_pkt->link_save = NULL;
110 * Free the temp directory ff_pkt
112 static void free_dir_ff_pkt(FF_PKT *dir_ff_pkt)
114 free(dir_ff_pkt->fname);
115 free(dir_ff_pkt->link);
116 free_pool_memory(dir_ff_pkt->sys_fname);
117 if (dir_ff_pkt->fname_save) {
118 free_pool_memory(dir_ff_pkt->fname_save);
120 if (dir_ff_pkt->link_save) {
121 free_pool_memory(dir_ff_pkt->link_save);
127 * Check to see if we allow the file system type of a file or directory.
128 * If we do not have a list of file system types, we accept anything.
130 static int accept_fstype(FF_PKT *ff, void *dummy) {
135 if (ff->fstypes.size()) {
137 if (!fstype(ff->fname, fs, sizeof(fs))) {
138 Dmsg1(50, "Cannot determine file system type for \"%s\"\n", ff->fname);
140 for (i = 0; i < ff->fstypes.size(); ++i) {
141 if (strcmp(fs, (char *)ff->fstypes.get(i)) == 0) {
142 Dmsg2(100, "Accepting fstype %s for \"%s\"\n", fs, ff->fname);
146 Dmsg3(200, "fstype %s for \"%s\" does not match %s\n", fs,
147 ff->fname, ff->fstypes.get(i));
155 * Check to see if we allow the drive type of a file or directory.
156 * If we do not have a list of drive types, we accept anything.
158 static int accept_drivetype(FF_PKT *ff, void *dummy) {
163 if (ff->drivetypes.size()) {
165 if (!drivetype(ff->fname, dt, sizeof(dt))) {
166 Dmsg1(50, "Cannot determine drive type for \"%s\"\n", ff->fname);
168 for (i = 0; i < ff->drivetypes.size(); ++i) {
169 if (strcmp(dt, (char *)ff->drivetypes.get(i)) == 0) {
170 Dmsg2(100, "Accepting drive type %s for \"%s\"\n", dt, ff->fname);
174 Dmsg3(200, "drive type %s for \"%s\" does not match %s\n", dt,
175 ff->fname, ff->drivetypes.get(i));
183 * This function determines whether we can use getattrlist()
184 * It's odd, but we have to use the function to determine that...
185 * Also, the man pages talk about things as if they were implemented.
187 * On Mac OS X, this succesfully differentiates between HFS+ and UFS
188 * volumes, which makes me trust it is OK for others, too.
190 static bool volume_has_attrlist(const char *fname)
192 #ifdef HAVE_DARWIN_OS
194 struct volinfo_struct {
195 unsigned long length; /* Mandatory field */
196 vol_capabilities_attr_t info; /* Volume capabilities */
198 struct attrlist attrList;
200 memset(&attrList, 0, sizeof(attrList));
201 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
202 attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES;
203 if (statfs(fname, &st) == 0) {
204 /* We need to check on the mount point */
205 if (getattrlist(st.f_mntonname, &attrList, &vol, sizeof(vol), FSOPT_NOFOLLOW) == 0
206 && (vol.info.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)
207 && (vol.info.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)) {
215 /* check if a file have changed during backup and display an error */
216 bool has_file_changed(JCR *jcr, FF_PKT *ff_pkt)
219 Dmsg1(500, "has_file_changed fname=%s\n",ff_pkt->fname);
221 if (ff_pkt->type != FT_REG) { /* not a regular file */
225 if (lstat(ff_pkt->fname, &statp) != 0) {
227 Jmsg(jcr, M_WARNING, 0,
228 _("Cannot stat file %s: ERR=%s\n"),ff_pkt->fname,be.bstrerror());
232 if (statp.st_mtime != ff_pkt->statp.st_mtime) {
233 /* TODO: add time of changes */
234 Jmsg(jcr, M_ERROR, 0, _("%s mtime changed during backup.\n"), ff_pkt->fname);
238 if (statp.st_ctime != ff_pkt->statp.st_ctime) {
239 /* TODO: add time of changes */
240 Jmsg(jcr, M_ERROR, 0, _("%s ctime changed during backup.\n"), ff_pkt->fname);
244 if (statp.st_size != ff_pkt->statp.st_size) {
245 /* TODO: add size change */
246 Jmsg(jcr, M_ERROR, 0, _("%s size changed during backup.\n"),ff_pkt->fname);
250 if ((statp.st_blksize != ff_pkt->statp.st_blksize) ||
251 (statp.st_blocks != ff_pkt->statp.st_blocks)) {
252 /* TODO: add size change */
253 Jmsg(jcr, M_ERROR, 0, _("%s size changed during backup.\n"),ff_pkt->fname);
261 * In incremental/diffential or accurate backup, we
262 * say if the current file has changed.
264 static bool check_changes(JCR *jcr, FF_PKT *ff_pkt)
266 /* in special mode (like accurate backup), user can
267 * choose his comparison function.
269 if (ff_pkt->check_fct) {
270 return ff_pkt->check_fct(jcr, ff_pkt);
273 /* in normal modes (incr/diff), we use this default
276 if (ff_pkt->incremental &&
277 (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
278 ((ff_pkt->flags & FO_MTIMEONLY) ||
279 ff_pkt->statp.st_ctime < ff_pkt->save_time)))
288 * Find a single file.
289 * handle_file is the callback for handling the file.
291 * parent_device is the device we are currently on
292 * top_level is 1 when not recursing or 0 when
293 * descending into a directory.
296 find_one_file(JCR *jcr, FF_PKT *ff_pkt,
297 int handle_file(JCR *jcr, FF_PKT *ff, bool top_level),
298 char *fname, dev_t parent_device, bool top_level)
300 struct utimbuf restore_times;
304 ff_pkt->fname = ff_pkt->link = fname;
306 if (lstat(fname, &ff_pkt->statp) != 0) {
307 /* Cannot stat file */
308 ff_pkt->type = FT_NOSTAT;
309 ff_pkt->ff_errno = errno;
310 return handle_file(jcr, ff_pkt, top_level);
313 Dmsg1(300, "File ----: %s\n", fname);
315 /* Save current times of this directory in case we need to
316 * reset them because the user doesn't want them changed.
318 restore_times.actime = ff_pkt->statp.st_atime;
319 restore_times.modtime = ff_pkt->statp.st_mtime;
322 * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
325 if (!accept_fstype(ff_pkt, NULL)) {
326 ff_pkt->type = FT_INVALIDFS;
327 if (ff_pkt->flags & FO_KEEPATIME) {
328 utime(fname, &restore_times);
333 if (!fstype(ff_pkt->fname, fs, sizeof(fs))) {
334 bstrncpy(fs, "unknown", sizeof(fs));
337 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
338 return 1; /* Just ignore this error - or the whole backup is cancelled */
340 if (!accept_drivetype(ff_pkt, NULL)) {
341 ff_pkt->type = FT_INVALIDDT;
342 if (ff_pkt->flags & FO_KEEPATIME) {
343 utime(fname, &restore_times);
348 if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
349 bstrncpy(dt, "unknown", sizeof(dt));
352 Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
353 return 1; /* Just ignore this error - or the whole backup is cancelled */
355 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
358 * If this is an Incremental backup, see if file was modified
359 * since our last "save_time", presumably the last Full save
362 if ( ff_pkt->incremental
363 && !S_ISDIR(ff_pkt->statp.st_mode)
364 && !check_changes(jcr, ff_pkt))
366 Dmsg1(500, "Non-directory incremental: %s\n", ff_pkt->fname);
367 ff_pkt->type = FT_NOCHG;
368 return handle_file(jcr, ff_pkt, top_level);
371 #ifdef HAVE_DARWIN_OS
372 if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
373 && S_ISREG(ff_pkt->statp.st_mode)) {
374 /* TODO: initialise attrList once elsewhere? */
375 struct attrlist attrList;
376 memset(&attrList, 0, sizeof(attrList));
377 attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
378 attrList.commonattr = ATTR_CMN_FNDRINFO;
379 attrList.fileattr = ATTR_FILE_RSRCLENGTH;
380 if (getattrlist(fname, &attrList, &ff_pkt->hfsinfo,
381 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
382 ff_pkt->type = FT_NOSTAT;
383 ff_pkt->ff_errno = errno;
384 return handle_file(jcr, ff_pkt, top_level);
391 * Handle hard linked files
393 * Maintain a list of hard linked files already backed up. This
394 * allows us to ensure that the data of each file gets backed
397 if (!(ff_pkt->flags & FO_NO_HARDLINK)
398 && ff_pkt->statp.st_nlink > 1
399 && (S_ISREG(ff_pkt->statp.st_mode)
400 || S_ISCHR(ff_pkt->statp.st_mode)
401 || S_ISBLK(ff_pkt->statp.st_mode)
402 || S_ISFIFO(ff_pkt->statp.st_mode)
403 || S_ISSOCK(ff_pkt->statp.st_mode))) {
406 if (ff_pkt->linkhash == NULL) {
407 ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
408 memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
410 const int linkhash = LINKHASH(ff_pkt->statp);
412 /* Search link list of hard linked files */
413 for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
414 if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
415 lp->dev == (dev_t)ff_pkt->statp.st_dev) {
416 /* If we have already backed up the hard linked file don't do it again */
417 if (strcmp(lp->name, fname) == 0) {
418 return 1; /* ignore */
420 ff_pkt->link = lp->name;
421 ff_pkt->type = FT_LNKSAVED; /* Handle link, file already saved */
422 ff_pkt->LinkFI = lp->FileIndex;
423 return handle_file(jcr, ff_pkt, top_level);
426 /* File not previously dumped. Chain it into our list. */
427 len = strlen(fname) + 1;
428 lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
429 lp->ino = ff_pkt->statp.st_ino;
430 lp->dev = ff_pkt->statp.st_dev;
431 bstrncpy(lp->name, fname, len);
432 lp->next = ff_pkt->linkhash[linkhash];
433 ff_pkt->linkhash[linkhash] = lp;
434 ff_pkt->linked = lp; /* mark saved link */
436 ff_pkt->linked = NULL;
439 /* This is not a link to a previously dumped file, so dump it. */
440 if (S_ISREG(ff_pkt->statp.st_mode)) {
443 sizeleft = ff_pkt->statp.st_size;
445 /* Don't bother opening empty, world readable files. Also do not open
446 files when archive is meant for /dev/null. */
447 if (ff_pkt->null_output_device || (sizeleft == 0
448 && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
449 ff_pkt->type = FT_REGE;
451 ff_pkt->type = FT_REG;
453 rtn_stat = handle_file(jcr, ff_pkt, top_level);
454 if (ff_pkt->linked) {
455 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
457 if (ff_pkt->flags & FO_KEEPATIME) {
458 utime(fname, &restore_times);
463 } else if (S_ISLNK(ff_pkt->statp.st_mode)) { /* soft link */
465 char *buffer = (char *)alloca(path_max + name_max + 102);
467 size = readlink(fname, buffer, path_max + name_max + 101);
469 /* Could not follow link */
470 ff_pkt->type = FT_NOFOLLOW;
471 ff_pkt->ff_errno = errno;
472 rtn_stat = handle_file(jcr, ff_pkt, top_level);
473 if (ff_pkt->linked) {
474 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
479 ff_pkt->link = buffer; /* point to link */
480 ff_pkt->type = FT_LNK; /* got a real link */
481 rtn_stat = handle_file(jcr, ff_pkt, top_level);
482 if (ff_pkt->linked) {
483 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
487 } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
489 struct dirent *entry, *result;
494 dev_t our_device = ff_pkt->statp.st_dev;
496 bool volhas_attrlist = ff_pkt->volhas_attrlist; /* Remember this if we recurse */
499 * If we are using Win32 (non-portable) backup API, don't check
500 * access as everything is more complicated, and
501 * in principle, we should be able to access everything.
503 if (!have_win32_api() || (ff_pkt->flags & FO_PORTABLE)) {
504 if (access(fname, R_OK) == -1 && geteuid() != 0) {
505 /* Could not access() directory */
506 ff_pkt->type = FT_NOACCESS;
507 ff_pkt->ff_errno = errno;
508 rtn_stat = handle_file(jcr, ff_pkt, top_level);
509 if (ff_pkt->linked) {
510 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
517 * Ignore this directory and everything below if the file .nobackup
518 * (or what is defined for IgnoreDir in this fileset) exists
520 if (ff_pkt->ignoredir != NULL) {
522 char fname[MAXPATHLEN];
524 if (strlen(ff_pkt->fname) + strlen("/") +
525 strlen(ff_pkt->ignoredir) + 1 > MAXPATHLEN)
526 return 1; /* Is this wisdom? */
528 strcpy(fname, ff_pkt->fname);
530 strcat(fname, ff_pkt->ignoredir);
531 if (stat(fname, &sb) == 0) {
532 Dmsg2(100, "Directory '%s' ignored (found %s)\n",
533 ff_pkt->fname, ff_pkt->ignoredir);
534 return 1; /* Just ignore this directory */
538 /* Build a canonical directory name with a trailing slash in link var */
540 link_len = len + 200;
541 link = (char *)bmalloc(link_len + 2);
542 bstrncpy(link, fname, link_len);
543 /* Strip all trailing slashes */
544 while (len >= 1 && IsPathSeparator(link[len - 1]))
546 link[len++] = '/'; /* add back one */
550 if (ff_pkt->incremental && !check_changes(jcr, ff_pkt)) {
551 /* Incremental option, directory entry not changed */
552 ff_pkt->type = FT_DIRNOCHG;
554 ff_pkt->type = FT_DIRBEGIN;
557 * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
558 * if st_rdev is 2, it is a mount point
560 if (have_win32_api() && ff_pkt->statp.st_rdev == 1) {
561 ff_pkt->type = FT_REPARSE;
564 * Note, we return the directory to the calling program (handle_file)
565 * when we first see the directory (FT_DIRBEGIN.
566 * This allows the program to apply matches and make a
567 * choice whether or not to accept it. If it is accepted, we
568 * do not immediately save it, but do so only after everything
569 * in the directory is seen (i.e. the FT_DIREND).
571 rtn_stat = handle_file(jcr, ff_pkt, top_level);
572 if (rtn_stat < 1 || ff_pkt->type == FT_REPARSE) { /* ignore or error status */
576 /* Done with DIRBEGIN, next call will be DIREND */
577 if (ff_pkt->type == FT_DIRBEGIN) {
578 ff_pkt->type = FT_DIREND;
582 * Create a temporary ff packet for this directory
583 * entry, and defer handling the directory until
584 * we have recursed into it. This saves the
585 * directory after all files have been processed, and
586 * during the restore, the directory permissions will
587 * be reset after all the files have been restored.
589 Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
590 FF_PKT *dir_ff_pkt = new_dir_ff_pkt(ff_pkt);
593 * Do not descend into subdirectories (recurse) if the
594 * user has turned it off for this directory.
596 * If we are crossing file systems, we are either not allowed
597 * to cross, or we may be restricted by a list of permitted
600 if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
601 ff_pkt->type = FT_NORECURSE;
603 } else if (!top_level && parent_device != ff_pkt->statp.st_dev) {
604 if(!(ff_pkt->flags & FO_MULTIFS)) {
605 ff_pkt->type = FT_NOFSCHG;
607 } else if (!accept_fstype(ff_pkt, NULL)) {
608 ff_pkt->type = FT_INVALIDFS;
611 ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
614 /* If not recursing, just backup dir and return */
616 rtn_stat = handle_file(jcr, ff_pkt, top_level);
617 if (ff_pkt->linked) {
618 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
621 free_dir_ff_pkt(dir_ff_pkt);
622 ff_pkt->link = ff_pkt->fname; /* reset "link" */
623 if (ff_pkt->flags & FO_KEEPATIME) {
624 utime(fname, &restore_times);
629 ff_pkt->link = ff_pkt->fname; /* reset "link" */
632 * Descend into or "recurse" into the directory to read
633 * all the files in it.
636 if ((directory = opendir(fname)) == NULL) {
637 ff_pkt->type = FT_NOOPEN;
638 ff_pkt->ff_errno = errno;
639 rtn_stat = handle_file(jcr, ff_pkt, top_level);
640 if (ff_pkt->linked) {
641 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
644 free_dir_ff_pkt(dir_ff_pkt);
649 * Process all files in this directory entry (recursing).
650 * This would possibly run faster if we chdir to the directory
651 * before traversing it.
654 entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
655 for ( ; !job_canceled(jcr); ) {
659 status = readdir_r(directory, entry, &result);
660 if (status != 0 || result == NULL) {
661 // Dmsg2(99, "readdir returned stat=%d result=0x%x\n",
662 // status, (long)result);
665 ASSERT(name_max+1 > (int)sizeof(struct dirent) + (int)NAMELEN(entry));
667 /* Skip `.', `..', and excluded file names. */
668 if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
669 (p[1] == '.' && p[2] == '\0')))) {
673 if ((int)NAMELEN(entry) + len >= link_len) {
674 link_len = len + NAMELEN(entry) + 1;
675 link = (char *)brealloc(link, link_len + 1);
678 for (i=0; i < (int)NAMELEN(entry); i++) {
682 if (!file_is_excluded(ff_pkt, link)) {
683 rtn_stat = find_one_file(jcr, ff_pkt, handle_file, link, our_device, false);
684 if (ff_pkt->linked) {
685 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
694 * Now that we have recursed through all the files in the
695 * directory, we "save" the directory so that after all
696 * the files are restored, this entry will serve to reset
697 * the directory modes and dates. Temp directory values
698 * were used without this record.
700 handle_file(jcr, dir_ff_pkt, top_level); /* handle directory entry */
701 if (ff_pkt->linked) {
702 ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
704 free_dir_ff_pkt(dir_ff_pkt);
706 if (ff_pkt->flags & FO_KEEPATIME) {
707 utime(fname, &restore_times);
709 ff_pkt->volhas_attrlist = volhas_attrlist; /* Restore value in case it changed. */
711 } /* end check for directory */
714 * If it is explicitly mentioned (i.e. top_level) and is
715 * a block device, we do a raw backup of it or if it is
716 * a fifo, we simply read it.
718 #ifdef HAVE_FREEBSD_OS
720 * On FreeBSD, all block devices are character devices, so
721 * to be able to read a raw disk, we need the check for
722 * a character device.
723 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
724 * crw-r----- 1 root operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
726 if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
728 if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
730 ff_pkt->type = FT_RAW; /* raw partition */
731 } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
732 ff_pkt->flags & FO_READFIFO) {
733 ff_pkt->type = FT_FIFO;
735 /* The only remaining types are special (character, ...) files */
736 ff_pkt->type = FT_SPEC;
738 rtn_stat = handle_file(jcr, ff_pkt, top_level);
739 if (ff_pkt->linked) {
740 ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
745 int term_find_one(FF_PKT *ff)
747 struct f_link *lp, *lc;
752 if (ff->linkhash == NULL) return 0;
754 for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
755 /* Free up list of hard linked files */
756 lp = ff->linkhash[i];
765 ff->linkhash[i] = NULL;