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
|