"Fossies" - the Fresh Open Source Software Archive

Member "burp-2.3.6/src/client/find.c" (28 Apr 2019, 17931 Bytes) of package /linux/privat/burp-2.3.6.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "find.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.2.12_vs_2.3.0.

    1 /*
    2    Bacula® - The Network Backup Solution
    3 
    4    Copyright (C) 2000-2010 Free Software Foundation Europe e.V.
    5 
    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 three of the GNU Affero General Public
   10    License as published by the Free Software Foundation and included
   11    in the file LICENSE.
   12 
   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.
   17 
   18    You should have received a copy of the GNU Affero 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
   21    02110-1301, USA.
   22 
   23    Bacula® is a registered trademark of Kern Sibbald.
   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.
   27 */
   28 /*
   29    This file was derived from GNU TAR source code. Except for a few key
   30    ideas, it has been entirely rewritten for Bacula.
   31 
   32       Kern Sibbald, MM
   33 
   34    Thanks to the TAR programmers.
   35 */
   36 /*
   37    This file was derived from the findlib code from bacula-5.0.3, and
   38    heavily modified. Therefore, I have retained the bacula copyright notice.
   39    The specific bacula files were:
   40    src/findlib/find.c
   41    src/findlib/find_one.c.
   42    The comment by Kern above, about TAR, was from find_one.c.
   43 
   44       Graham Keeling, 2014.
   45 */
   46 
   47 #include "../burp.h"
   48 #include "../alloc.h"
   49 #include "../conf.h"
   50 #include "../fsops.h"
   51 #include "../linkhash.h"
   52 #include "../log.h"
   53 #include "../pathcmp.h"
   54 #include "../prepend.h"
   55 #include "../regexp.h"
   56 #include "../strlist.h"
   57 #include "find.h"
   58 #include "find_logic.h"
   59 
   60 #ifdef HAVE_LINUX_OS
   61 #include <sys/statfs.h>
   62 #endif
   63 #ifdef HAVE_SUN_OS
   64 #include <sys/statvfs.h>
   65 #endif
   66 
   67 static int (*my_send_file)(struct asfd *, struct FF_PKT *, struct conf **);
   68 
   69 // Initialize the find files "global" variables
   70 struct FF_PKT *find_files_init(
   71     int callback(struct asfd *asfd, struct FF_PKT *ff, struct conf **confs))
   72 {
   73     struct FF_PKT *ff;
   74 
   75     if(!(ff=(struct FF_PKT *)calloc_w(1, sizeof(struct FF_PKT), __func__))
   76       || linkhash_init())
   77         return NULL;
   78     my_send_file=callback;
   79 
   80     return ff;
   81 }
   82 
   83 void find_files_free(struct FF_PKT **ff)
   84 {
   85     linkhash_free();
   86     free_v((void **)ff);
   87 }
   88 
   89 // Return 1 to include the file, 0 to exclude it.
   90 static int in_include_ext(struct strlist *incext, const char *fname)
   91 {
   92     int i=0;
   93     struct strlist *l;
   94     const char *cp=NULL;
   95     // If not doing include_ext, let the file get backed up.
   96     if(!incext) return 1;
   97 
   98     // The flag of the first item contains the maximum number of characters
   99     // that need to be checked.
  100     // FIX THIS: The next two functions do things very similar to this.
  101     for(cp=fname+strlen(fname)-1; i<incext->flag && cp>=fname; cp--, i++)
  102     {
  103         if(*cp!='.') continue;
  104         for(l=incext; l; l=l->next)
  105             if(!strcasecmp(l->path, cp+1))
  106                 return 1;
  107         // If file has no extension, it cannot be included.
  108         return 0;
  109     }
  110     return 0;
  111 }
  112 
  113 static int in_exclude_ext(struct strlist *excext, const char *fname)
  114 {
  115     int i=0;
  116     struct strlist *l;
  117     const char *cp=NULL;
  118     // If not doing exclude_ext, let the file get backed up.
  119     if(!excext) return 0;
  120 
  121     // The flag of the first item contains the maximum number of characters
  122     // that need to be checked.
  123     for(cp=fname+strlen(fname)-1; i<excext->flag && cp>=fname; cp--, i++)
  124     {
  125         if(*cp!='.') continue;
  126         for(l=excext; l; l=l->next)
  127             if(!strcasecmp(l->path, cp+1))
  128                 return 1;
  129         // If file has no extension, it is included.
  130         return 0;
  131     }
  132     return 0;
  133 }
  134 
  135 // Returns the level of compression.
  136 int in_exclude_comp(struct strlist *excom, const char *fname, int compression)
  137 {
  138     int i=0;
  139     struct strlist *l;
  140     const char *cp=NULL;
  141     // If not doing compression, or there are no excludes, return
  142     // straight away.
  143     if(!compression || !excom) return compression;
  144 
  145     // The flag of the first item contains the maximum number of characters
  146     // that need to be checked.
  147     for(cp=fname+strlen(fname)-1; i<excom->flag && cp>=fname; cp--, i++)
  148     {
  149         if(*cp!='.') continue;
  150         for(l=excom; l; l=l->next)
  151             if(!strcasecmp(l->path, cp+1))
  152                 return 0;
  153         return compression;
  154     }
  155     return compression;
  156 }
  157 
  158 /* Return 1 to include the file, 0 to exclude it. */
  159 int in_include_regex(struct strlist *increg, const char *fname)
  160 {
  161     // If not doing include_regex, let the file get backed up.
  162     if(!increg) return 1;
  163     for(; increg; increg=increg->next)
  164         if(regex_check(increg->re, fname))
  165             return 1;
  166     return 0;
  167 }
  168 
  169 static int in_exclude_regex(struct strlist *excreg, const char *fname)
  170 {
  171     // If not doing exclude_regex, let the file get backed up.
  172     for(; excreg; excreg=excreg->next)
  173         if(regex_check(excreg->re, fname))
  174             return 1;
  175     return 0;
  176 }
  177 
  178 // When recursing into directories, do not want to check the include_ext list.
  179 #ifndef UTEST
  180 static
  181 #endif
  182 int file_is_included_no_incext(struct conf **confs, const char *fname)
  183 {
  184     int ret=0;
  185     int longest=0;
  186     int matching=0;
  187     struct strlist *l=NULL;
  188     struct strlist *best=NULL;
  189 
  190     if(in_exclude_ext(get_strlist(confs[OPT_EXCEXT]), fname)
  191       || in_exclude_regex(get_strlist(confs[OPT_EXCREG]), fname)
  192         || !in_include_regex(get_strlist(confs[OPT_INCREG]), fname))
  193         return 0;
  194 
  195     // Check include/exclude directories.
  196     for(l=get_strlist(confs[OPT_INCEXCDIR]); l; l=l->next)
  197     {
  198         matching=is_subdir(l->path, fname);
  199         if(matching>=longest)
  200         {
  201             longest=matching;
  202             best=l;
  203         }
  204     }
  205     if(!best) ret=0;
  206     else ret=best->flag;
  207 
  208     return ret;
  209 }
  210 
  211 static int file_is_included(struct conf **confs,
  212     const char *fname, bool top_level)
  213 {
  214     // Always save the top level directory.
  215     // This will help in the simulation of browsing backups because it
  216     // will mean that there is always a directory before any files:
  217     // d /home/graham
  218     // f /home/graham/somefile.txt
  219     // This means that we can use the stats of the directory (/home/graham
  220     // in this example) as the stats of the parent directories (/home,
  221     // for example). Trust me on this.
  222     if(!top_level
  223       && !in_include_ext(get_strlist(confs[OPT_INCEXT]), fname)) return 0;
  224 
  225     return file_is_included_no_incext(confs, fname);
  226 }
  227 
  228 static int fs_change_is_allowed(struct conf **confs, const char *fname)
  229 {
  230     struct strlist *l;
  231     if(get_int(confs[OPT_CROSS_ALL_FILESYSTEMS])) return 1;
  232     for(l=get_strlist(confs[OPT_FSCHGDIR]); l; l=l->next)
  233         if(!strcmp(l->path, fname)) return 1;
  234     return 0;
  235 }
  236 
  237 static int need_to_read_fifo(struct conf **confs, const char *fname)
  238 {
  239     struct strlist *l;
  240     if(get_int(confs[OPT_READ_ALL_FIFOS])) return 1;
  241     for(l=get_strlist(confs[OPT_FIFOS]); l; l=l->next)
  242         if(!strcmp(l->path, fname)) return 1;
  243     return 0;
  244 }
  245 
  246 static int need_to_read_blockdev(struct conf **confs, const char *fname)
  247 {
  248     struct strlist *l;
  249     if(get_int(confs[OPT_READ_ALL_BLOCKDEVS])) return 1;
  250     for(l=get_strlist(confs[OPT_BLOCKDEVS]); l; l=l->next)
  251         if(!strcmp(l->path, fname)) return 1;
  252     return 0;
  253 }
  254 
  255 static int nobackup_directory(struct strlist *nobackup, const char *path)
  256 {
  257     struct stat statp;
  258     for(; nobackup; nobackup=nobackup->next)
  259     {
  260         char *fullpath=NULL;
  261         if(!(fullpath=prepend_s(path, nobackup->path)))
  262             return -1;
  263         if(!lstat(fullpath, &statp))
  264         {
  265             free_w(&fullpath);
  266             return 1;
  267         }
  268         free_w(&fullpath);
  269     }
  270     return 0;
  271 }
  272 
  273 static int file_size_match(struct FF_PKT *ff_pkt, struct conf **confs)
  274 {
  275     uint64_t sizeleft;
  276     uint64_t min_file_size=get_uint64_t(confs[OPT_MIN_FILE_SIZE]);
  277     uint64_t max_file_size=get_uint64_t(confs[OPT_MAX_FILE_SIZE]);
  278     sizeleft=(uint64_t)ff_pkt->statp.st_size;
  279 
  280     if(min_file_size && sizeleft<min_file_size)
  281         return 0;
  282     if(max_file_size && sizeleft>max_file_size)
  283         return 0;
  284     return 1;
  285 }
  286 
  287 // Last checks before actually processing the file system entry.
  288 static int my_send_file_w(struct asfd *asfd, struct FF_PKT *ff, bool top_level, struct conf **confs)
  289 {
  290     if(!file_is_included(confs, ff->fname, top_level)
  291         || is_logic_excluded(confs, ff)) return 0;
  292 
  293     // Doing the file size match here also catches hard links.
  294     if(S_ISREG(ff->statp.st_mode)
  295       && !file_size_match(ff, confs))
  296         return 0;
  297 
  298     /*
  299      * Handle hard linked files
  300      * Maintain a list of hard linked files already backed up. This
  301      *  allows us to ensure that the data of each file gets backed
  302      *  up only once.
  303      */
  304     if(ff->statp.st_nlink > 1
  305       && (S_ISREG(ff->statp.st_mode)
  306         || S_ISCHR(ff->statp.st_mode)
  307         || S_ISBLK(ff->statp.st_mode)
  308         || S_ISFIFO(ff->statp.st_mode)
  309         || S_ISSOCK(ff->statp.st_mode)))
  310     {
  311         struct f_link *lp;
  312         struct f_link **bucket=NULL;
  313 
  314         if((lp=linkhash_search(&ff->statp, &bucket)))
  315         {
  316             if(!strcmp(lp->name, ff->fname)) return 0;
  317             ff->link=lp->name;
  318             /* Handle link, file already saved */
  319             ff->type=FT_LNK_H;
  320         }
  321         else
  322         {
  323             if(linkhash_add(ff->fname,
  324                 &ff->statp, bucket)) return -1;
  325         }
  326     }
  327 
  328     return my_send_file(asfd, ff, confs);
  329 }
  330 
  331 static int found_regular_file(struct asfd *asfd,
  332     struct FF_PKT *ff_pkt, struct conf **confs,
  333     bool top_level)
  334 {
  335     ff_pkt->type=FT_REG;
  336     return my_send_file_w(asfd, ff_pkt, top_level, confs);
  337 }
  338 
  339 static int found_soft_link(struct asfd *asfd, struct FF_PKT *ff_pkt, struct conf **confs,
  340     char *fname, bool top_level)
  341 {
  342     ssize_t size;
  343     char *buffer=(char *)alloca(fs_full_path_max+102);
  344 
  345     if((size=readlink(fname, buffer, fs_full_path_max+101))<0)
  346     {
  347         /* Could not follow link */
  348         ff_pkt->type=FT_NOFOLLOW;
  349     }
  350     else
  351     {
  352         buffer[size]=0;
  353         ff_pkt->link=buffer;    /* point to link */
  354         ff_pkt->type=FT_LNK_S;  /* got a soft link */
  355     }
  356     return my_send_file_w(asfd, ff_pkt, top_level, confs);
  357 }
  358 
  359 static int fstype_matches(struct asfd *asfd,
  360     struct conf **confs, const char *fname, int inex)
  361 {
  362 #if defined(HAVE_LINUX_OS) \
  363  || defined(HAVE_SUN_OS)
  364     struct strlist *l;
  365 #if defined(HAVE_LINUX_OS)
  366     struct statfs buf;
  367     if(statfs(fname, &buf))
  368 #elif defined(HAVE_SUN_OS)
  369     struct statvfs buf;
  370     if(statvfs(fname, &buf))
  371 #endif
  372     {
  373         logw(asfd, get_cntr(confs), "Could not statfs %s: %s\n",
  374             fname, strerror(errno));
  375         return -1;
  376     }
  377     for(l=get_strlist(confs[inex]); l; l=l->next)
  378 #if defined(HAVE_LINUX_OS)
  379         if(l->flag==buf.f_type)
  380 #elif defined(HAVE_SUN_OS)
  381         if(strcmp(l->path,buf.f_basetype)==0)
  382 #endif
  383             return -1;
  384 #elif defined(HAVE_WIN32)
  385     char filesystem_name[MAX_PATH_UTF8 + 1];
  386     if (win32_getfsname(fname, filesystem_name, sizeof(filesystem_name)))
  387         return -1;
  388     for(strlist *l=get_strlist(confs[inex]); l; l=l->next)
  389         if(strcmp(l->path,filesystem_name)==0)
  390             return -1;
  391 #endif
  392     return 0;
  393 }
  394 
  395 #if defined(HAVE_WIN32)
  396 static void windows_reparse_point_fiddling(struct FF_PKT *ff_pkt)
  397 {
  398     /*
  399     * We have set st_rdev to 1 if it is a reparse point, otherwise 0,
  400     *  if st_rdev is 2, it is a mount point.
  401     */
  402     /*
  403      * A reparse point (WIN32_REPARSE_POINT)
  404      *  is something special like one of the following:
  405      *  IO_REPARSE_TAG_DFS              0x8000000A
  406      *  IO_REPARSE_TAG_DFSR             0x80000012
  407      *  IO_REPARSE_TAG_HSM              0xC0000004
  408      *  IO_REPARSE_TAG_HSM2             0x80000006
  409      *  IO_REPARSE_TAG_SIS              0x80000007
  410      *  IO_REPARSE_TAG_SYMLINK          0xA000000C
  411      *
  412      * A junction point is a:
  413      *  IO_REPARSE_TAG_MOUNT_POINT      0xA0000003
  414      * which can be either a link to a Volume (WIN32_MOUNT_POINT)
  415      * or a link to a directory (WIN32_JUNCTION_POINT)
  416      *
  417      * Ignore WIN32_REPARSE_POINT and WIN32_JUNCTION_POINT
  418      */
  419     if (ff_pkt->statp.st_rdev == WIN32_REPARSE_POINT) {
  420         ff_pkt->type = FT_REPARSE;
  421     } else if (ff_pkt->statp.st_rdev == WIN32_JUNCTION_POINT) {
  422         ff_pkt->type = FT_JUNCTION;
  423     }
  424 }
  425 #endif
  426 
  427 // Prototype because process_entries_in_directory() recurses using find_files().
  428 static int find_files(struct asfd *asfd,
  429     struct FF_PKT *ff_pkt, struct conf **confs,
  430     char *fname, dev_t parent_device, bool top_level);
  431 
  432 static int process_entries_in_directory(struct asfd *asfd, char **nl,
  433     int count, char **link, size_t len, size_t *link_len,
  434     struct conf **confs, struct FF_PKT *ff_pkt, dev_t our_device)
  435 {
  436     int m=0;
  437     int ret=0;
  438     for(m=0; m<count; m++)
  439     {
  440         size_t i;
  441         char *p=NULL;
  442         char *q=NULL;
  443         size_t plen;
  444 
  445         p=nl[m];
  446 
  447         if(strlen(p)+len>=*link_len)
  448         {
  449             *link_len=len+strlen(p)+1;
  450             if(!(*link=(char *)
  451               realloc_w(*link, (*link_len)+1, __func__)))
  452                 return -1;
  453         }
  454         q=(*link)+len;
  455         plen=strlen(p);
  456         for(i=0; i<plen; i++)
  457             *q++=*p++;
  458         *q=0;
  459         ff_pkt->flen=i;
  460 
  461         if(file_is_included_no_incext(confs, *link))
  462         {
  463             ret=find_files(asfd, ff_pkt,
  464                 confs, *link, our_device, false /*top_level*/);
  465         }
  466         else
  467         {
  468             struct strlist *x;
  469             // Excluded, but there might be a subdirectory that is
  470             // included.
  471             for(x=get_strlist(confs[OPT_INCEXCDIR]); x; x=x->next)
  472             {
  473                 if(x->flag
  474                   && is_subdir(*link, x->path))
  475                 {
  476                     struct strlist *y;
  477                     if((ret=find_files(asfd, ff_pkt,
  478                         confs, x->path,
  479                         our_device, false)))
  480                             break;
  481                     // Now need to skip subdirectories of
  482                     // the thing that we just stuck in
  483                     // find_one_file(), or we might get
  484                     // some things backed up twice.
  485                     for(y=x->next; y; y=y->next)
  486                         if(y->next
  487                          && is_subdir(x->path, y->path))
  488                             y=y->next;
  489                 }
  490             }
  491         }
  492         free_w(&(nl[m]));
  493         if(ret) break;
  494     }
  495     return ret;
  496 }
  497 
  498 static int found_directory(struct asfd *asfd,
  499     struct FF_PKT *ff_pkt, struct conf **confs,
  500     char *fname, dev_t parent_device, bool top_level)
  501 {
  502     int ret=-1;
  503     char *link=NULL;
  504     size_t link_len;
  505     size_t len;
  506     int nbret=0;
  507     int count=0;
  508     dev_t our_device;
  509     char **nl=NULL;
  510 
  511     our_device=ff_pkt->statp.st_dev;
  512 
  513     if((nbret=nobackup_directory(get_strlist(confs[OPT_NOBACKUP]),
  514         ff_pkt->fname)))
  515     {
  516         if(nbret<0) goto end; // Error.
  517         ret=0; // Do not back it up.
  518         goto end;
  519     }
  520 
  521     /* Build a canonical directory name with a trailing slash in link var */
  522     len=strlen(fname);
  523     link_len=len+200;
  524     if(!(link=(char *)malloc_w(link_len+2, __func__)))
  525         goto end;
  526     snprintf(link, link_len, "%s", fname);
  527 
  528     /* Strip all trailing slashes */
  529     while(len >= 1 && IsPathSeparator(link[len - 1])) len--;
  530     /* add back one */
  531     link[len++]='/';
  532     link[len]=0;
  533 
  534     ff_pkt->link=link;
  535     ff_pkt->type=FT_DIR;
  536 
  537 #if defined(HAVE_WIN32)
  538     windows_reparse_point_fiddling(ff_pkt);
  539 #endif
  540 
  541     if(my_send_file_w(asfd, ff_pkt, top_level, confs))
  542         goto end;
  543     if(ff_pkt->type==FT_REPARSE || ff_pkt->type==FT_JUNCTION)
  544     {
  545         // Ignore.
  546         ret=0;
  547         goto end;
  548     }
  549 
  550     if(top_level
  551       || (parent_device!=ff_pkt->statp.st_dev
  552 #if defined(HAVE_WIN32)
  553         || ff_pkt->statp.st_rdev==WIN32_MOUNT_POINT
  554 #endif
  555         ))
  556     {
  557         if(fstype_matches(asfd, confs, ff_pkt->fname, OPT_EXCFS)
  558           || (get_strlist(confs[OPT_INCFS])
  559              && !fstype_matches(asfd, confs, ff_pkt->fname, OPT_INCFS)))
  560         {
  561             if(top_level)
  562                 logw(asfd, get_cntr(confs),
  563                     "Skipping '%s' because of file system include or exclude.\n", fname);
  564             ret=my_send_file_w(asfd, ff_pkt, top_level, confs);
  565             goto end;
  566         }
  567         if(!top_level && !fs_change_is_allowed(confs, ff_pkt->fname))
  568         {
  569             ff_pkt->type=FT_NOFSCHG;
  570             // Just backup the directory and return.
  571             ret=my_send_file_w(asfd, ff_pkt, top_level, confs);
  572             goto end;
  573         }
  574     }
  575 
  576     ff_pkt->link=ff_pkt->fname;
  577 
  578     errno=0;
  579     switch(entries_in_directory_alphasort(fname,
  580         &nl, &count, get_int(confs[OPT_ATIME]),
  581         /* follow_symlinks */ 0))
  582     {
  583         case 0: break;
  584         case 1:
  585             ff_pkt->type=FT_NOOPEN;
  586             ret=my_send_file_w(asfd, ff_pkt, top_level, confs);
  587         default:
  588             goto end;
  589     }
  590 
  591     if(nl)
  592     {
  593         if(process_entries_in_directory(asfd, nl, count,
  594             &link, len, &link_len, confs, ff_pkt, our_device))
  595                 goto end;
  596     }
  597     ret=0;
  598 end:
  599     free_w(&link);
  600     free_v((void **)&nl);
  601     return ret;
  602 }
  603 
  604 static int found_other(struct asfd *asfd, struct FF_PKT *ff_pkt,
  605     struct conf **confs, bool top_level)
  606 {
  607 #ifdef HAVE_FREEBSD_OS
  608     /*
  609      * On FreeBSD, all block devices are character devices, so
  610      *   to be able to read a raw disk, we need the check for
  611      *   a character device.
  612      * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/ad0s3
  613      * crw-r----- 1 root  operator - 116, 0x00040002 Jun 9 19:32 /dev/rad0s3
  614      */
  615     if((S_ISBLK(ff_pkt->statp.st_mode) || S_ISCHR(ff_pkt->statp.st_mode))
  616         && need_to_read_blockdev(confs, ff_pkt->fname))
  617     {
  618 #else
  619     if(S_ISBLK(ff_pkt->statp.st_mode)
  620         && need_to_read_blockdev(confs, ff_pkt->fname))
  621     {
  622 #endif
  623         /* raw partition */
  624         ff_pkt->type=FT_RAW;
  625     }
  626     else if(S_ISFIFO(ff_pkt->statp.st_mode)
  627         && need_to_read_fifo(confs, ff_pkt->fname))
  628     {
  629         ff_pkt->type=FT_FIFO;
  630     }
  631     else
  632     {
  633         /* The only remaining are special (character, ...) files */
  634         ff_pkt->type=FT_SPEC;
  635     }
  636     return my_send_file_w(asfd, ff_pkt, top_level, confs);
  637 }
  638 
  639 static int find_files(struct asfd *asfd, struct FF_PKT *ff_pkt, struct conf **confs,
  640     char *fname, dev_t parent_device, bool top_level)
  641 {
  642     ff_pkt->fname=fname;
  643     ff_pkt->link=fname;
  644 
  645 #ifdef HAVE_WIN32
  646     if(win32_lstat(fname, &ff_pkt->statp, &ff_pkt->winattr))
  647 #else
  648     if(lstat(fname, &ff_pkt->statp))
  649 #endif
  650     {
  651         ff_pkt->type=FT_NOSTAT;
  652         return my_send_file_w(asfd, ff_pkt, top_level, confs);
  653     }
  654 
  655     if(S_ISREG(ff_pkt->statp.st_mode))
  656         return found_regular_file(asfd, ff_pkt, confs, top_level);
  657     else if(S_ISLNK(ff_pkt->statp.st_mode))
  658     {
  659 #ifdef S_IFLNK
  660         /* A symlink.
  661            If they have specified the symlink in a read_blockdev
  662            argument, treat it as a block device.
  663         */
  664         struct strlist *l;
  665         for(l=get_strlist(confs[OPT_BLOCKDEVS]); l; l=l->next)
  666         {
  667             if(!strcmp(l->path, fname))
  668             {
  669                 ff_pkt->statp.st_mode ^= S_IFLNK;
  670                 ff_pkt->statp.st_mode |= S_IFBLK;
  671                 return found_other(asfd, ff_pkt, confs,
  672                     top_level);
  673             }
  674         }
  675 #endif
  676         return found_soft_link(asfd, ff_pkt, confs, fname, top_level);
  677     }
  678     else if(S_ISDIR(ff_pkt->statp.st_mode))
  679         return found_directory(asfd, ff_pkt, confs, fname,
  680             parent_device, top_level);
  681     else
  682         return found_other(asfd, ff_pkt, confs, top_level);
  683 }
  684 
  685 int find_files_begin(struct asfd *asfd,
  686     struct FF_PKT *ff_pkt, struct conf **confs, char *fname)
  687 {
  688     return find_files(asfd, ff_pkt,
  689         confs, fname, (dev_t)-1, 1 /* top_level */);
  690 }