LCOV - code coverage report
Current view: top level - src - fsops.c (source / functions) Hit Total Coverage
Test: burp-coverage-clean.info Lines: 160 206 77.7 %
Date: 2016-07-02 Functions: 25 30 83.3 %

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

Generated by: LCOV version 1.10