LCOV - code coverage report
Current view: top level - src - fsops.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 171 221 77.4 %
Date: 2022-08-30 22:36:43 Functions: 24 27 88.9 %

          Line data    Source code
       1             : #include "burp.h"
       2             : #include "alloc.h"
       3             : #include "fsops.h"
       4             : #include "fzp.h"
       5             : #include "log.h"
       6             : #include "pathcmp.h"
       7             : #include "prepend.h"
       8             : 
       9             : #ifndef HAVE_WIN32
      10             : #include <sys/un.h>
      11             : #endif
      12             : 
      13             : uint32_t fs_name_max=0;
      14             : uint32_t fs_full_path_max=0;
      15             : static uint32_t fs_path_max=0;
      16             : 
      17         260 : void close_fd(int *fd)
      18             : {
      19         260 :         if(!fd || *fd<0) return;
      20             :         //logp("closing %d\n", *fd);
      21           9 :         close(*fd);
      22           9 :         *fd=-1;
      23             : }
      24             : 
      25           0 : int is_dir_lstat(const char *path)
      26             : {
      27             :         struct stat buf;
      28           0 :         if(lstat(path, &buf))
      29             :                 return -1;
      30           0 :         return S_ISDIR(buf.st_mode);
      31             : }
      32             : 
      33          55 : int is_reg_lstat(const char *path)
      34             : {
      35             :         struct stat buf;
      36          55 :         if(lstat(path, &buf))
      37             :                 return -1;
      38           7 :         return S_ISREG(buf.st_mode);
      39             : }
      40             : 
      41        2492 : int is_dir(const char *path, struct dirent *d)
      42             : {
      43             : #ifdef _DIRENT_HAVE_D_TYPE
      44             :         // Faster evaluation on most systems.
      45        2492 :         switch(d->d_type)
      46             :         {
      47             :                 case DT_DIR:
      48             :                         return 1;
      49             :                 case DT_UNKNOWN:
      50             :                         break;
      51             :                 default:
      52        1315 :                         return 0;
      53             :         }
      54             : #endif
      55           0 :         return is_dir_lstat(path);
      56             : }
      57             : 
      58        2128 : int mkpath(char **rpath, const char *limit)
      59             : {
      60        2128 :         int ret=-1;
      61        2128 :         char *cp=NULL;
      62             :         struct stat buf;
      63             : 
      64        2128 :         if((cp=strrchr(*rpath, '/')))
      65             :         {
      66        1488 :                 *cp='\0';
      67             : #ifdef HAVE_WIN32
      68             :                 if(strlen(*rpath)==2 && (*rpath)[1]==':')
      69             :                 {
      70             :                         // We are down to the drive letter, which is OK.
      71             :                 } else
      72             : #endif
      73        1488 :                 if(!**rpath)
      74             :                 {
      75             :                         // We are down to the root, which is OK.
      76             :                 }
      77        2976 :                 else if(lstat(*rpath, &buf))
      78             :                 {
      79             :                         // does not exist - recurse further down, then come
      80             :                         // back and try to mkdir it.
      81         910 :                         if(mkpath(rpath, limit)) goto end;
      82             : 
      83             :                         // Require that the user has set up the required paths
      84             :                         // on the server correctly. I have seen problems with
      85             :                         // part of the path being a temporary symlink that
      86             :                         // gets replaced by burp with a proper directory.
      87             :                         // Allow it to create the actual directory specified,
      88             :                         // though.
      89             : 
      90             :                         // That is, if limit is:
      91             :                         // /var/spool/burp
      92             :                         // and /var/spool exists, the directory will be
      93             :                         // created.
      94             :                         // If only /var exists, the directory will not be
      95             :                         // created.
      96             : 
      97             :                         // Caller can give limit=NULL to create the whole
      98             :                         // path with no limit, as in a restore.
      99         910 :                         if(limit && pathcmp(*rpath, limit)<0)
     100             :                         {
     101           0 :                                 logp("will not mkdir %s\n", *rpath);
     102           0 :                                 goto end;
     103             :                         }
     104         910 :                         if(mkdir(*rpath, 0777))
     105             :                         {
     106           0 :                                 logp("could not mkdir %s: %s\n", *rpath, strerror(errno));
     107           0 :                                 goto end;
     108             :                         }
     109             :                 }
     110         578 :                 else if(S_ISDIR(buf.st_mode))
     111             :                 {
     112             :                         // Is a directory - can put the slash back and return.
     113             :                 }
     114          43 :                 else if(S_ISLNK(buf.st_mode))
     115             :                 {
     116             :                         // to help with the 'current' symlink
     117             :                 }
     118             :                 else
     119             :                 {
     120             :                         // something funny going on
     121           0 :                         logp("warning: wanted '%s' to be a directory\n",
     122             :                                 *rpath);
     123             :                 }
     124             :         }
     125             : 
     126             :         ret=0;
     127             : end:
     128        2128 :         if(cp) *cp='/';
     129        2128 :         return ret;
     130             : }
     131             : 
     132        1117 : int build_path(const char *datadir, const char *fname, char **rpath, const char *limit)
     133             : {
     134        1117 :         if(!(*rpath=prepend_s(datadir, fname))) return -1;
     135        1117 :         if(mkpath(rpath, limit))
     136             :         {
     137           0 :                 free_w(rpath);
     138           0 :                 return -1;
     139             :         }
     140             :         return 0;
     141             : }
     142             : 
     143         138 : int do_rename(const char *oldpath, const char *newpath)
     144             : {
     145             :         // Be careful, this is not actually atomic. Everything that uses this
     146             :         // needs to deal with the consequences.
     147         138 :         if(rename(oldpath, newpath))
     148             :         {
     149           0 :                 logp("could not rename '%s' to '%s': %s\n",
     150           0 :                         oldpath, newpath, strerror(errno)); 
     151           0 :                 return -1; 
     152             :         }
     153             :         return 0;
     154             : }
     155             : 
     156        1105 : int build_path_w(const char *path)
     157             : {
     158             :         int ret;
     159        1105 :         char *rpath=NULL;
     160        1105 :         ret=build_path(path, "", &rpath, NULL);
     161        1105 :         free_w(&rpath);
     162        1105 :         return ret;
     163             : }
     164             : 
     165             : #define RECDEL_ERROR                    -1
     166             : #define RECDEL_OK                       0
     167             : #define RECDEL_ENTRIES_REMAINING        1
     168             : 
     169             : static void get_max(int32_t *max, int32_t default_max)
     170             : {
     171        1268 :         *max = pathconf(".", default_max);
     172        1268 :         if(*max < 1024) *max = 1024;
     173             :         // Add for EOS.
     174        1268 :         (*max)++;
     175             : }
     176             : 
     177        2445 : static int do_recursive_delete(const char *d, const char *file,
     178             :         uint8_t delfiles, int32_t name_max,
     179             :         uint8_t ignore_not_empty_errors)
     180             : {
     181        2445 :         int ret=RECDEL_ERROR;
     182        2445 :         DIR *dirp=NULL;
     183        2445 :         struct dirent *entry=NULL;
     184             :         struct stat statp;
     185        2445 :         char *directory=NULL;
     186        2445 :         char *fullpath=NULL;
     187             : 
     188        2445 :         if(!file)
     189             :         {
     190        1268 :                 if(!(directory=prepend_s(d, "")))
     191             :                         goto end;
     192             :         }
     193        1177 :         else if(!(directory=prepend_s(d, file)))
     194             :         {
     195           0 :                 log_out_of_memory(__func__);
     196           0 :                 goto end;
     197             :         }
     198             : 
     199        4890 :         if(lstat(directory, &statp))
     200             :         {
     201             :                 // path does not exist.
     202             :                 ret=RECDEL_OK;
     203             :                 goto end;
     204             :         }
     205             : 
     206        1577 :         if(!(dirp=opendir(directory)))
     207             :         {
     208           0 :                 logp("opendir %s in %s: %s\n",
     209           0 :                         directory, __func__, strerror(errno));
     210           0 :                 goto end;
     211             :         }
     212             : 
     213             :         while(1)
     214             :         {
     215        7223 :                 errno=0;
     216        7223 :                 if(!(entry=readdir(dirp)))
     217             :                 {
     218        1577 :                         if(errno)
     219             :                         {
     220           0 :                                 logp("error in readdir in %s: %s\n",
     221             :                                         __func__, strerror(errno));
     222           0 :                                 goto end;
     223             :                         }
     224             :                         // Got to the end of the directory.
     225        1577 :                         ret=RECDEL_OK;
     226             :                         break;
     227             :                 }
     228             : 
     229        5646 :                 if(!filter_dot(entry))
     230        3154 :                         continue;
     231        2492 :                 free_w(&fullpath);
     232        2492 :                 if(!(fullpath=prepend_s(directory, entry->d_name)))
     233             :                         goto end;
     234             : 
     235        2492 :                 if(is_dir(fullpath, entry)>0)
     236             :                 {
     237             :                         int r;
     238        1177 :                         if((r=do_recursive_delete(directory, entry->d_name,
     239             :                                 delfiles, name_max,
     240             :                                 ignore_not_empty_errors))==RECDEL_ERROR)
     241             :                                         goto end;
     242             :                         // do not overwrite ret with OK if it previously
     243             :                         // had ENTRIES_REMAINING
     244        1177 :                         if(r==RECDEL_ENTRIES_REMAINING) ret=r;
     245             :                 }
     246        1315 :                 else if(delfiles)
     247             :                 {
     248        1315 :                         if(unlink(fullpath))
     249             :                         {
     250           0 :                                 logp("unlink %s: %s\n",
     251             :                                         fullpath, strerror(errno));
     252           0 :                                 ret=RECDEL_ENTRIES_REMAINING;
     253             :                         }
     254             :                 }
     255             :                 else
     256             :                 {
     257             :                         ret=RECDEL_ENTRIES_REMAINING;
     258             :                 }
     259             :         }
     260             : 
     261        1577 :         if(ret==RECDEL_OK && rmdir(directory))
     262             :         {
     263           0 :                 if(errno!=ENOTEMPTY || !ignore_not_empty_errors)
     264             :                 {
     265           0 :                         logp("rmdir %s: %s\n", directory, strerror(errno));
     266           0 :                         ret=RECDEL_ERROR;
     267             :                 }
     268             :         }
     269             : end:
     270        2445 :         if(dirp) closedir(dirp);
     271        2445 :         free_w(&fullpath);
     272        2445 :         free_w(&directory);
     273        2445 :         return ret;
     274             : }
     275             : 
     276        1268 : static int do_recursive_delete_w(const char *path, uint8_t delfiles,
     277             :         uint8_t ignore_not_empty_errors)
     278             : {
     279             :         int32_t name_max;
     280        1268 :         get_max(&name_max, _PC_NAME_MAX);
     281        1268 :         return do_recursive_delete(path,
     282             :                 NULL, delfiles, name_max, ignore_not_empty_errors);
     283             : }
     284             : 
     285        1262 : int recursive_delete(const char *path)
     286             : {
     287             :         struct stat statp;
     288             :         // We might have been given a file entry, instead of a directory.
     289        1262 :         if(!lstat(path, &statp) && !S_ISDIR(statp.st_mode))
     290             :         {
     291         130 :                 if(unlink(path))
     292             :                 {
     293           0 :                         logp("unlink %s: %s\n", path, strerror(errno));
     294           0 :                         return RECDEL_ENTRIES_REMAINING;
     295             :                 }
     296             :         }
     297        1262 :         return do_recursive_delete_w(path, 1, 0/*ignore_not_empty_errors*/);
     298             : }
     299             : 
     300           2 : int recursive_delete_dirs_only(const char *path)
     301             : {
     302           2 :         return do_recursive_delete_w(path, 0, 0/*ignore_not_empty_errors*/);
     303             : }
     304             : 
     305           4 : int recursive_delete_dirs_only_no_warnings(const char *path)
     306             : {
     307           4 :         return do_recursive_delete_w(path, 0, 1/*ignore_not_empty_errors*/);
     308             : }
     309             : 
     310           0 : int unlink_w(const char *path, const char *func)
     311             : {
     312           0 :         if(unlink(path))
     313             :         {
     314           0 :                 logp("unlink(%s) called from %s(): %s\n",
     315           0 :                         path, func, strerror(errno));
     316           0 :                 return -1;
     317             :         }
     318             :         return 0;
     319             : }
     320             : 
     321             : static void init_max(const char *path,
     322             :         uint32_t *max, int what, uint32_t default_max)
     323             : {
     324          14 :         *max=pathconf(path?path:".", what);
     325          14 :         if(*max<default_max) *max=default_max;
     326             : }
     327             : 
     328           7 : int init_fs_max(const char *path)
     329             : {
     330             :         struct stat statp;
     331           7 :         if(stat(path, &statp))
     332             :         {
     333           0 :                 logp("Path %s does not exist in %s\n", path, __func__);
     334           0 :                 return -1;
     335             :         }
     336             :         // Get system path and filename maximum lengths.
     337           7 :         init_max(path, &fs_path_max, _PC_PATH_MAX, 1024);
     338           7 :         init_max(path, &fs_name_max, _PC_NAME_MAX, 255);
     339           7 :         fs_full_path_max=fs_path_max+fs_name_max;
     340           7 :         return 0;
     341             : }
     342             : 
     343          26 : static int do_get_entries_in_directory(DIR *directory, char ***nl,
     344             :         int *count, int (*compar)(const void *, const void *))
     345             : {
     346          26 :         int allocated=0;
     347          26 :         char **ntmp=NULL;
     348          26 :         struct dirent *result=NULL;
     349             : 
     350          26 :         *count=0;
     351             : 
     352             :         // This here is doing a funky kind of scandir/alphasort
     353             :         // that can also run on Windows.
     354             :         while(1)
     355             :         {
     356         157 :                 errno=0;
     357         157 :                 if(!(result=readdir(directory)))
     358             :                 {
     359          26 :                         if(errno)
     360             :                         {
     361           0 :                                 logp("error in readdir: %s\n",
     362             :                                         strerror(errno));
     363           0 :                                 goto error;
     364             :                         }
     365             :                         break;
     366             :                 }
     367             : 
     368         131 :                 if(!filter_dot(result))
     369          52 :                         continue;
     370             : 
     371          79 :                 if(*count==allocated)
     372             :                 {
     373          20 :                         if(!allocated) allocated=10;
     374           0 :                         else allocated*=2;
     375             : 
     376          20 :                         if(!(ntmp=(char **)
     377          20 :                           realloc_w(*nl, allocated*sizeof(**nl), __func__)))
     378             :                                 goto error;
     379          20 :                         *nl=ntmp;
     380             :                 }
     381          79 :                 if(!((*nl)[(*count)++]=strdup_w(result->d_name, __func__)))
     382             :                         goto error;
     383             :         }
     384          26 :         if(*nl && compar)
     385          20 :                 qsort(*nl, *count, sizeof(**nl), compar);
     386             :         return 0;
     387             : error:
     388           0 :         if(*nl)
     389             :         {
     390             :                 int i;
     391           0 :                 for(i=0; i<*count; i++)
     392           0 :                         free_w(&((*nl)[i]));
     393           0 :                 free_v((void **)nl);
     394             :         }
     395             :         return -1;
     396             : }
     397             : 
     398          26 : static int entries_in_directory(const char *path, char ***nl,
     399             :         int *count, int atime, int follow_symlinks,
     400             :         int (*compar)(const char **, const char **))
     401             : {
     402          26 :         int ret=0;
     403          26 :         DIR *directory=NULL;
     404             : 
     405          26 :         if(!fs_name_max)
     406             :         {
     407             :                 // Get system path and filename maximum lengths.
     408             :                 // FIX THIS: maybe this should be done every time a file system
     409             :                 // is crossed?
     410           2 :                 if(init_fs_max(path)) return -1;
     411             :         }
     412             : #if defined(O_DIRECTORY) && defined(O_NOATIME)
     413          26 :         int dfd=-1;
     414          52 :         if((dfd=open(path, O_RDONLY|O_DIRECTORY|(atime?0:O_NOATIME)
     415             : #ifdef O_NOFOLLOW
     416          26 :           |(follow_symlinks?0:O_NOFOLLOW)
     417             : #endif
     418             :           ))<0
     419          26 :           || !(directory=fdopendir(dfd)))
     420             : #else
     421             : // Mac OS X appears to have no O_NOATIME and no fdopendir(), so it should
     422             : // end up using opendir() here.
     423             :         if(!(directory=opendir(path)))
     424             : #endif
     425             :         {
     426             : #if defined(O_DIRECTORY) && defined(O_NOATIME)
     427             :                 close_fd(&dfd);
     428             : #endif
     429             :                 ret=1;
     430             :         }
     431             :         else
     432             :         {
     433          26 :                 if(do_get_entries_in_directory(directory, nl, count,
     434             :                         (int (*)(const void *, const void *))compar))
     435           0 :                                 ret=-1;
     436             :         }
     437          26 :         if(directory) closedir(directory);
     438             :         return ret;
     439             : }
     440             : 
     441        7032 : int filter_dot(const struct dirent *d)
     442             : {
     443        7032 :         if(!d
     444        7032 :           || !strcmp(d->d_name, ".")
     445        5252 :           || !strcmp(d->d_name, ".."))
     446             :                 return 0;
     447        3472 :         return 1;
     448             : }
     449             : 
     450         108 : static int my_alphasort(const char **a, const char **b)
     451             : {
     452         108 :         return pathcmp(*a, *b);
     453             : }
     454             : 
     455          26 : int entries_in_directory_alphasort(const char *path, char ***nl,
     456             :         int *count, int atime, int follow_symlinks)
     457             : {
     458          26 :         return entries_in_directory(path, nl, count, atime, follow_symlinks,
     459             :                 my_alphasort);
     460             : }
     461             : 
     462             : #define FULL_CHUNK      4096
     463             : 
     464          10 : int files_equal(const char *opath, const char *npath, int compressed)
     465             : {
     466          10 :         int ret=0;
     467             :         size_t ogot;
     468             :         size_t ngot;
     469          10 :         unsigned int i=0;
     470          10 :         struct fzp *ofp=NULL;
     471          10 :         struct fzp *nfp=NULL;
     472             :         static char obuf[FULL_CHUNK];
     473             :         static char nbuf[FULL_CHUNK];
     474             : 
     475          10 :         if(compressed)
     476             :         {
     477           2 :                 ofp=fzp_gzopen(opath, "rb");
     478           2 :                 nfp=fzp_gzopen(npath, "rb");
     479             :         }
     480             :         else
     481             :         {
     482           8 :                 ofp=fzp_open(opath, "rb");
     483           8 :                 nfp=fzp_open(npath, "rb");
     484             :         }
     485             : 
     486          10 :         if(!ofp && !nfp)
     487             :         {
     488             :                 ret=1;
     489             :                 goto end;
     490             :         }
     491          10 :         if(!ofp && nfp)
     492             :                 goto end;
     493          10 :         if(!nfp && ofp)
     494             :                 goto end;
     495             : 
     496             :         while(1)
     497             :         {
     498          10 :                 ogot=fzp_read(ofp, obuf, FULL_CHUNK);
     499          10 :                 ngot=fzp_read(nfp, nbuf, FULL_CHUNK);
     500          10 :                 if(ogot!=ngot)
     501             :                         goto end;
     502        8394 :                 for(i=0; i<ogot; i++)
     503             :                 {
     504        8394 :                         if(obuf[i]!=nbuf[i])
     505             :                                 goto end;
     506             :                 }
     507          10 :                 if(ogot<FULL_CHUNK)
     508             :                         break;
     509             :         }
     510             :         ret=1;
     511             : end:
     512          10 :         fzp_close(&ofp);
     513          10 :         fzp_close(&nfp);
     514          10 :         return ret;
     515             : }
     516             : 
     517             : #ifndef HAVE_WIN32
     518           1 : int mksock(const char *path)
     519             : {
     520           1 :         int fd=-1;
     521           1 :         int ret=-1;
     522             :         struct sockaddr_un addr;
     523           1 :         memset(&addr, 0, sizeof(addr));
     524           1 :         addr.sun_family=AF_UNIX;
     525           1 :         strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
     526           1 :         if((fd=socket(addr.sun_family, SOCK_STREAM, 0))<0
     527           1 :           || (bind(fd, (struct sockaddr *)&addr, sizeof(addr)))<0)
     528             :                 goto end;
     529           1 :         ret=0;
     530             : end:
     531           1 :         if(fd>=0) close(fd);
     532           1 :         return ret;
     533             : }
     534             : 
     535          45 : int is_lnk_lstat(const char *path)
     536             : {
     537             :         struct stat buf;
     538          45 :         if(lstat(path, &buf))
     539             :                 return -1;
     540           0 :         return S_ISLNK(buf.st_mode);
     541             : }
     542             : 
     543           0 : int is_lnk_valid(const char *path)
     544             : {
     545             :         struct stat buf;
     546           0 :         if(stat(path, &buf))
     547             :                 return 0;
     548           0 :         return 1;
     549             : }
     550             : 
     551           3 : int do_symlink(const char *oldpath, const char *newpath)
     552             : {
     553           3 :         if(!symlink(oldpath, newpath))
     554             :                 return 0;
     555           0 :         logp("could not symlink '%s' to '%s': %s\n",
     556           0 :                 newpath, oldpath, strerror(errno));
     557           0 :         return -1;
     558             : }
     559             : 
     560             : static int do_readlink(const char *path, char buf[], size_t buflen)
     561             : {
     562             :         ssize_t len;
     563         166 :         if((len=readlink(path, buf, buflen-1))<0)
     564             :                 return -1;
     565         166 :         buf[len]='\0';
     566             :         return 0;
     567             : }
     568             : 
     569         468 : int readlink_w(const char *path, char buf[], size_t buflen)
     570             : {
     571             :         struct stat statp;
     572         468 :         if(lstat(path, &statp))
     573             :                 return -1;
     574         166 :         if(S_ISLNK(statp.st_mode))
     575             :                 return do_readlink(path, buf, buflen);
     576             :         return -1;
     577             : }
     578             : 
     579         460 : int readlink_w_in_dir(const char *dir, const char *lnk,
     580             :         char buf[], size_t buflen)
     581             : {
     582         460 :         char *tmp=NULL;
     583         460 :         if(!(tmp=prepend_s(dir, lnk)))
     584             :                 return -1;
     585         460 :         readlink_w(tmp, buf, buflen);
     586         460 :         free_w(&tmp);
     587         460 :         return 0;
     588             : }
     589             : 
     590             : #endif

Generated by: LCOV version 1.13