Line data Source code
1 : #include "burp.h"
2 : #include "alloc.h"
3 : #include "fsops.h"
4 : #include "log.h"
5 : #include "pathcmp.h"
6 : #include "prepend.h"
7 :
8 : #ifndef HAVE_WIN32
9 : #include <sys/un.h>
10 : #endif
11 :
12 : uint32_t fs_name_max=0;
13 : uint32_t fs_full_path_max=0;
14 : static uint32_t fs_path_max=0;
15 :
16 204 : void close_fd(int *fd)
17 : {
18 409 : if(!fd || *fd<0) return;
19 : //logp("closing %d\n", *fd);
20 204 : close(*fd);
21 204 : *fd=-1;
22 : }
23 :
24 0 : int is_dir_lstat(const char *path)
25 : {
26 : struct stat buf;
27 :
28 0 : if(lstat(path, &buf)) return -1;
29 :
30 0 : return S_ISDIR(buf.st_mode);
31 : }
32 :
33 4713 : int is_dir(const char *path, struct dirent *d)
34 : {
35 : #ifdef _DIRENT_HAVE_D_TYPE
36 : // Faster evaluation on most systems.
37 4713 : switch(d->d_type)
38 : {
39 : case DT_DIR:
40 : return 1;
41 : case DT_UNKNOWN:
42 : break;
43 : default:
44 2495 : return 0;
45 : }
46 : #endif
47 0 : return is_dir_lstat(path);
48 : }
49 :
50 4157 : int mkpath(char **rpath, const char *limit)
51 : {
52 4157 : int ret=-1;
53 4157 : char *cp=NULL;
54 : struct stat buf;
55 : #ifdef HAVE_WIN32
56 : int windows_stupidity=0;
57 : #endif
58 8314 : if((cp=strrchr(*rpath, '/')))
59 : {
60 3296 : *cp='\0';
61 : #ifdef HAVE_WIN32
62 : if(strlen(*rpath)==2 && (*rpath)[1]==':')
63 : {
64 : (*rpath)[1]='\0';
65 : windows_stupidity++;
66 : }
67 : #endif
68 3296 : if(!**rpath)
69 : {
70 : // We are down to the root, which is OK.
71 : }
72 6592 : else if(lstat(*rpath, &buf))
73 : {
74 : // does not exist - recurse further down, then come
75 : // back and try to mkdir it.
76 1621 : if(mkpath(rpath, limit)) goto end;
77 :
78 : // Require that the user has set up the required paths
79 : // on the server correctly. I have seen problems with
80 : // part of the path being a temporary symlink that
81 : // gets replaced by burp with a proper directory.
82 : // Allow it to create the actual directory specified,
83 : // though.
84 :
85 : // That is, if limit is:
86 : // /var/spool/burp
87 : // and /var/spool exists, the directory will be
88 : // created.
89 : // If only /var exists, the directory will not be
90 : // created.
91 :
92 : // Caller can give limit=NULL to create the whole
93 : // path with no limit, as in a restore.
94 1621 : if(limit && pathcmp(*rpath, limit)<0)
95 : {
96 0 : logp("will not mkdir %s\n", *rpath);
97 0 : goto end;
98 : }
99 1621 : if(mkdir(*rpath, 0777))
100 : {
101 0 : logp("could not mkdir %s: %s\n", *rpath, strerror(errno));
102 0 : goto end;
103 : }
104 : }
105 1675 : else if(S_ISDIR(buf.st_mode))
106 : {
107 : // Is a directory - can put the slash back and return.
108 : }
109 30 : else if(S_ISLNK(buf.st_mode))
110 : {
111 : // to help with the 'current' symlink
112 : }
113 : else
114 : {
115 : // something funny going on
116 : logp("warning: wanted '%s' to be a directory\n",
117 0 : *rpath);
118 : }
119 : }
120 :
121 : ret=0;
122 : end:
123 : #ifdef HAVE_WIN32
124 : if(windows_stupidity) (*rpath)[1]=':';
125 : #endif
126 4157 : if(cp) *cp='/';
127 4157 : return ret;
128 : }
129 :
130 2532 : int build_path(const char *datadir, const char *fname, char **rpath, const char *limit)
131 : {
132 : //logp("build path: '%s/%s'\n", datadir, fname);
133 2532 : if(!(*rpath=prepend_s(datadir, fname))) return -1;
134 2532 : if(mkpath(rpath, limit))
135 : {
136 0 : free_w(rpath);
137 0 : return -1;
138 : }
139 : return 0;
140 : }
141 :
142 232 : int do_rename(const char *oldpath, const char *newpath)
143 : {
144 : // Be careful, this is not actually atomic. Everything that uses this
145 : // needs to deal with the consequences.
146 232 : if(rename(oldpath, newpath))
147 : {
148 : logp("could not rename '%s' to '%s': %s\n",
149 0 : oldpath, newpath, strerror(errno));
150 0 : return -1;
151 : }
152 : return 0;
153 : }
154 :
155 2504 : int build_path_w(const char *path)
156 : {
157 : int ret;
158 2504 : char *rpath=NULL;
159 2504 : ret=build_path(path, "", &rpath, NULL);
160 2504 : free_w(&rpath);
161 2504 : return ret;
162 : }
163 :
164 : #define RECDEL_ERROR -1
165 : #define RECDEL_OK 0
166 : #define RECDEL_ENTRIES_REMAINING 1
167 :
168 1980 : static void get_max(int32_t *max, int32_t default_max)
169 : {
170 1980 : *max = pathconf(".", default_max);
171 1980 : if(*max < 1024) *max = 1024;
172 : // Add for EOS.
173 1980 : (*max)++;
174 1980 : }
175 :
176 4130 : static int do_recursive_delete(const char *d, const char *file,
177 : uint8_t delfiles, int32_t name_max)
178 : {
179 4130 : int ret=RECDEL_ERROR;
180 4130 : DIR *dirp=NULL;
181 4130 : struct dirent *entry=NULL;
182 : struct dirent *result;
183 : struct stat statp;
184 4130 : char *directory=NULL;
185 4130 : char *fullpath=NULL;
186 :
187 4130 : if(!file)
188 : {
189 1980 : if(!(directory=prepend_s(d, ""))) goto end;
190 : }
191 2150 : else if(!(directory=prepend_s(d, file)))
192 : {
193 0 : log_out_of_memory(__func__);
194 0 : goto end;
195 : }
196 :
197 8260 : if(lstat(directory, &statp))
198 : {
199 : // path does not exist.
200 : ret=RECDEL_OK;
201 : goto end;
202 : }
203 :
204 2884 : if(!(dirp=opendir(directory)))
205 : {
206 0 : logp("opendir %s: %s\n", directory, strerror(errno));
207 0 : goto end;
208 : }
209 :
210 2884 : if(!(entry=(struct dirent *)
211 2884 : malloc_w(sizeof(struct dirent)+name_max+100, __func__)))
212 : goto end;
213 :
214 : while(1)
215 : {
216 13257 : if(readdir_r(dirp, entry, &result) || !result)
217 : {
218 : // Got to the end of the directory.
219 2884 : ret=RECDEL_OK;
220 : break;
221 : }
222 :
223 10373 : if(!entry->d_ino
224 10373 : || !strcmp(entry->d_name, ".")
225 7489 : || !strcmp(entry->d_name, ".."))
226 : continue;
227 4605 : free_w(&fullpath);
228 4605 : if(!(fullpath=prepend_s(directory, entry->d_name)))
229 : goto end;
230 :
231 4605 : if(is_dir(fullpath, entry)>0)
232 : {
233 : int r;
234 2150 : if((r=do_recursive_delete(directory, entry->d_name,
235 2150 : delfiles, name_max))==RECDEL_ERROR)
236 : goto end;
237 : // do not overwrite ret with OK if it previously
238 : // had ENTRIES_REMAINING
239 2150 : if(r==RECDEL_ENTRIES_REMAINING) ret=r;
240 : }
241 2455 : else if(delfiles)
242 : {
243 2455 : if(unlink(fullpath))
244 : {
245 : logp("unlink %s: %s\n",
246 0 : fullpath, strerror(errno));
247 0 : ret=RECDEL_ENTRIES_REMAINING;
248 : }
249 : }
250 : else
251 : {
252 : ret=RECDEL_ENTRIES_REMAINING;
253 : }
254 : }
255 :
256 5768 : if(ret==RECDEL_OK && rmdir(directory))
257 : {
258 0 : logp("rmdir %s: %s\n", directory, strerror(errno));
259 0 : ret=RECDEL_ERROR;
260 : }
261 : end:
262 4130 : if(dirp) closedir(dirp);
263 4130 : free_w(&fullpath);
264 4130 : free_w(&directory);
265 4130 : free_v((void **)&entry);
266 4130 : return ret;
267 : }
268 :
269 1980 : static int do_recursive_delete_w(const char *path, uint8_t delfiles)
270 : {
271 : int32_t name_max;
272 1980 : get_max(&name_max, _PC_NAME_MAX);
273 1980 : return do_recursive_delete(path, NULL, delfiles, name_max);
274 : }
275 :
276 1980 : int recursive_delete(const char *path)
277 : {
278 : struct stat statp;
279 : // We might have been given a file entry, instead of a directory.
280 1980 : if(!lstat(path, &statp) && !S_ISDIR(statp.st_mode))
281 : {
282 156 : if(unlink(path))
283 : {
284 0 : logp("unlink %s: %s\n", path, strerror(errno));
285 0 : return RECDEL_ENTRIES_REMAINING;
286 : }
287 : }
288 1980 : return do_recursive_delete_w(path, 1);
289 : }
290 :
291 0 : int recursive_delete_dirs_only(const char *path)
292 : {
293 0 : return do_recursive_delete_w(path, 0);
294 : }
295 :
296 0 : int unlink_w(const char *path, const char *func)
297 : {
298 0 : if(unlink(path))
299 : {
300 : logp("unlink(%s) called from %s(): %s\n",
301 0 : path, func, strerror(errno));
302 0 : return -1;
303 : }
304 : return 0;
305 : }
306 :
307 : static void init_max(const char *path,
308 : uint32_t *max, int what, uint32_t default_max)
309 : {
310 94 : *max=pathconf(path?path:".", what);
311 94 : if(*max<default_max) *max=default_max;
312 : }
313 :
314 47 : int init_fs_max(const char *path)
315 : {
316 : struct stat statp;
317 47 : if(stat(path, &statp))
318 : {
319 0 : logp("Path %s does not exist in %s\n", path, __func__);
320 0 : return -1;
321 : }
322 : // Get system path and filename maximum lengths.
323 : init_max(path, &fs_path_max, _PC_PATH_MAX, 1024);
324 : init_max(path, &fs_name_max, _PC_NAME_MAX, 255);
325 47 : fs_full_path_max=fs_path_max+fs_name_max;
326 47 : return 0;
327 : }
328 :
329 92 : int looks_like_tmp_or_hidden_file(const char *filename)
330 : {
331 92 : if(!filename) return 0;
332 92 : if(filename[0]=='.' // Also avoids '.' and '..'.
333 : // I am told that emacs tmp files end with '~'.
334 91 : || filename[strlen(filename)-1]=='~')
335 : return 1;
336 90 : return 0;
337 : }
338 :
339 370 : int do_get_entries_in_directory(DIR *directory, struct dirent ***nl,
340 : int *count, int (*compar)(const void *, const void *))
341 : {
342 : int status;
343 370 : int allocated=0;
344 370 : struct dirent **ntmp=NULL;
345 370 : struct dirent *entry=NULL;
346 370 : struct dirent *result=NULL;
347 :
348 370 : *count=0;
349 :
350 : // This here is doing a funky kind of scandir/alphasort
351 : // that can also run on Windows.
352 : while(1)
353 : {
354 : char *p;
355 2850 : if(!(entry=(struct dirent *)malloc_w(
356 2850 : sizeof(struct dirent)+fs_name_max+100, __func__)))
357 : goto error;
358 2850 : status=readdir_r(directory, entry, &result);
359 2850 : if(status || !result)
360 : {
361 370 : free_v((void **)&entry);
362 : break;
363 : }
364 :
365 2480 : p=entry->d_name;
366 : ASSERT(fs_name_max+1 > (int)sizeof(struct dirent)+strlen(p));
367 :
368 2480 : if(!p
369 2480 : || !strcmp(p, ".")
370 2110 : || !strcmp(p, ".."))
371 : {
372 740 : free_v((void **)&entry);
373 740 : continue;
374 : }
375 :
376 1740 : if(*count==allocated)
377 : {
378 369 : if(!allocated) allocated=10;
379 11 : else allocated*=2;
380 :
381 369 : if(!(ntmp=(struct dirent **)
382 369 : realloc_w(*nl, allocated*sizeof(**nl), __func__)))
383 : goto error;
384 369 : *nl=ntmp;
385 : }
386 1740 : (*nl)[(*count)++]=entry;
387 : }
388 728 : if(*nl && compar) qsort(*nl, *count, sizeof(**nl), compar);
389 : return 0;
390 : error:
391 0 : free_v((void **)&entry);
392 0 : if(*nl)
393 : {
394 : int i;
395 0 : for(i=0; i<*count; i++)
396 0 : free_v((void **)&((*nl)[i]));
397 0 : free_v((void **)nl);
398 : }
399 : return -1;
400 : }
401 :
402 371 : static int entries_in_directory(const char *path, struct dirent ***nl,
403 : int *count, int atime,
404 : int (*compar)(const struct dirent **, const struct dirent **))
405 : {
406 371 : int ret=0;
407 371 : DIR *directory=NULL;
408 :
409 371 : if(!fs_name_max)
410 : {
411 : // Get system path and filename maximum lengths.
412 : // FIX THIS: maybe this should be done every time a file system
413 : // is crossed?
414 42 : if(init_fs_max(path)) return -1;
415 : }
416 : #if defined(O_DIRECTORY) && defined(O_NOATIME)
417 371 : int dfd=-1;
418 371 : if((dfd=open(path, O_RDONLY|O_DIRECTORY|atime?0:O_NOATIME))<0
419 371 : || !(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 370 : if(do_get_entries_in_directory(directory, nl, count,
434 370 : (int (*)(const void *, const void *))compar))
435 0 : ret=-1;
436 : }
437 371 : if(directory) closedir(directory);
438 : return ret;
439 : }
440 :
441 76 : static int my_alphasort(const struct dirent **a, const struct dirent **b)
442 : {
443 76 : return strcmp((*a)->d_name, (*b)->d_name);
444 : }
445 :
446 2590 : static int rev_alphasort(const struct dirent **a, const struct dirent **b)
447 : {
448 2590 : return strcmp((*a)->d_name, (*b)->d_name)*-1;
449 : }
450 :
451 23 : int entries_in_directory_alphasort(const char *path, struct dirent ***nl,
452 : int *count, int atime)
453 : {
454 23 : return entries_in_directory(path, nl, count, atime, my_alphasort);
455 : }
456 :
457 279 : int entries_in_directory_alphasort_rev(const char *path, struct dirent ***nl,
458 : int *count, int atime)
459 : {
460 279 : return entries_in_directory(path, nl, count, atime, rev_alphasort);
461 : }
462 :
463 69 : int entries_in_directory_no_sort(const char *path, struct dirent ***nl,
464 : int *count, int atime)
465 : {
466 69 : return entries_in_directory(path, nl, count, atime, NULL);
467 : }
468 :
469 : #ifndef HAVE_WIN32
470 1 : int mksock(const char *path)
471 : {
472 1 : int fd=-1;
473 1 : int ret=-1;
474 : struct sockaddr_un addr;
475 : memset(&addr, 0, sizeof(addr));
476 1 : addr.sun_family=AF_UNIX;
477 1 : strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
478 1 : if((fd=socket(addr.sun_family, SOCK_STREAM, 0))<0
479 1 : || (bind(fd, (struct sockaddr *)&addr, sizeof(addr)))<0)
480 : goto end;
481 1 : ret=0;
482 : end:
483 1 : if(fd>=0) close(fd);
484 1 : return ret;
485 : }
486 : #endif
|