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 348 : void close_fd(int *fd)
18 : {
19 696 : if(!fd || *fd<0) return;
20 : //logp("closing %d\n", *fd);
21 348 : close(*fd);
22 348 : *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 70 : int is_reg_lstat(const char *path)
34 : {
35 : struct stat buf;
36 70 : if(lstat(path, &buf))
37 : return -1;
38 4 : return S_ISREG(buf.st_mode);
39 : }
40 :
41 5271 : int is_dir(const char *path, struct dirent *d)
42 : {
43 : #ifdef _DIRENT_HAVE_D_TYPE
44 : // Faster evaluation on most systems.
45 5271 : switch(d->d_type)
46 : {
47 : case DT_DIR:
48 : return 1;
49 : case DT_UNKNOWN:
50 : break;
51 : default:
52 2654 : return 0;
53 : }
54 : #endif
55 0 : return is_dir_lstat(path);
56 : }
57 :
58 4845 : int mkpath(char **rpath, const char *limit)
59 : {
60 4845 : int ret=-1;
61 4845 : char *cp=NULL;
62 : struct stat buf;
63 : #ifdef HAVE_WIN32
64 : int windows_stupidity=0;
65 : #endif
66 4845 : if((cp=strrchr(*rpath, '/')))
67 : {
68 3939 : *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 3939 : if(!**rpath)
77 : {
78 : // We are down to the root, which is OK.
79 : }
80 7878 : else if(lstat(*rpath, &buf))
81 : {
82 : // does not exist - recurse further down, then come
83 : // back and try to mkdir it.
84 2061 : 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 2061 : if(limit && pathcmp(*rpath, limit)<0)
103 : {
104 0 : logp("will not mkdir %s\n", *rpath);
105 0 : goto end;
106 : }
107 2061 : 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 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 4845 : if(cp) *cp='/';
135 4845 : return ret;
136 : }
137 :
138 2694 : 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 2694 : if(!(*rpath=prepend_s(datadir, fname))) return -1;
142 2694 : 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 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 2666 : int build_path_w(const char *path)
164 : {
165 : int ret;
166 2666 : char *rpath=NULL;
167 2666 : ret=build_path(path, "", &rpath, NULL);
168 2666 : free_w(&rpath);
169 2666 : return ret;
170 : }
171 :
172 : #define RECDEL_ERROR -1
173 : #define RECDEL_OK 0
174 : #define RECDEL_ENTRIES_REMAINING 1
175 :
176 2228 : static void get_max(int32_t *max, int32_t default_max)
177 : {
178 2228 : *max = pathconf(".", default_max);
179 2228 : if(*max < 1024) *max = 1024;
180 : // Add for EOS.
181 2228 : (*max)++;
182 2228 : }
183 :
184 4777 : 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 4777 : int ret=RECDEL_ERROR;
189 4777 : DIR *dirp=NULL;
190 4777 : struct dirent *entry=NULL;
191 : struct stat statp;
192 4777 : char *directory=NULL;
193 4777 : char *fullpath=NULL;
194 :
195 4777 : if(!file)
196 : {
197 2228 : if(!(directory=prepend_s(d, "")))
198 : goto end;
199 : }
200 2549 : else if(!(directory=prepend_s(d, file)))
201 : {
202 0 : log_out_of_memory(__func__);
203 0 : goto end;
204 : }
205 :
206 9554 : if(lstat(directory, &statp))
207 : {
208 : // path does not exist.
209 : ret=RECDEL_OK;
210 : goto end;
211 : }
212 :
213 3329 : 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 15150 : errno=0;
223 15150 : if(!(entry=readdir(dirp)))
224 : {
225 3329 : 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 3329 : ret=RECDEL_OK;
233 : break;
234 : }
235 :
236 11821 : if(!filter_dot(entry))
237 6658 : continue;
238 5163 : free_w(&fullpath);
239 5163 : if(!(fullpath=prepend_s(directory, entry->d_name)))
240 : goto end;
241 :
242 5163 : if(is_dir(fullpath, entry)>0)
243 : {
244 : int r;
245 2549 : 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 2549 : if(r==RECDEL_ENTRIES_REMAINING) ret=r;
252 : }
253 2614 : else if(delfiles)
254 : {
255 2614 : 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 3329 : 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 4777 : if(dirp) closedir(dirp);
278 4777 : free_w(&fullpath);
279 4777 : free_w(&directory);
280 4777 : return ret;
281 : }
282 :
283 2228 : 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 2228 : get_max(&name_max, _PC_NAME_MAX);
288 2228 : return do_recursive_delete(path,
289 : NULL, delfiles, name_max, ignore_not_empty_errors);
290 : }
291 :
292 2224 : 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 2224 : 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 2224 : 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 2 : int recursive_delete_dirs_only_no_warnings(const char *path)
313 : {
314 2 : 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 12 : *max=pathconf(path?path:".", what);
332 12 : if(*max<default_max) *max=default_max;
333 : }
334 :
335 6 : int init_fs_max(const char *path)
336 : {
337 : struct stat statp;
338 6 : 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 6 : fs_full_path_max=fs_path_max+fs_name_max;
347 6 : return 0;
348 : }
349 :
350 106 : int looks_like_tmp_or_hidden_file(const char *filename)
351 : {
352 106 : if(!filename) return 0;
353 106 : if(filename[0]=='.' // Also avoids '.' and '..'.
354 : // I am told that emacs tmp files end with '~'.
355 105 : || filename[strlen(filename)-1]=='~')
356 : return 1;
357 104 : return 0;
358 : }
359 :
360 23 : static int do_get_entries_in_directory(DIR *directory, char ***nl,
361 : int *count, int (*compar)(const void *, const void *))
362 : {
363 23 : int allocated=0;
364 23 : char **ntmp=NULL;
365 23 : struct dirent *result=NULL;
366 :
367 23 : *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 128 : errno=0;
374 128 : if(!(result=readdir(directory)))
375 : {
376 23 : if(errno)
377 : {
378 0 : logp("error in readdir: %s\n",
379 : strerror(errno));
380 0 : goto error;
381 : }
382 : break;
383 : }
384 :
385 105 : if(!filter_dot(result))
386 46 : continue;
387 :
388 59 : if(*count==allocated)
389 : {
390 17 : if(!allocated) allocated=10;
391 0 : else allocated*=2;
392 :
393 17 : if(!(ntmp=(char **)
394 17 : realloc_w(*nl, allocated*sizeof(**nl), __func__)))
395 : goto error;
396 17 : *nl=ntmp;
397 : }
398 59 : if(!((*nl)[(*count)++]=strdup_w(result->d_name, __func__)))
399 : goto error;
400 : }
401 23 : if(*nl && compar)
402 17 : 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 23 : static int entries_in_directory(const char *path, char ***nl,
416 : int *count, int atime,
417 : int (*compar)(const char **, const char **))
418 : {
419 23 : int ret=0;
420 23 : DIR *directory=NULL;
421 :
422 23 : 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 1 : if(init_fs_max(path)) return -1;
428 : }
429 : #if defined(O_DIRECTORY) && defined(O_NOATIME)
430 23 : int dfd=-1;
431 23 : if((dfd=open(path, O_RDONLY|O_DIRECTORY|atime?0:O_NOATIME))<0
432 23 : || !(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 23 : if(do_get_entries_in_directory(directory, nl, count,
447 : (int (*)(const void *, const void *))compar))
448 0 : ret=-1;
449 : }
450 23 : if(directory) closedir(directory);
451 : return ret;
452 : }
453 :
454 14301 : int filter_dot(const struct dirent *d)
455 : {
456 14301 : if(!d->d_name
457 14301 : || !strcmp(d->d_name, ".")
458 10602 : || !strcmp(d->d_name, ".."))
459 : return 0;
460 6903 : return 1;
461 : }
462 :
463 76 : static int my_alphasort(const char **a, const char **b)
464 : {
465 76 : return pathcmp(*a, *b);
466 : }
467 :
468 23 : int entries_in_directory_alphasort(const char *path, char ***nl,
469 : int *count, int atime)
470 : {
471 23 : return entries_in_directory(path, nl, count, atime, my_alphasort);
472 : }
473 :
474 : #ifndef HAVE_WIN32
475 1 : int mksock(const char *path)
476 : {
477 1 : int fd=-1;
478 1 : int ret=-1;
479 : struct sockaddr_un addr;
480 : memset(&addr, 0, sizeof(addr));
481 1 : addr.sun_family=AF_UNIX;
482 : strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
483 1 : if((fd=socket(addr.sun_family, SOCK_STREAM, 0))<0
484 1 : || (bind(fd, (struct sockaddr *)&addr, sizeof(addr)))<0)
485 : goto end;
486 1 : ret=0;
487 : end:
488 1 : if(fd>=0) close(fd);
489 1 : return ret;
490 : }
491 :
492 5 : int do_symlink(const char *oldpath, const char *newpath)
493 : {
494 5 : if(!symlink(oldpath, newpath))
495 : return 0;
496 0 : logp("could not symlink '%s' to '%s': %s\n",
497 0 : newpath, oldpath, strerror(errno));
498 0 : return -1;
499 : }
500 :
501 306 : static int do_readlink(const char *path, char buf[], size_t buflen)
502 : {
503 : ssize_t len;
504 612 : if((len=readlink(path, buf, buflen-1))<0)
505 : return -1;
506 306 : buf[len]='\0';
507 306 : return 0;
508 : }
509 :
510 854 : int readlink_w(const char *path, char buf[], size_t buflen)
511 : {
512 : struct stat statp;
513 854 : if(lstat(path, &statp))
514 : return -1;
515 306 : if(S_ISLNK(statp.st_mode))
516 306 : return do_readlink(path, buf, buflen);
517 : return -1;
518 : }
519 :
520 845 : int readlink_w_in_dir(const char *dir, const char *lnk,
521 : char buf[], size_t buflen)
522 : {
523 845 : char *tmp=NULL;
524 845 : if(!(tmp=prepend_s(dir, lnk)))
525 : return -1;
526 845 : readlink_w(tmp, buf, buflen);
527 845 : free_w(&tmp);
528 845 : return 0;
529 : }
530 :
531 0 : int is_lnk(const char *path)
532 : {
533 : struct stat buf;
534 0 : if(lstat(path, &buf))
535 : return -1;
536 0 : return S_ISLNK(buf.st_mode);
537 : }
538 :
539 0 : int is_lnk_valid(const char *path)
540 : {
541 : struct stat buf;
542 0 : if(stat(path, &buf))
543 : return 0;
544 0 : return 1;
545 : }
546 : #endif
|