LCOV - code coverage report
Current view: top level - src - fsops.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 152 204 74.5 %
Date: 2016-11-07 Functions: 25 29 86.2 %

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

Generated by: LCOV version 1.10