]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/findlib/find_one.c
Strip pathname portion off all message routines that print filename:line.
[bacula/bacula] / bacula / src / findlib / find_one.c
1 /*
2    Copyright (C) 2000-2006 Kern Sibbald
3
4    This program is free software; you can redistribute it and/or
5    modify it under the terms of the GNU General Public License
6    version 2 as amended with additional clauses defined in the
7    file LICENSE in the main source directory.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
12    the file LICENSE for additional details.
13
14  */
15
16 /*
17
18    This file is based on GNU TAR source code. Except for a few key
19    ideas, it has been entirely rewritten for Bacula.
20
21       Kern Sibbald, MM
22
23    Thanks to the TAR programmers.
24
25      Version $Id$
26
27  */
28
29 #include "bacula.h"
30 #include "find.h"
31 #ifdef HAVE_DARWIN_OS
32 #include <sys/param.h>
33 #include <sys/mount.h>
34 #include <sys/attr.h>
35 #endif
36
37 extern int32_t name_max;              /* filename max length */
38 extern int32_t path_max;              /* path name max length */
39
40 /*
41  * Structure for keeping track of hard linked files, we
42  *   keep an entry for each hardlinked file that we save,
43  *   which is the first one found. For all the other files that
44  *   are linked to this one, we save only the directory
45  *   entry so we can link it.
46  */
47 struct f_link {
48     struct f_link *next;
49     dev_t dev;                        /* device */
50     ino_t ino;                        /* inode with device is unique */
51     short linkcount;
52     uint32_t FileIndex;               /* Bacula FileIndex of this file */
53     char name[1];                     /* The name */
54 };
55
56 typedef struct f_link link_t;
57 #define LINK_HASHTABLE_BITS 16
58 #define LINK_HASHTABLE_SIZE (1<<LINK_HASHTABLE_BITS)
59 #define LINK_HASHTABLE_MASK (LINK_HASHTABLE_SIZE-1)
60
61 static inline int LINKHASH(const struct stat &info)
62 {
63     int hash = info.st_dev;
64     unsigned long long i = info.st_ino;
65     hash ^= i;
66     i >>= 16;
67     hash ^= i;
68     i >>= 16;
69     hash ^= i;
70     i >>= 16;
71     hash ^= i;
72     return hash & LINK_HASHTABLE_MASK;
73 }
74
75 static void free_dir_ff_pkt(FF_PKT *dir_ff_pkt)
76 {
77    free(dir_ff_pkt->fname);
78    free(dir_ff_pkt->link);
79    free_pool_memory(dir_ff_pkt->sys_fname);
80    free(dir_ff_pkt);
81 }
82
83 /*
84  * Check to see if we allow the file system type of a file or directory.
85  * If we do not have a list of file system types, we accept anything.
86  */
87 static int accept_fstype(FF_PKT *ff, void *dummy) {
88    int i;
89    char fs[1000];
90    bool accept = true;
91
92    if (ff->fstypes.size()) {
93       accept = false;
94       if (!fstype(ff->fname, fs, sizeof(fs))) {
95          Dmsg1(50, "Cannot determine file system type for \"%s\"\n", ff->fname);
96       } else {
97          for (i = 0; i < ff->fstypes.size(); ++i) {
98             if (strcmp(fs, (char *)ff->fstypes.get(i)) == 0) {
99                Dmsg2(100, "Accepting fstype %s for \"%s\"\n", fs, ff->fname);
100                accept = true;
101                break;
102             }
103             Dmsg3(200, "fstype %s for \"%s\" does not match %s\n", fs,
104                   ff->fname, ff->fstypes.get(i));
105          }
106       }
107    }
108    return accept;
109 }
110
111 /*
112  * Check to see if we allow the drive type of a file or directory.
113  * If we do not have a list of drive types, we accept anything.
114  */
115 static int accept_drivetype(FF_PKT *ff, void *dummy) {
116    int i;
117    char dt[100];
118    bool accept = true;
119
120    if (ff->drivetypes.size()) {
121       accept = false;
122       if (!drivetype(ff->fname, dt, sizeof(dt))) {
123          Dmsg1(50, "Cannot determine drive type for \"%s\"\n", ff->fname);
124       } else {
125          for (i = 0; i < ff->drivetypes.size(); ++i) {
126             if (strcmp(dt, (char *)ff->drivetypes.get(i)) == 0) {
127                Dmsg2(100, "Accepting drive type %s for \"%s\"\n", dt, ff->fname);
128                accept = true;
129                break;
130             }
131             Dmsg3(200, "drive type %s for \"%s\" does not match %s\n", dt,
132                   ff->fname, ff->drivetypes.get(i));
133          }
134       }
135    }
136    return accept;
137 }
138
139 /*
140  * This function determines whether we can use getattrlist()
141  * It's odd, but we have to use the function to determine that...
142  * Also, the man pages talk about things as if they were implemented.
143  *
144  * On Mac OS X, this succesfully differentiates between HFS+ and UFS
145  * volumes, which makes me trust it is OK for others, too.
146  */
147 static bool volume_has_attrlist(const char *fname)
148 {
149 #ifdef HAVE_DARWIN_OS
150    struct statfs st;
151    struct volinfo_struct {
152       unsigned long length;               /* Mandatory field */
153       vol_capabilities_attr_t info;       /* Volume capabilities */
154    } vol;
155    struct attrlist attrList;
156
157    memset(&attrList, 0, sizeof(attrList));
158    attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
159    attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_CAPABILITIES;
160    if (statfs(fname, &st) == 0) {
161       /* We need to check on the mount point */
162       if (getattrlist(st.f_mntonname, &attrList, &vol, sizeof(vol), FSOPT_NOFOLLOW) == 0
163             && (vol.info.capabilities[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)
164             && (vol.info.valid[VOL_CAPABILITIES_INTERFACES] & VOL_CAP_INT_ATTRLIST)) {
165          return true;
166       }
167    }
168 #endif
169    return false;
170 }
171
172 /*
173  * Find a single file.
174  * handle_file is the callback for handling the file.
175  * p is the filename
176  * parent_device is the device we are currently on
177  * top_level is 1 when not recursing or 0 when
178  *  descending into a directory.
179  */
180 int
181 find_one_file(JCR *jcr, FF_PKT *ff_pkt, 
182                int handle_file(FF_PKT *ff, void *hpkt, bool top_level),
183                void *pkt, char *fname, dev_t parent_device, bool top_level)
184 {
185    struct utimbuf restore_times;
186    int rtn_stat;
187    int len;
188
189    ff_pkt->fname = ff_pkt->link = fname;
190
191    if (lstat(fname, &ff_pkt->statp) != 0) {
192        /* Cannot stat file */
193        ff_pkt->type = FT_NOSTAT;
194        ff_pkt->ff_errno = errno;
195        return handle_file(ff_pkt, pkt, top_level);
196    }
197
198    Dmsg1(300, "File ----: %s\n", fname);
199
200    /* Save current times of this directory in case we need to
201     * reset them because the user doesn't want them changed.
202     */
203    restore_times.actime = ff_pkt->statp.st_atime;
204    restore_times.modtime = ff_pkt->statp.st_mtime;
205
206    /*
207     * We check for allowed fstypes and drivetypes at top_level and fstype change (below).
208     */
209    if (top_level) {
210       if (!accept_fstype(ff_pkt, NULL)) {
211          ff_pkt->type = FT_INVALIDFS;
212          if (ff_pkt->flags & FO_KEEPATIME) {
213             utime(fname, &restore_times);
214          }
215
216          char fs[100];
217
218          if (!fstype(ff_pkt->fname, fs, sizeof(fs))) {
219              bstrncpy(fs, "unknown", sizeof(fs));
220          }
221
222          Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has unlisted fstype \"%s\"\n"), fname, fs);
223          return 1;      /* Just ignore this error - or the whole backup is cancelled */
224       }
225       if (!accept_drivetype(ff_pkt, NULL)) {
226          ff_pkt->type = FT_INVALIDDT;
227          if (ff_pkt->flags & FO_KEEPATIME) {
228             utime(fname, &restore_times);
229          }
230
231          char dt[100];
232
233          if (!drivetype(ff_pkt->fname, dt, sizeof(dt))) {
234              bstrncpy(dt, "unknown", sizeof(dt));
235          }
236
237          Jmsg(jcr, M_INFO, 0, _("Top level directory \"%s\" has an unlisted drive type \"%s\"\n"), fname, dt);
238          return 1;      /* Just ignore this error - or the whole backup is cancelled */
239       }
240       ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
241    }
242
243    /*
244     * If this is an Incremental backup, see if file was modified
245     * since our last "save_time", presumably the last Full save
246     * or Incremental.
247     */
248    if (ff_pkt->incremental && !S_ISDIR(ff_pkt->statp.st_mode)) {
249       Dmsg1(300, "Non-directory incremental: %s\n", ff_pkt->fname);
250       /* Not a directory */
251       if (ff_pkt->statp.st_mtime < ff_pkt->save_time
252           && ((ff_pkt->flags & FO_MTIMEONLY) ||
253               ff_pkt->statp.st_ctime < ff_pkt->save_time)) {
254          /* Incremental option, file not changed */
255          ff_pkt->type = FT_NOCHG;
256          return handle_file(ff_pkt, pkt, top_level);
257       }
258    }
259
260 #ifdef HAVE_DARWIN_OS
261    if (ff_pkt->flags & FO_HFSPLUS && ff_pkt->volhas_attrlist
262          && S_ISREG(ff_pkt->statp.st_mode)) {
263        /* TODO: initialise attrList once elsewhere? */
264        struct attrlist attrList;
265        memset(&attrList, 0, sizeof(attrList));
266        attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
267        attrList.commonattr = ATTR_CMN_FNDRINFO;
268        attrList.fileattr = ATTR_FILE_RSRCLENGTH;
269        if (getattrlist(fname, &attrList, &ff_pkt->hfsinfo,
270                 sizeof(ff_pkt->hfsinfo), FSOPT_NOFOLLOW) != 0) {
271           ff_pkt->type = FT_NOSTAT;
272           ff_pkt->ff_errno = errno;
273           return handle_file(ff_pkt, pkt, top_level);
274        }
275    }
276 #endif
277
278 /* ***FIXME*** implement this */
279 #if xxxxxxx
280    /* See if we are trying to dump the archive.  */
281    if (ar_dev && ff_pkt->statp.st_dev == ar_dev && ff_pkt->statp.st_ino == ar_ino) {
282        ff_pkt->type = FT_ISARCH;
283        return handle_file(ff_pkt, pkt, top_level);
284    }
285 #endif
286    ff_pkt->LinkFI = 0;
287    /*
288     * Handle hard linked files
289     *
290     * Maintain a list of hard linked files already backed up. This
291     *  allows us to ensure that the data of each file gets backed
292     *  up only once.
293     */
294    if (!(ff_pkt->flags & FO_NO_HARDLINK)
295        && ff_pkt->statp.st_nlink > 1
296        && (S_ISREG(ff_pkt->statp.st_mode)
297            || S_ISCHR(ff_pkt->statp.st_mode)
298            || S_ISBLK(ff_pkt->statp.st_mode)
299            || S_ISFIFO(ff_pkt->statp.st_mode)
300            || S_ISSOCK(ff_pkt->statp.st_mode))) {
301
302        struct f_link *lp;
303        if (ff_pkt->linkhash == NULL) {
304            ff_pkt->linkhash = (link_t **)bmalloc(LINK_HASHTABLE_SIZE * sizeof(link_t *));
305            memset(ff_pkt->linkhash, 0, LINK_HASHTABLE_SIZE * sizeof(link_t *));
306        }
307        const int linkhash = LINKHASH(ff_pkt->statp);
308
309       /* Search link list of hard linked files */
310        for (lp = ff_pkt->linkhash[linkhash]; lp; lp = lp->next)
311          if (lp->ino == (ino_t)ff_pkt->statp.st_ino &&
312              lp->dev == (dev_t)ff_pkt->statp.st_dev) {
313              /* If we have already backed up the hard linked file don't do it again */
314              if (strcmp(lp->name, fname) == 0) {
315                 return 1;             /* ignore */
316              }
317              ff_pkt->link = lp->name;
318              ff_pkt->type = FT_LNKSAVED;       /* Handle link, file already saved */
319              ff_pkt->LinkFI = lp->FileIndex;
320              return handle_file(ff_pkt, pkt, top_level);
321          }
322
323       /* File not previously dumped. Chain it into our list. */
324       len = strlen(fname) + 1;
325       lp = (struct f_link *)bmalloc(sizeof(struct f_link) + len);
326       lp->ino = ff_pkt->statp.st_ino;
327       lp->dev = ff_pkt->statp.st_dev;
328       bstrncpy(lp->name, fname, len);
329        lp->next = ff_pkt->linkhash[linkhash];
330        ff_pkt->linkhash[linkhash] = lp;
331       ff_pkt->linked = lp;            /* mark saved link */
332    } else {
333       ff_pkt->linked = NULL;
334    }
335
336    /* This is not a link to a previously dumped file, so dump it.  */
337    if (S_ISREG(ff_pkt->statp.st_mode)) {
338       off_t sizeleft;
339
340       sizeleft = ff_pkt->statp.st_size;
341
342       /* Don't bother opening empty, world readable files.  Also do not open
343          files when archive is meant for /dev/null.  */
344       if (ff_pkt->null_output_device || (sizeleft == 0
345               && MODE_RALL == (MODE_RALL & ff_pkt->statp.st_mode))) {
346          ff_pkt->type = FT_REGE;
347       } else {
348          ff_pkt->type = FT_REG;
349       }
350       rtn_stat = handle_file(ff_pkt, pkt, top_level);
351       if (ff_pkt->linked) {
352          ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
353       }
354       if (ff_pkt->flags & FO_KEEPATIME) {
355          utime(fname, &restore_times);
356       }       
357       return rtn_stat;
358
359
360    } else if (S_ISLNK(ff_pkt->statp.st_mode)) {  /* soft link */
361       int size;
362       char *buffer = (char *)alloca(path_max + name_max + 102);
363
364       size = readlink(fname, buffer, path_max + name_max + 101);
365       if (size < 0) {
366          /* Could not follow link */
367          ff_pkt->type = FT_NOFOLLOW;
368          ff_pkt->ff_errno = errno;
369          rtn_stat = handle_file(ff_pkt, pkt, top_level);
370          if (ff_pkt->linked) {
371             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
372          }
373          return rtn_stat;
374       }
375       buffer[size] = 0;
376       ff_pkt->link = buffer;          /* point to link */
377       ff_pkt->type = FT_LNK;          /* got a real link */
378       rtn_stat = handle_file(ff_pkt, pkt, top_level);
379       if (ff_pkt->linked) {
380          ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
381       }
382       return rtn_stat;
383
384    } else if (S_ISDIR(ff_pkt->statp.st_mode)) {
385       DIR *directory;
386       struct dirent *entry, *result;
387       char *link;
388       int link_len;
389       int len;
390       int status;
391       dev_t our_device = ff_pkt->statp.st_dev;
392       bool recurse = true;
393       bool volhas_attrlist = ff_pkt->volhas_attrlist;    /* Remember this if we recurse */
394
395       /*
396        * If we are using Win32 (non-portable) backup API, don't check
397        *  access as everything is more complicated, and
398        *  in principle, we should be able to access everything.
399        */
400       if (!have_win32_api() || (ff_pkt->flags & FO_PORTABLE)) {
401          if (access(fname, R_OK) == -1 && geteuid() != 0) {
402             /* Could not access() directory */
403             ff_pkt->type = FT_NOACCESS;
404             ff_pkt->ff_errno = errno;
405             rtn_stat = handle_file(ff_pkt, pkt, top_level);
406             if (ff_pkt->linked) {
407                ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
408             }
409             return rtn_stat;
410          }
411       }
412
413       /* Build a canonical directory name with a trailing slash in link var */
414       len = strlen(fname);
415       link_len = len + 200;
416       link = (char *)bmalloc(link_len + 2);
417       bstrncpy(link, fname, link_len);
418       /* Strip all trailing slashes */
419       while (len >= 1 && link[len - 1] == '/')
420         len--;
421       link[len++] = '/';             /* add back one */
422       link[len] = 0;
423
424       ff_pkt->link = link;
425       if (ff_pkt->incremental &&
426           (ff_pkt->statp.st_mtime < ff_pkt->save_time &&
427              ((ff_pkt->flags & FO_MTIMEONLY) ||
428                ff_pkt->statp.st_ctime < ff_pkt->save_time))) {
429          /* Incremental option, directory entry not changed */
430          ff_pkt->type = FT_DIRNOCHG;
431       } else {
432          ff_pkt->type = FT_DIRBEGIN;
433       }
434       /*
435        * Note, we return the directory to the calling program (handle_file)
436        * when we first see the directory (FT_DIRBEGIN.
437        * This allows the program to apply matches and make a
438        * choice whether or not to accept it.  If it is accepted, we
439        * do not immediately save it, but do so only after everything
440        * in the directory is seen (i.e. the FT_DIREND).
441        */
442       rtn_stat = handle_file(ff_pkt, pkt, top_level);
443       if (rtn_stat < 1) {             /* ignore or error status */
444          free(link);
445          return rtn_stat;
446       }
447       /* Done with DIRBEGIN, next call will be DIREND */
448       if (ff_pkt->type == FT_DIRBEGIN) {
449          ff_pkt->type = FT_DIREND;
450       }
451
452       /*
453        * Create a temporary ff packet for this directory
454        *   entry, and defer handling the directory until
455        *   we have recursed into it.  This saves the
456        *   directory after all files have been processed, and
457        *   during the restore, the directory permissions will
458        *   be reset after all the files have been restored.
459        */
460       Dmsg1(300, "Create temp ff packet for dir: %s\n", ff_pkt->fname);
461       FF_PKT *dir_ff_pkt = (FF_PKT *)bmalloc(sizeof(FF_PKT));
462       memcpy(dir_ff_pkt, ff_pkt, sizeof(FF_PKT));
463       dir_ff_pkt->fname = bstrdup(ff_pkt->fname);
464       dir_ff_pkt->link = bstrdup(ff_pkt->link);
465       dir_ff_pkt->sys_fname = get_pool_memory(PM_FNAME);
466       dir_ff_pkt->included_files_list = NULL;
467       dir_ff_pkt->excluded_files_list = NULL;
468       dir_ff_pkt->excluded_paths_list = NULL;
469       dir_ff_pkt->linkhash = NULL;
470
471       /*
472        * Do not descend into subdirectories (recurse) if the
473        * user has turned it off for this directory.
474        *
475        * If we are crossing file systems, we are either not allowed
476        * to cross, or we may be restricted by a list of permitted
477        * file systems.
478        */
479       if (!top_level && ff_pkt->flags & FO_NO_RECURSION) {
480          ff_pkt->type = FT_NORECURSE;
481          recurse = false;
482       } else if (!top_level && parent_device != ff_pkt->statp.st_dev) {
483          if(!(ff_pkt->flags & FO_MULTIFS)) {
484             ff_pkt->type = FT_NOFSCHG;
485             recurse = false;
486          } else if (!accept_fstype(ff_pkt, NULL)) {
487             ff_pkt->type = FT_INVALIDFS;
488             recurse = false;
489          } else {
490             ff_pkt->volhas_attrlist = volume_has_attrlist(fname);
491          }
492       }
493       /* If not recursing, just backup dir and return */
494       if (!recurse) {
495          rtn_stat = handle_file(ff_pkt, pkt, top_level);
496          if (ff_pkt->linked) {
497             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
498          }
499          free(link);
500          free_dir_ff_pkt(dir_ff_pkt);
501          ff_pkt->link = ff_pkt->fname;     /* reset "link" */
502          if (ff_pkt->flags & FO_KEEPATIME) {
503             utime(fname, &restore_times);
504          }
505          return rtn_stat;
506       }
507
508       ff_pkt->link = ff_pkt->fname;     /* reset "link" */
509
510       /*
511        * Descend into or "recurse" into the directory to read
512        *   all the files in it.
513        */
514       errno = 0;
515       if ((directory = opendir(fname)) == NULL) {
516          ff_pkt->type = FT_NOOPEN;
517          ff_pkt->ff_errno = errno;
518          rtn_stat = handle_file(ff_pkt, pkt, top_level);
519          if (ff_pkt->linked) {
520             ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
521          }
522          free(link);
523          free_dir_ff_pkt(dir_ff_pkt);
524          return rtn_stat;
525       }
526
527       /*
528        * Process all files in this directory entry (recursing).
529        *    This would possibly run faster if we chdir to the directory
530        *    before traversing it.
531        */
532       rtn_stat = 1;
533       entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 100);
534       for ( ; !job_canceled(jcr); ) {
535          char *p, *q;
536          int i;
537
538          status  = readdir_r(directory, entry, &result);
539          if (status != 0 || result == NULL) {
540 //          Dmsg2(99, "readdir returned stat=%d result=0x%x\n",
541 //             status, (long)result);
542             break;
543          }
544          ASSERT(name_max+1 > (int)sizeof(struct dirent) + (int)NAMELEN(entry));
545          p = entry->d_name;
546          /* Skip `.', `..', and excluded file names.  */
547          if (p[0] == '\0' || (p[0] == '.' && (p[1] == '\0' ||
548              (p[1] == '.' && p[2] == '\0')))) {
549             continue;
550          }
551
552          if ((int)NAMELEN(entry) + len >= link_len) {
553              link_len = len + NAMELEN(entry) + 1;
554              link = (char *)brealloc(link, link_len + 1);
555          }
556          q = link + len;
557          for (i=0; i < (int)NAMELEN(entry); i++) {
558             *q++ = *p++;
559          }
560          *q = 0;
561          if (!file_is_excluded(ff_pkt, link)) {
562             rtn_stat = find_one_file(jcr, ff_pkt, handle_file, pkt, link, our_device, false);
563             if (ff_pkt->linked) {
564                ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
565             }
566          }
567       }
568       closedir(directory);
569       free(link);
570       free(entry);
571
572       /*
573        * Now that we have recursed through all the files in the
574        *  directory, we "save" the directory so that after all
575        *  the files are restored, this entry will serve to reset
576        *  the directory modes and dates.  Temp directory values
577        *  were used without this record.
578        */
579       handle_file(dir_ff_pkt, pkt, top_level);       /* handle directory entry */
580       if (ff_pkt->linked) {
581          ff_pkt->linked->FileIndex = dir_ff_pkt->FileIndex;
582       }
583       free_dir_ff_pkt(dir_ff_pkt);
584
585       if (ff_pkt->flags & FO_KEEPATIME) {
586          utime(fname, &restore_times);
587       }
588       ff_pkt->volhas_attrlist = volhas_attrlist;      /* Restore value in case it changed. */
589       return rtn_stat;
590    } /* end check for directory */
591
592    /*
593     * If it is explicitly mentioned (i.e. top_level) and is
594     *  a block device, we do a raw backup of it or if it is
595     *  a fifo, we simply read it.
596     */
597 #ifdef HAVE_FREEBSD_OS
598    /*
599     * On FreeBSD, all block devices are character devices, so
600     *   to be able to read a raw disk, we need the check for
601     *   a character device.
602     * crw-r-----  1 root  operator  - 116, 0x00040002 Jun  9 19:32 /dev/ad0s3
603     * crw-r-----  1 root  operator  - 116, 0x00040002 Jun  9 19:32 /dev/rad0s3
604     */
605    if (top_level && (S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))) {
606 #else
607    if (top_level && S_ISBLK(ff_pkt->statp.st_mode)) {
608 #endif
609       ff_pkt->type = FT_RAW;          /* raw partition */
610    } else if (top_level && S_ISFIFO(ff_pkt->statp.st_mode) &&
611               ff_pkt->flags & FO_READFIFO) {
612       ff_pkt->type = FT_FIFO;
613    } else {
614       /* The only remaining types are special (character, ...) files */
615       ff_pkt->type = FT_SPEC;
616    }
617    rtn_stat = handle_file(ff_pkt, pkt, top_level);
618    if (ff_pkt->linked) {
619       ff_pkt->linked->FileIndex = ff_pkt->FileIndex;
620    }
621    return rtn_stat;
622 }
623
624 int term_find_one(FF_PKT *ff)
625 {
626    struct f_link *lp, *lc;
627    int count = 0;
628    int i;
629
630    
631    if (ff->linkhash == NULL) return 0;
632
633    for (i =0 ; i < LINK_HASHTABLE_SIZE; i ++) {
634    /* Free up list of hard linked files */
635        lp = ff->linkhash[i];
636        while (lp) {
637       lc = lp;
638       lp = lp->next;
639       if (lc) {
640          free(lc);
641          count++;
642       }
643    }
644        ff->linkhash[i] = NULL;
645    }
646    free(ff->linkhash);
647    ff->linkhash = NULL;
648    return count;
649 }