LCOV - code coverage report
Current view: top level - src - fsops.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 139 195 71.3 %
Date: 2015-10-31 Functions: 18 23 78.3 %

          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          11 : void close_fd(int *fd)
      13             : {
      14          22 :         if(!fd || *fd<0) return;
      15             :         //logp("closing %d\n", *fd);
      16          11 :         close(*fd);
      17          11 :         *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        2254 : int is_dir(const char *path, struct dirent *d)
      30             : {
      31             : #ifdef _DIRENT_HAVE_D_TYPE
      32             :         // Faster evaluation on most systems.
      33        2254 :         switch(d->d_type)
      34             :         {
      35             :                 case DT_DIR:
      36         955 :                         return 1;
      37             :                 case DT_UNKNOWN:
      38           0 :                         break;
      39             :                 default:
      40        1299 :                         return 0;
      41             :         }
      42             : #endif
      43           0 :         return is_dir_lstat(path);
      44             : }
      45             : 
      46        2603 : int mkpath(char **rpath, const char *limit)
      47             : {
      48        2603 :         int ret=-1;
      49        2603 :         char *cp=NULL;
      50             :         struct stat buf;
      51             : #ifdef HAVE_WIN32
      52             :         int windows_stupidity=0;
      53             : #endif
      54        2603 :         if((cp=strrchr(*rpath, '/')))
      55             :         {
      56        2011 :                 *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        2011 :                 if(!**rpath)
      65             :                 {
      66             :                         // We are down to the root, which is OK.
      67             :                 }
      68        2011 :                 else if(lstat(*rpath, &buf))
      69             :                 {
      70             :                         // does not exist - recurse further down, then come
      71             :                         // back and try to mkdir it.
      72         655 :                         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         655 :                         if(limit && pathcmp(*rpath, limit)<0)
      91             :                         {
      92           0 :                                 logp("will not mkdir %s\n", *rpath);
      93           0 :                                 goto end;
      94             :                         }
      95         655 :                         if(mkdir(*rpath, 0777))
      96             :                         {
      97           0 :                                 logp("could not mkdir %s: %s\n", *rpath, strerror(errno));
      98           0 :                                 goto end;
      99             :                         }
     100             :                 }
     101        1356 :                 else if(S_ISDIR(buf.st_mode))
     102             :                 {
     103             :                         // Is a directory - can put the slash back and return.
     104             :                 }
     105           0 :                 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        2603 :         ret=0;
     118             : end:
     119             : #ifdef HAVE_WIN32
     120             :         if(windows_stupidity) (*rpath)[1]=':';
     121             : #endif
     122        2603 :         if(cp) *cp='/';
     123        2603 :         return ret;
     124             : }
     125             : 
     126        1948 : 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        1948 :         if(!(*rpath=prepend_s(datadir, fname))) return -1;
     130        1948 :         if(mkpath(rpath, limit))
     131             :         {
     132           0 :                 free_w(rpath);
     133           0 :                 return -1;
     134             :         }
     135        1948 :         return 0;
     136             : }
     137             : 
     138         110 : 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         110 :         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         110 :         return 0;
     149             : }
     150             : 
     151        1948 : int build_path_w(const char *path)
     152             : {
     153             :         int ret;
     154        1948 :         char *rpath=NULL;
     155        1948 :         ret=build_path(path, "", &rpath, NULL);
     156        1948 :         free_w(&rpath);
     157        1948 :         return ret;
     158             : }
     159             : 
     160             : #define RECDEL_ERROR                    -1
     161             : #define RECDEL_OK                       0
     162             : #define RECDEL_ENTRIES_REMAINING        1
     163             : 
     164        1016 : static void get_max(int32_t *max, int32_t default_max)
     165             : {
     166        1016 :         *max = pathconf(".", default_max);
     167        1016 :         if(*max < 1024) *max = 1024;
     168             :         // Add for EOS.
     169        1016 :         (*max)++;
     170        1016 : }
     171             : 
     172        1971 : static int do_recursive_delete(const char *d, const char *file,
     173             :         uint8_t delfiles, int32_t name_max)
     174             : {
     175        1971 :         int ret=RECDEL_ERROR;
     176        1971 :         DIR *dirp=NULL;
     177        1971 :         struct dirent *entry=NULL;
     178             :         struct dirent *result;
     179             :         struct stat statp;
     180        1971 :         char *directory=NULL;
     181        1971 :         char *fullpath=NULL;
     182             : 
     183        1971 :         if(!file)
     184             :         {
     185        1016 :                 if(!(directory=prepend_s(d, ""))) goto end;
     186             :         }
     187         955 :         else if(!(directory=prepend_s(d, file)))
     188             :         {
     189           0 :                 log_out_of_memory(__func__);
     190           0 :                 goto end;
     191             :         }
     192             : 
     193        1971 :         if(lstat(directory, &statp))
     194             :         {
     195             :                 // path does not exist.
     196         664 :                 ret=RECDEL_OK;
     197         664 :                 goto end;
     198             :         }
     199             : 
     200        1307 :         if(!(dirp=opendir(directory)))
     201             :         {
     202           0 :                 logp("opendir %s: %s\n", directory, strerror(errno));
     203           0 :                 goto end;
     204             :         }
     205             : 
     206        2614 :         if(!(entry=(struct dirent *)
     207        2614 :                 malloc_w(sizeof(struct dirent)+name_max+100, __func__)))
     208           0 :                         goto end;
     209             : 
     210             :         while(1)
     211             :         {
     212        6175 :                 if(readdir_r(dirp, entry, &result) || !result)
     213             :                 {
     214             :                         // Got to the end of the directory.
     215        1307 :                         ret=RECDEL_OK;
     216        1307 :                         break;
     217             :                 }
     218             : 
     219        4868 :                 if(!entry->d_ino
     220        4868 :                   || !strcmp(entry->d_name, ".")
     221        3561 :                   || !strcmp(entry->d_name, ".."))
     222        2614 :                         continue;
     223        2254 :                 free_w(&fullpath);
     224        2254 :                 if(!(fullpath=prepend_s(directory, entry->d_name)))
     225           0 :                         goto end;
     226             : 
     227        2254 :                 if(is_dir(fullpath, entry)>0)
     228             :                 {
     229             :                         int r;
     230         955 :                         if((r=do_recursive_delete(directory, entry->d_name,
     231         955 :                                 delfiles, name_max))==RECDEL_ERROR)
     232           0 :                                         goto end;
     233             :                         // do not overwrite ret with OK if it previously
     234             :                         // had ENTRIES_REMAINING
     235         955 :                         if(r==RECDEL_ENTRIES_REMAINING) ret=r;
     236             :                 }
     237        1299 :                 else if(delfiles)
     238             :                 {
     239        1299 :                         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           0 :                         ret=RECDEL_ENTRIES_REMAINING;
     249             :                 }
     250             :         }
     251             : 
     252        1307 :         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        1971 :         if(dirp) closedir(dirp);
     259        1971 :         free_w(&fullpath);
     260        1971 :         free_w(&directory);
     261        1971 :         free_v((void **)&entry);
     262        6839 :         return ret;
     263             : }
     264             : 
     265        1016 : static int do_recursive_delete_w(const char *path, uint8_t delfiles)
     266             : {
     267             :         int32_t name_max;
     268        1016 :         get_max(&name_max, _PC_NAME_MAX);
     269        1016 :         return do_recursive_delete(path, NULL, delfiles, name_max);
     270             : }
     271             : 
     272        1016 : 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        1016 :         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        1016 :         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           0 :         return 0;
     301             : }
     302             : 
     303          24 : static void init_max(const char *path,
     304             :         uint32_t *max, int what, uint32_t default_max)
     305             : {
     306          24 :         *max=pathconf(path?path:".", what);
     307          24 :         if(*max<default_max) *max=default_max;
     308          24 : }
     309             : 
     310          12 : int init_fs_max(const char *path)
     311             : {
     312             :         struct stat statp;
     313          12 :         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          12 :         init_max(path, &fs_path_max, _PC_PATH_MAX, 1024);
     320          12 :         init_max(path, &fs_name_max, _PC_NAME_MAX, 255);
     321          12 :         fs_full_path_max=fs_path_max+fs_name_max;
     322          12 :         return 0;
     323             : }
     324             : 
     325           0 : int looks_like_tmp_or_hidden_file(const char *filename)
     326             : {
     327           0 :         if(!filename) return 0;
     328           0 :         if(filename[0]=='.' // Also avoids '.' and '..'.
     329             :           // I am told that emacs tmp files end with '~'.
     330           0 :           || filename[strlen(filename)-1]=='~')
     331           0 :                 return 1;
     332           0 :         return 0;
     333             : }
     334             : 
     335         191 : 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         191 :         int allocated=0;
     340         191 :         struct dirent **ntmp=NULL;
     341         191 :         struct dirent *entry=NULL;
     342         191 :         struct dirent *result=NULL;
     343             : 
     344             :         // This here is doing a funky kind of scandir/alphasort
     345             :         // that can also run on Windows.
     346             :         while(1)
     347             :         {
     348             :                 char *p;
     349        3496 :                 if(!(entry=(struct dirent *)malloc_w(
     350        3496 :                   sizeof(struct dirent)+fs_name_max+100, __func__)))
     351           0 :                         goto error;
     352        1748 :                 status=readdir_r(directory, entry, &result);
     353        1748 :                 if(status || !result)
     354             :                 {
     355         191 :                         free_v((void **)&entry);
     356         191 :                         break;
     357             :                 }
     358             : 
     359        1557 :                 p=entry->d_name;
     360             :                 ASSERT(fs_name_max+1 > (int)sizeof(struct dirent)+strlen(p));
     361             : 
     362        1557 :                 if(!p
     363        1557 :                   || !strcmp(p, ".")
     364        1366 :                   || !strcmp(p, ".."))
     365             :                 {
     366         382 :                         free_v((void **)&entry);
     367         382 :                         continue;
     368             :                 }
     369             : 
     370        1175 :                 if(*count==allocated)
     371             :                 {
     372         199 :                         if(!allocated) allocated=10;
     373          16 :                         else allocated*=2;
     374             : 
     375         199 :                         if(!(ntmp=(struct dirent **)
     376         199 :                           realloc_w(*nl, allocated*sizeof(**nl), __func__)))
     377           0 :                                 goto error;
     378         199 :                         *nl=ntmp;
     379             :                 }
     380        1175 :                 (*nl)[(*count)++]=entry;
     381             :         }
     382         191 :         if(*nl && compar) qsort(*nl, *count, sizeof(**nl), compar);
     383         191 :         return 0;
     384             : error:
     385           0 :         free_v((void **)&entry);
     386           0 :         if(*nl)
     387             :         {
     388             :                 int i;
     389           0 :                 for(i=0; i<*count; i++)
     390           0 :                         free_v((void **)&((*nl)[i]));
     391           0 :                 free_v((void **)nl);
     392             :         }
     393        1557 :         return -1;
     394             : }
     395             : 
     396         191 : static int entries_in_directory(const char *path, struct dirent ***nl,
     397             :         int *count, int atime,
     398             :         int (*compar)(const struct dirent **, const struct dirent **))
     399             : {
     400         191 :         int ret=0;
     401         191 :         DIR *directory=NULL;
     402             : 
     403         191 :         if(!fs_name_max)
     404             :         {
     405             :                 // Get system path and filename maximum lengths.
     406             :                 // FIX THIS: maybe this should be done every time a file system
     407             :                 // is crossed?
     408          12 :                 if(init_fs_max(path)) return -1;
     409             :         }
     410             : #if defined(O_DIRECTORY) && defined(O_NOATIME)
     411         191 :         int dfd=-1;
     412         573 :         if((dfd=open(path, O_RDONLY|O_DIRECTORY|atime?0:O_NOATIME))<0
     413         382 :           || !(directory=fdopendir(dfd)))
     414             : #else
     415             : // Mac OS X appears to have no O_NOATIME and no fdopendir(), so it should
     416             : // end up using opendir() here.
     417             :         if(!(directory=opendir(path)))
     418             : #endif
     419             :         {
     420             : #if defined(O_DIRECTORY) && defined(O_NOATIME)
     421           0 :                 close_fd(&dfd);
     422             : #endif
     423           0 :                 ret=1;
     424             :         }
     425             :         else
     426             :         {
     427         191 :                 if(do_get_entries_in_directory(directory, nl, count,
     428         191 :                         (int (*)(const void *, const void *))compar))
     429           0 :                                 ret=-1;
     430             :         }
     431         191 :         if(directory) closedir(directory);
     432         191 :         return ret;
     433             : }
     434             : 
     435          76 : static int my_alphasort(const struct dirent **a, const struct dirent **b)
     436             : {
     437          76 :         return strcmp((*a)->d_name, (*b)->d_name);
     438             : }
     439             : 
     440        2202 : static int rev_alphasort(const struct dirent **a, const struct dirent **b)
     441             : {
     442        2202 :         return strcmp((*a)->d_name, (*b)->d_name)*-1;
     443             : }
     444             : 
     445          23 : int entries_in_directory_alphasort(const char *path, struct dirent ***nl,
     446             :         int *count, int atime)
     447             : {
     448          23 :         return entries_in_directory(path, nl, count, atime, my_alphasort);
     449             : }
     450             : 
     451         168 : int entries_in_directory_alphasort_rev(const char *path, struct dirent ***nl,
     452             :         int *count, int atime)
     453             : {
     454         168 :         return entries_in_directory(path, nl, count, atime, rev_alphasort);
     455             : }
     456             : 
     457           0 : int entries_in_directory_no_sort(const char *path, struct dirent ***nl,
     458             :         int *count, int atime)
     459             : {
     460           0 :         return entries_in_directory(path, nl, count, atime, NULL);
     461             : }

Generated by: LCOV version 1.10