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