LCOV - code coverage report
Current view: top level - src - fsops.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 132 169 78.1 %
Date: 2016-01-03 Functions: 19 22 86.4 %

          Line data    Source code
       1             : #include "burp.h"
       2             : #include "alloc.h"
       3             : #include "fsops.h"
       4             : #include "log.h"
       5             : #include "pathcmp.h"
       6             : #include "prepend.h"
       7             : 
       8             : uint32_t fs_name_max=0;
       9             : uint32_t fs_full_path_max=0;
      10             : static uint32_t fs_path_max=0;
      11             : 
      12         196 : void close_fd(int *fd)
      13             : {
      14         393 :         if(!fd || *fd<0) return;
      15             :         //logp("closing %d\n", *fd);
      16         196 :         close(*fd);
      17         196 :         *fd=-1;
      18             : }
      19             : 
      20           0 : int is_dir_lstat(const char *path)
      21             : {
      22             :         struct stat buf;
      23             : 
      24           0 :         if(lstat(path, &buf)) return -1;
      25             : 
      26           0 :         return S_ISDIR(buf.st_mode);
      27             : }
      28             : 
      29        3841 : int is_dir(const char *path, struct dirent *d)
      30             : {
      31             : #ifdef _DIRENT_HAVE_D_TYPE
      32             :         // Faster evaluation on most systems.
      33        3841 :         switch(d->d_type)
      34             :         {
      35             :                 case DT_DIR:
      36             :                         return 1;
      37             :                 case DT_UNKNOWN:
      38             :                         break;
      39             :                 default:
      40        2112 :                         return 0;
      41             :         }
      42             : #endif
      43           0 :         return is_dir_lstat(path);
      44             : }
      45             : 
      46        4116 : int mkpath(char **rpath, const char *limit)
      47             : {
      48        4116 :         int ret=-1;
      49        4116 :         char *cp=NULL;
      50             :         struct stat buf;
      51             : #ifdef HAVE_WIN32
      52             :         int windows_stupidity=0;
      53             : #endif
      54        8232 :         if((cp=strrchr(*rpath, '/')))
      55             :         {
      56        3325 :                 *cp='\0';
      57             : #ifdef HAVE_WIN32
      58             :                 if(strlen(*rpath)==2 && (*rpath)[1]==':')
      59             :                 {
      60             :                         (*rpath)[1]='\0';
      61             :                         windows_stupidity++;
      62             :                 }
      63             : #endif
      64        3325 :                 if(!**rpath)
      65             :                 {
      66             :                         // We are down to the root, which is OK.
      67             :                 }
      68        6650 :                 else if(lstat(*rpath, &buf))
      69             :                 {
      70             :                         // does not exist - recurse further down, then come
      71             :                         // back and try to mkdir it.
      72        1366 :                         if(mkpath(rpath, limit)) goto end;
      73             : 
      74             :                         // Require that the user has set up the required paths
      75             :                         // on the server correctly. I have seen problems with
      76             :                         // part of the path being a temporary symlink that
      77             :                         // gets replaced by burp with a proper directory.
      78             :                         // Allow it to create the actual directory specified,
      79             :                         // though.
      80             : 
      81             :                         // That is, if limit is:
      82             :                         // /var/spool/burp
      83             :                         // and /var/spool exists, the directory will be
      84             :                         // created.
      85             :                         // If only /var exists, the directory will not be
      86             :                         // created.
      87             : 
      88             :                         // Caller can give limit=NULL to create the whole
      89             :                         // path with no limit, as in a restore.
      90        1366 :                         if(limit && pathcmp(*rpath, limit)<0)
      91             :                         {
      92           0 :                                 logp("will not mkdir %s\n", *rpath);
      93           0 :                                 goto end;
      94             :                         }
      95        1366 :                         if(mkdir(*rpath, 0777))
      96             :                         {
      97           0 :                                 logp("could not mkdir %s: %s\n", *rpath, strerror(errno));
      98           0 :                                 goto end;
      99             :                         }
     100             :                 }
     101        1959 :                 else if(S_ISDIR(buf.st_mode))
     102             :                 {
     103             :                         // Is a directory - can put the slash back and return.
     104             :                 }
     105          30 :                 else if(S_ISLNK(buf.st_mode))
     106             :                 {
     107             :                         // to help with the 'current' symlink
     108             :                 }
     109             :                 else
     110             :                 {
     111             :                         // something funny going on
     112             :                         logp("warning: wanted '%s' to be a directory\n",
     113           0 :                                 *rpath);
     114             :                 }
     115             :         }
     116             : 
     117             :         ret=0;
     118             : end:
     119             : #ifdef HAVE_WIN32
     120             :         if(windows_stupidity) (*rpath)[1]=':';
     121             : #endif
     122        4116 :         if(cp) *cp='/';
     123        4116 :         return ret;
     124             : }
     125             : 
     126        2746 : int build_path(const char *datadir, const char *fname, char **rpath, const char *limit)
     127             : {
     128             :         //logp("build path: '%s/%s'\n", datadir, fname);
     129        2746 :         if(!(*rpath=prepend_s(datadir, fname))) return -1;
     130        2746 :         if(mkpath(rpath, limit))
     131             :         {
     132           0 :                 free_w(rpath);
     133           0 :                 return -1;
     134             :         }
     135             :         return 0;
     136             : }
     137             : 
     138         128 : int do_rename(const char *oldpath, const char *newpath)
     139             : {
     140             :         // Be careful, this is not actually atomic. Everything that uses this
     141             :         // needs to deal with the consequences.
     142         128 :         if(rename(oldpath, newpath))
     143             :         {
     144             :                 logp("could not rename '%s' to '%s': %s\n",
     145           0 :                         oldpath, newpath, strerror(errno)); 
     146           0 :                 return -1; 
     147             :         }
     148             :         return 0;
     149             : }
     150             : 
     151        2718 : int build_path_w(const char *path)
     152             : {
     153             :         int ret;
     154        2718 :         char *rpath=NULL;
     155        2718 :         ret=build_path(path, "", &rpath, NULL);
     156        2718 :         free_w(&rpath);
     157        2718 :         return ret;
     158             : }
     159             : 
     160             : #define RECDEL_ERROR                    -1
     161             : #define RECDEL_OK                       0
     162             : #define RECDEL_ENTRIES_REMAINING        1
     163             : 
     164        1555 : static void get_max(int32_t *max, int32_t default_max)
     165             : {
     166        1555 :         *max = pathconf(".", default_max);
     167        1555 :         if(*max < 1024) *max = 1024;
     168             :         // Add for EOS.
     169        1555 :         (*max)++;
     170        1555 : }
     171             : 
     172        3284 : static int do_recursive_delete(const char *d, const char *file,
     173             :         uint8_t delfiles, int32_t name_max)
     174             : {
     175        3284 :         int ret=RECDEL_ERROR;
     176        3284 :         DIR *dirp=NULL;
     177        3284 :         struct dirent *entry=NULL;
     178             :         struct dirent *result;
     179             :         struct stat statp;
     180        3284 :         char *directory=NULL;
     181        3284 :         char *fullpath=NULL;
     182             : 
     183        3284 :         if(!file)
     184             :         {
     185        1555 :                 if(!(directory=prepend_s(d, ""))) goto end;
     186             :         }
     187        1729 :         else if(!(directory=prepend_s(d, file)))
     188             :         {
     189           0 :                 log_out_of_memory(__func__);
     190           0 :                 goto end;
     191             :         }
     192             : 
     193        6568 :         if(lstat(directory, &statp))
     194             :         {
     195             :                 // path does not exist.
     196             :                 ret=RECDEL_OK;
     197             :                 goto end;
     198             :         }
     199             : 
     200        2284 :         if(!(dirp=opendir(directory)))
     201             :         {
     202           0 :                 logp("opendir %s: %s\n", directory, strerror(errno));
     203           0 :                 goto end;
     204             :         }
     205             : 
     206        2284 :         if(!(entry=(struct dirent *)
     207        2284 :                 malloc_w(sizeof(struct dirent)+name_max+100, __func__)))
     208             :                         goto end;
     209             : 
     210             :         while(1)
     211             :         {
     212       10693 :                 if(readdir_r(dirp, entry, &result) || !result)
     213             :                 {
     214             :                         // Got to the end of the directory.
     215        2284 :                         ret=RECDEL_OK;
     216             :                         break;
     217             :                 }
     218             : 
     219        8409 :                 if(!entry->d_ino
     220        8409 :                   || !strcmp(entry->d_name, ".")
     221        6125 :                   || !strcmp(entry->d_name, ".."))
     222             :                         continue;
     223        3841 :                 free_w(&fullpath);
     224        3841 :                 if(!(fullpath=prepend_s(directory, entry->d_name)))
     225             :                         goto end;
     226             : 
     227        3841 :                 if(is_dir(fullpath, entry)>0)
     228             :                 {
     229             :                         int r;
     230        1729 :                         if((r=do_recursive_delete(directory, entry->d_name,
     231        1729 :                                 delfiles, name_max))==RECDEL_ERROR)
     232             :                                         goto end;
     233             :                         // do not overwrite ret with OK if it previously
     234             :                         // had ENTRIES_REMAINING
     235        1729 :                         if(r==RECDEL_ENTRIES_REMAINING) ret=r;
     236             :                 }
     237        2112 :                 else if(delfiles)
     238             :                 {
     239        2112 :                         if(unlink(fullpath))
     240             :                         {
     241             :                                 logp("unlink %s: %s\n",
     242           0 :                                         fullpath, strerror(errno));
     243           0 :                                 ret=RECDEL_ENTRIES_REMAINING;
     244             :                         }
     245             :                 }
     246             :                 else
     247             :                 {
     248             :                         ret=RECDEL_ENTRIES_REMAINING;
     249             :                 }
     250             :         }
     251             : 
     252        4568 :         if(ret==RECDEL_OK && rmdir(directory))
     253             :         {
     254           0 :                 logp("rmdir %s: %s\n", directory, strerror(errno));
     255           0 :                 ret=RECDEL_ERROR;
     256             :         }
     257             : end:
     258        3284 :         if(dirp) closedir(dirp);
     259        3284 :         free_w(&fullpath);
     260        3284 :         free_w(&directory);
     261        3284 :         free_v((void **)&entry);
     262        3284 :         return ret;
     263             : }
     264             : 
     265        1555 : static int do_recursive_delete_w(const char *path, uint8_t delfiles)
     266             : {
     267             :         int32_t name_max;
     268        1555 :         get_max(&name_max, _PC_NAME_MAX);
     269        1555 :         return do_recursive_delete(path, NULL, delfiles, name_max);
     270             : }
     271             : 
     272        1555 : int recursive_delete(const char *path)
     273             : {
     274             :         struct stat statp;
     275             :         // We might have been given a file entry, instead of a directory.
     276        1555 :         if(!lstat(path, &statp) && !S_ISDIR(statp.st_mode))
     277             :         {
     278         152 :                 if(unlink(path))
     279             :                 {
     280           0 :                         logp("unlink %s: %s\n", path, strerror(errno));
     281           0 :                         return RECDEL_ENTRIES_REMAINING;
     282             :                 }
     283             :         }
     284        1555 :         return do_recursive_delete_w(path, 1);
     285             : }
     286             : 
     287           0 : int recursive_delete_dirs_only(const char *path)
     288             : {
     289           0 :         return do_recursive_delete_w(path, 0);
     290             : }
     291             : 
     292           0 : int unlink_w(const char *path, const char *func)
     293             : {
     294           0 :         if(unlink(path))
     295             :         {
     296             :                 logp("unlink(%s) called from %s(): %s\n",
     297           0 :                         path, func, strerror(errno));
     298           0 :                 return -1;
     299             :         }
     300             :         return 0;
     301             : }
     302             : 
     303             : static void init_max(const char *path,
     304             :         uint32_t *max, int what, uint32_t default_max)
     305             : {
     306          88 :         *max=pathconf(path?path:".", what);
     307          88 :         if(*max<default_max) *max=default_max;
     308             : }
     309             : 
     310          44 : int init_fs_max(const char *path)
     311             : {
     312             :         struct stat statp;
     313          44 :         if(stat(path, &statp))
     314             :         {
     315           0 :                 logp("Path %s does not exist in %s\n", path, __func__);
     316           0 :                 return -1;
     317             :         }
     318             :         // Get system path and filename maximum lengths.
     319             :         init_max(path, &fs_path_max, _PC_PATH_MAX, 1024);
     320             :         init_max(path, &fs_name_max, _PC_NAME_MAX, 255);
     321          44 :         fs_full_path_max=fs_path_max+fs_name_max;
     322          44 :         return 0;
     323             : }
     324             : 
     325          89 : int looks_like_tmp_or_hidden_file(const char *filename)
     326             : {
     327          89 :         if(!filename) return 0;
     328          89 :         if(filename[0]=='.' // Also avoids '.' and '..'.
     329             :           // I am told that emacs tmp files end with '~'.
     330          88 :           || filename[strlen(filename)-1]=='~')
     331             :                 return 1;
     332          87 :         return 0;
     333             : }
     334             : 
     335         325 : int do_get_entries_in_directory(DIR *directory, struct dirent ***nl,
     336             :         int *count, int (*compar)(const void *, const void *))
     337             : {
     338             :         int status;
     339         325 :         int allocated=0;
     340         325 :         struct dirent **ntmp=NULL;
     341         325 :         struct dirent *entry=NULL;
     342         325 :         struct dirent *result=NULL;
     343             : 
     344         325 :         *count=0;
     345             : 
     346             :         // This here is doing a funky kind of scandir/alphasort
     347             :         // that can also run on Windows.
     348             :         while(1)
     349             :         {
     350             :                 char *p;
     351        2648 :                 if(!(entry=(struct dirent *)malloc_w(
     352        2648 :                   sizeof(struct dirent)+fs_name_max+100, __func__)))
     353             :                         goto error;
     354        2648 :                 status=readdir_r(directory, entry, &result);
     355        2648 :                 if(status || !result)
     356             :                 {
     357         325 :                         free_v((void **)&entry);
     358             :                         break;
     359             :                 }
     360             : 
     361        2323 :                 p=entry->d_name;
     362             :                 ASSERT(fs_name_max+1 > (int)sizeof(struct dirent)+strlen(p));
     363             : 
     364        2323 :                 if(!p
     365        2323 :                   || !strcmp(p, ".")
     366        1998 :                   || !strcmp(p, ".."))
     367             :                 {
     368         650 :                         free_v((void **)&entry);
     369         650 :                         continue;
     370             :                 }
     371             : 
     372        1673 :                 if(*count==allocated)
     373             :                 {
     374         331 :                         if(!allocated) allocated=10;
     375          16 :                         else allocated*=2;
     376             : 
     377         331 :                         if(!(ntmp=(struct dirent **)
     378         331 :                           realloc_w(*nl, allocated*sizeof(**nl), __func__)))
     379             :                                 goto error;
     380         331 :                         *nl=ntmp;
     381             :                 }
     382        1673 :                 (*nl)[(*count)++]=entry;
     383             :         }
     384         640 :         if(*nl && compar) qsort(*nl, *count, sizeof(**nl), compar);
     385             :         return 0;
     386             : error:
     387           0 :         free_v((void **)&entry);
     388           0 :         if(*nl)
     389             :         {
     390             :                 int i;
     391           0 :                 for(i=0; i<*count; i++)
     392           0 :                         free_v((void **)&((*nl)[i]));
     393           0 :                 free_v((void **)nl);
     394             :         }
     395             :         return -1;
     396             : }
     397             : 
     398         326 : static int entries_in_directory(const char *path, struct dirent ***nl,
     399             :         int *count, int atime,
     400             :         int (*compar)(const struct dirent **, const struct dirent **))
     401             : {
     402         326 :         int ret=0;
     403         326 :         DIR *directory=NULL;
     404             : 
     405         326 :         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          39 :                 if(init_fs_max(path)) return -1;
     411             :         }
     412             : #if defined(O_DIRECTORY) && defined(O_NOATIME)
     413         326 :         int dfd=-1;
     414         326 :         if((dfd=open(path, O_RDONLY|O_DIRECTORY|atime?0:O_NOATIME))<0
     415         326 :           || !(directory=fdopendir(dfd)))
     416             : #else
     417             : // Mac OS X appears to have no O_NOATIME and no fdopendir(), so it should
     418             : // end up using opendir() here.
     419             :         if(!(directory=opendir(path)))
     420             : #endif
     421             :         {
     422             : #if defined(O_DIRECTORY) && defined(O_NOATIME)
     423             :                 close_fd(&dfd);
     424             : #endif
     425             :                 ret=1;
     426             :         }
     427             :         else
     428             :         {
     429         325 :                 if(do_get_entries_in_directory(directory, nl, count,
     430         325 :                         (int (*)(const void *, const void *))compar))
     431           0 :                                 ret=-1;
     432             :         }
     433         326 :         if(directory) closedir(directory);
     434             :         return ret;
     435             : }
     436             : 
     437          76 : static int my_alphasort(const struct dirent **a, const struct dirent **b)
     438             : {
     439          76 :         return strcmp((*a)->d_name, (*b)->d_name);
     440             : }
     441             : 
     442        2730 : static int rev_alphasort(const struct dirent **a, const struct dirent **b)
     443             : {
     444        2730 :         return strcmp((*a)->d_name, (*b)->d_name)*-1;
     445             : }
     446             : 
     447          23 : int entries_in_directory_alphasort(const char *path, struct dirent ***nl,
     448             :         int *count, int atime)
     449             : {
     450          23 :         return entries_in_directory(path, nl, count, atime, my_alphasort);
     451             : }
     452             : 
     453         278 : int entries_in_directory_alphasort_rev(const char *path, struct dirent ***nl,
     454             :         int *count, int atime)
     455             : {
     456         278 :         return entries_in_directory(path, nl, count, atime, rev_alphasort);
     457             : }
     458             : 
     459          25 : int entries_in_directory_no_sort(const char *path, struct dirent ***nl,
     460             :         int *count, int atime)
     461             : {
     462          25 :         return entries_in_directory(path, nl, count, atime, NULL);
     463             : }

Generated by: LCOV version 1.10