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