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