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 372 : void close_fd(int *fd)
18 : {
19 372 : if(!fd || *fd<0) return;
20 : //logp("closing %d\n", *fd);
21 9 : close(*fd);
22 9 : *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 108 : int is_reg_lstat(const char *path)
34 : {
35 : struct stat buf;
36 108 : if(lstat(path, &buf))
37 : return -1;
38 8 : return S_ISREG(buf.st_mode);
39 : }
40 :
41 5574 : int is_dir(const char *path, struct dirent *d)
42 : {
43 : #ifdef _DIRENT_HAVE_D_TYPE
44 : // Faster evaluation on most systems.
45 5574 : switch(d->d_type)
46 : {
47 : case DT_DIR:
48 : return 1;
49 : case DT_UNKNOWN:
50 : break;
51 : default:
52 2838 : return 0;
53 : }
54 : #endif
55 0 : return is_dir_lstat(path);
56 : }
57 :
58 5176 : int mkpath(char **rpath, const char *limit)
59 : {
60 5176 : int ret=-1;
61 5176 : char *cp=NULL;
62 : struct stat buf;
63 :
64 5176 : if((cp=strrchr(*rpath, '/')))
65 : {
66 4192 : *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 4192 : if(!**rpath)
74 : {
75 : // We are down to the root, which is OK.
76 : }
77 8384 : else if(lstat(*rpath, &buf))
78 : {
79 : // does not exist - recurse further down, then come
80 : // back and try to mkdir it.
81 2220 : 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 2217 : if(limit && pathcmp(*rpath, limit)<0)
100 : {
101 1 : logp("will not mkdir %s\n", *rpath);
102 1 : goto end;
103 : }
104 2216 : if(mkdir(*rpath, 0777))
105 : {
106 0 : logp("could not mkdir %s: %s\n", *rpath, strerror(errno));
107 0 : goto end;
108 : }
109 : }
110 1972 : else if(S_ISDIR(buf.st_mode))
111 : {
112 : // Is a directory - can put the slash back and return.
113 : }
114 39 : 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 0 : logp("warning: wanted '%s' to be a directory\n",
122 : *rpath);
123 : }
124 : }
125 :
126 : ret=0;
127 : end:
128 5176 : if(cp) *cp='/';
129 5176 : return ret;
130 : }
131 :
132 2851 : int build_path(const char *datadir, const char *fname, char **rpath, const char *limit)
133 : {
134 2851 : if(!(*rpath=prepend_s(datadir, fname))) return -1;
135 2851 : if(mkpath(rpath, limit))
136 : {
137 0 : free_w(rpath);
138 0 : return -1;
139 : }
140 : return 0;
141 : }
142 :
143 330 : 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 330 : if(rename(oldpath, newpath))
148 : {
149 0 : logp("could not rename '%s' to '%s': %s\n",
150 0 : oldpath, newpath, strerror(errno));
151 0 : return -1;
152 : }
153 : return 0;
154 : }
155 :
156 2823 : int build_path_w(const char *path)
157 : {
158 : int ret;
159 2823 : char *rpath=NULL;
160 2823 : ret=build_path(path, "", &rpath, NULL);
161 2823 : free_w(&rpath);
162 2823 : return ret;
163 : }
164 :
165 : #define RECDEL_ERROR -1
166 : #define RECDEL_OK 0
167 : #define RECDEL_ENTRIES_REMAINING 1
168 :
169 : static void get_max(int32_t *max, int32_t default_max)
170 : {
171 2444 : *max = pathconf(".", default_max);
172 2444 : if(*max < 1024) *max = 1024;
173 : // Add for EOS.
174 2444 : (*max)++;
175 : }
176 :
177 5116 : 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 5116 : int ret=RECDEL_ERROR;
182 5116 : DIR *dirp=NULL;
183 5116 : struct dirent *entry=NULL;
184 : struct stat statp;
185 5116 : char *directory=NULL;
186 5116 : char *fullpath=NULL;
187 :
188 5116 : if(!file)
189 : {
190 2444 : if(!(directory=prepend_s(d, "")))
191 : goto end;
192 : }
193 2672 : else if(!(directory=prepend_s(d, file)))
194 : {
195 0 : log_out_of_memory(__func__);
196 0 : goto end;
197 : }
198 :
199 10232 : if(lstat(directory, &statp))
200 : {
201 : // path does not exist.
202 : ret=RECDEL_OK;
203 : goto end;
204 : }
205 :
206 3540 : if(!(dirp=opendir(directory)))
207 : {
208 0 : logp("opendir %s in %s: %s\n",
209 0 : directory, __func__, strerror(errno));
210 0 : goto end;
211 : }
212 :
213 : while(1)
214 : {
215 16090 : errno=0;
216 16090 : if(!(entry=readdir(dirp)))
217 : {
218 3540 : if(errno)
219 : {
220 0 : logp("error in readdir in %s: %s\n",
221 : __func__, strerror(errno));
222 0 : goto end;
223 : }
224 : // Got to the end of the directory.
225 3540 : ret=RECDEL_OK;
226 : break;
227 : }
228 :
229 12550 : if(!filter_dot(entry))
230 7080 : continue;
231 5470 : free_w(&fullpath);
232 5470 : if(!(fullpath=prepend_s(directory, entry->d_name)))
233 : goto end;
234 :
235 5470 : if(is_dir(fullpath, entry)>0)
236 : {
237 : int r;
238 2672 : 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 2672 : if(r==RECDEL_ENTRIES_REMAINING) ret=r;
245 : }
246 2798 : else if(delfiles)
247 : {
248 2798 : if(unlink(fullpath))
249 : {
250 0 : logp("unlink %s: %s\n",
251 : fullpath, strerror(errno));
252 0 : ret=RECDEL_ENTRIES_REMAINING;
253 : }
254 : }
255 : else
256 : {
257 : ret=RECDEL_ENTRIES_REMAINING;
258 : }
259 : }
260 :
261 3540 : if(ret==RECDEL_OK && rmdir(directory))
262 : {
263 0 : if(errno!=ENOTEMPTY || !ignore_not_empty_errors)
264 : {
265 0 : logp("rmdir %s: %s\n", directory, strerror(errno));
266 0 : ret=RECDEL_ERROR;
267 : }
268 : }
269 : end:
270 5116 : if(dirp) closedir(dirp);
271 5116 : free_w(&fullpath);
272 5116 : free_w(&directory);
273 5116 : return ret;
274 : }
275 :
276 2444 : 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 2444 : get_max(&name_max, _PC_NAME_MAX);
281 2444 : return do_recursive_delete(path,
282 : NULL, delfiles, name_max, ignore_not_empty_errors);
283 : }
284 :
285 2438 : 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 2438 : if(!lstat(path, &statp) && !S_ISDIR(statp.st_mode))
290 : {
291 156 : if(unlink(path))
292 : {
293 0 : logp("unlink %s: %s\n", path, strerror(errno));
294 0 : return RECDEL_ENTRIES_REMAINING;
295 : }
296 : }
297 2438 : return do_recursive_delete_w(path, 1, 0/*ignore_not_empty_errors*/);
298 : }
299 :
300 2 : int recursive_delete_dirs_only(const char *path)
301 : {
302 2 : return do_recursive_delete_w(path, 0, 0/*ignore_not_empty_errors*/);
303 : }
304 :
305 4 : int recursive_delete_dirs_only_no_warnings(const char *path)
306 : {
307 4 : return do_recursive_delete_w(path, 0, 1/*ignore_not_empty_errors*/);
308 : }
309 :
310 0 : int unlink_w(const char *path, const char *func)
311 : {
312 0 : if(unlink(path))
313 : {
314 0 : logp("unlink(%s) called from %s(): %s\n",
315 0 : path, func, strerror(errno));
316 0 : return -1;
317 : }
318 : return 0;
319 : }
320 :
321 : static void init_max(const char *path,
322 : uint32_t *max, int what, uint32_t default_max)
323 : {
324 16 : *max=pathconf(path?path:".", what);
325 16 : if(*max<default_max) *max=default_max;
326 : }
327 :
328 8 : int init_fs_max(const char *path)
329 : {
330 : struct stat statp;
331 8 : if(stat(path, &statp))
332 : {
333 0 : logp("Path %s does not exist in %s\n", path, __func__);
334 0 : return -1;
335 : }
336 : // Get system path and filename maximum lengths.
337 8 : init_max(path, &fs_path_max, _PC_PATH_MAX, 1024);
338 8 : init_max(path, &fs_name_max, _PC_NAME_MAX, 255);
339 8 : fs_full_path_max=fs_path_max+fs_name_max;
340 8 : return 0;
341 : }
342 :
343 27 : static int do_get_entries_in_directory(DIR *directory, char ***nl,
344 : int *count, int (*compar)(const void *, const void *))
345 : {
346 27 : int allocated=0;
347 27 : char **ntmp=NULL;
348 27 : struct dirent *result=NULL;
349 :
350 27 : *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 164 : errno=0;
357 164 : if(!(result=readdir(directory)))
358 : {
359 27 : if(errno)
360 : {
361 0 : logp("error in readdir: %s\n",
362 : strerror(errno));
363 0 : goto error;
364 : }
365 : break;
366 : }
367 :
368 137 : if(!filter_dot(result))
369 54 : continue;
370 :
371 83 : if(*count==allocated)
372 : {
373 21 : if(!allocated) allocated=10;
374 0 : else allocated*=2;
375 :
376 21 : if(!(ntmp=(char **)
377 21 : realloc_w(*nl, allocated*sizeof(**nl), __func__)))
378 : goto error;
379 21 : *nl=ntmp;
380 : }
381 83 : if(!((*nl)[(*count)++]=strdup_w(result->d_name, __func__)))
382 : goto error;
383 : }
384 27 : if(*nl && compar)
385 21 : qsort(*nl, *count, sizeof(**nl), compar);
386 : return 0;
387 : error:
388 0 : if(*nl)
389 : {
390 : int i;
391 0 : for(i=0; i<*count; i++)
392 0 : free_w(&((*nl)[i]));
393 0 : free_v((void **)nl);
394 : }
395 : return -1;
396 : }
397 :
398 27 : 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 27 : int ret=0;
403 27 : DIR *directory=NULL;
404 :
405 27 : 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 3 : if(init_fs_max(path)) return -1;
411 : }
412 : #if defined(O_DIRECTORY) && defined(O_NOATIME)
413 27 : int dfd=-1;
414 54 : if((dfd=open(path, O_RDONLY|O_DIRECTORY|(atime?0:O_NOATIME)
415 : #ifdef O_NOFOLLOW
416 27 : |(follow_symlinks?0:O_NOFOLLOW)
417 : #endif
418 : ))<0
419 27 : || !(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 27 : if(do_get_entries_in_directory(directory, nl, count,
434 : (int (*)(const void *, const void *))compar))
435 0 : ret=-1;
436 : }
437 27 : if(directory) closedir(directory);
438 : return ret;
439 : }
440 :
441 15102 : int filter_dot(const struct dirent *d)
442 : {
443 15102 : if(!d
444 15102 : || !strcmp(d->d_name, ".")
445 11178 : || !strcmp(d->d_name, ".."))
446 : return 0;
447 7254 : return 1;
448 : }
449 :
450 113 : static int my_alphasort(const char **a, const char **b)
451 : {
452 113 : return pathcmp(*a, *b);
453 : }
454 :
455 27 : int entries_in_directory_alphasort(const char *path, char ***nl,
456 : int *count, int atime, int follow_symlinks)
457 : {
458 27 : return entries_in_directory(path, nl, count, atime, follow_symlinks,
459 : my_alphasort);
460 : }
461 :
462 : #define FULL_CHUNK 4096
463 :
464 13 : int files_equal(const char *opath, const char *npath, int compressed)
465 : {
466 13 : int ret=0;
467 : size_t ogot;
468 : size_t ngot;
469 13 : unsigned int i=0;
470 13 : struct fzp *ofp=NULL;
471 13 : struct fzp *nfp=NULL;
472 : static char obuf[FULL_CHUNK];
473 : static char nbuf[FULL_CHUNK];
474 :
475 13 : if(compressed)
476 : {
477 5 : ofp=fzp_gzopen(opath, "rb");
478 5 : nfp=fzp_gzopen(npath, "rb");
479 : }
480 : else
481 : {
482 8 : ofp=fzp_open(opath, "rb");
483 8 : nfp=fzp_open(npath, "rb");
484 : }
485 :
486 13 : if(!ofp && !nfp)
487 : {
488 : ret=1;
489 : goto end;
490 : }
491 13 : if(!ofp && nfp)
492 : goto end;
493 13 : if(!nfp && ofp)
494 : goto end;
495 :
496 : while(1)
497 : {
498 13 : ogot=fzp_read(ofp, obuf, FULL_CHUNK);
499 13 : ngot=fzp_read(nfp, nbuf, FULL_CHUNK);
500 13 : if(ogot!=ngot)
501 : goto end;
502 9234 : for(i=0; i<ogot; i++)
503 : {
504 9234 : if(obuf[i]!=nbuf[i])
505 : goto end;
506 : }
507 13 : if(ogot<FULL_CHUNK)
508 : break;
509 : }
510 : ret=1;
511 : end:
512 13 : fzp_close(&ofp);
513 13 : fzp_close(&nfp);
514 13 : return ret;
515 : }
516 :
517 : #ifndef HAVE_WIN32
518 1 : int mksock(const char *path)
519 : {
520 1 : int fd=-1;
521 1 : int ret=-1;
522 : struct sockaddr_un addr;
523 1 : memset(&addr, 0, sizeof(addr));
524 1 : addr.sun_family=AF_UNIX;
525 1 : strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
526 1 : if((fd=socket(addr.sun_family, SOCK_STREAM, 0))<0
527 1 : || (bind(fd, (struct sockaddr *)&addr, sizeof(addr)))<0)
528 : goto end;
529 1 : ret=0;
530 : end:
531 1 : if(fd>=0) close(fd);
532 1 : return ret;
533 : }
534 :
535 63 : int is_lnk_lstat(const char *path)
536 : {
537 : struct stat buf;
538 63 : if(lstat(path, &buf))
539 : return -1;
540 0 : return S_ISLNK(buf.st_mode);
541 : }
542 :
543 0 : int is_lnk_valid(const char *path)
544 : {
545 : struct stat buf;
546 0 : if(stat(path, &buf))
547 : return 0;
548 0 : return 1;
549 : }
550 :
551 6 : int do_symlink(const char *oldpath, const char *newpath)
552 : {
553 6 : if(!symlink(oldpath, newpath))
554 : return 0;
555 0 : logp("could not symlink '%s' to '%s': %s\n",
556 0 : newpath, oldpath, strerror(errno));
557 0 : return -1;
558 : }
559 :
560 : static int do_readlink(const char *path, char buf[], size_t buflen)
561 : {
562 : ssize_t len;
563 309 : if((len=readlink(path, buf, buflen-1))<0)
564 : return -1;
565 309 : buf[len]='\0';
566 : return 0;
567 : }
568 :
569 881 : int readlink_w(const char *path, char buf[], size_t buflen)
570 : {
571 : struct stat statp;
572 881 : if(lstat(path, &statp))
573 : return -1;
574 309 : if(S_ISLNK(statp.st_mode))
575 : return do_readlink(path, buf, buflen);
576 : return -1;
577 : }
578 :
579 872 : int readlink_w_in_dir(const char *dir, const char *lnk,
580 : char buf[], size_t buflen)
581 : {
582 872 : char *tmp=NULL;
583 872 : if(!(tmp=prepend_s(dir, lnk)))
584 : return -1;
585 872 : readlink_w(tmp, buf, buflen);
586 872 : free_w(&tmp);
587 872 : return 0;
588 : }
589 :
590 : #endif
|