LCOV - code coverage report
Current view: top level - src - fsops.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 142 179 79.3 %
Date: 2016-02-29 Functions: 20 23 87.0 %

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

Generated by: LCOV version 1.10