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 11 : void close_fd(int *fd)
13 : {
14 22 : if(!fd || *fd<0) return;
15 : //logp("closing %d\n", *fd);
16 11 : close(*fd);
17 11 : *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 2254 : int is_dir(const char *path, struct dirent *d)
30 : {
31 : #ifdef _DIRENT_HAVE_D_TYPE
32 : // Faster evaluation on most systems.
33 2254 : switch(d->d_type)
34 : {
35 : case DT_DIR:
36 955 : return 1;
37 : case DT_UNKNOWN:
38 0 : break;
39 : default:
40 1299 : return 0;
41 : }
42 : #endif
43 0 : return is_dir_lstat(path);
44 : }
45 :
46 2603 : int mkpath(char **rpath, const char *limit)
47 : {
48 2603 : int ret=-1;
49 2603 : char *cp=NULL;
50 : struct stat buf;
51 : #ifdef HAVE_WIN32
52 : int windows_stupidity=0;
53 : #endif
54 2603 : if((cp=strrchr(*rpath, '/')))
55 : {
56 2011 : *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 2011 : if(!**rpath)
65 : {
66 : // We are down to the root, which is OK.
67 : }
68 2011 : else if(lstat(*rpath, &buf))
69 : {
70 : // does not exist - recurse further down, then come
71 : // back and try to mkdir it.
72 655 : 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 655 : if(limit && pathcmp(*rpath, limit)<0)
91 : {
92 0 : logp("will not mkdir %s\n", *rpath);
93 0 : goto end;
94 : }
95 655 : if(mkdir(*rpath, 0777))
96 : {
97 0 : logp("could not mkdir %s: %s\n", *rpath, strerror(errno));
98 0 : goto end;
99 : }
100 : }
101 1356 : else if(S_ISDIR(buf.st_mode))
102 : {
103 : // Is a directory - can put the slash back and return.
104 : }
105 0 : 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 2603 : ret=0;
118 : end:
119 : #ifdef HAVE_WIN32
120 : if(windows_stupidity) (*rpath)[1]=':';
121 : #endif
122 2603 : if(cp) *cp='/';
123 2603 : return ret;
124 : }
125 :
126 1948 : 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 1948 : if(!(*rpath=prepend_s(datadir, fname))) return -1;
130 1948 : if(mkpath(rpath, limit))
131 : {
132 0 : free_w(rpath);
133 0 : return -1;
134 : }
135 1948 : return 0;
136 : }
137 :
138 110 : 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 110 : 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 110 : return 0;
149 : }
150 :
151 1948 : int build_path_w(const char *path)
152 : {
153 : int ret;
154 1948 : char *rpath=NULL;
155 1948 : ret=build_path(path, "", &rpath, NULL);
156 1948 : free_w(&rpath);
157 1948 : return ret;
158 : }
159 :
160 : #define RECDEL_ERROR -1
161 : #define RECDEL_OK 0
162 : #define RECDEL_ENTRIES_REMAINING 1
163 :
164 1016 : static void get_max(int32_t *max, int32_t default_max)
165 : {
166 1016 : *max = pathconf(".", default_max);
167 1016 : if(*max < 1024) *max = 1024;
168 : // Add for EOS.
169 1016 : (*max)++;
170 1016 : }
171 :
172 1971 : static int do_recursive_delete(const char *d, const char *file,
173 : uint8_t delfiles, int32_t name_max)
174 : {
175 1971 : int ret=RECDEL_ERROR;
176 1971 : DIR *dirp=NULL;
177 1971 : struct dirent *entry=NULL;
178 : struct dirent *result;
179 : struct stat statp;
180 1971 : char *directory=NULL;
181 1971 : char *fullpath=NULL;
182 :
183 1971 : if(!file)
184 : {
185 1016 : if(!(directory=prepend_s(d, ""))) goto end;
186 : }
187 955 : else if(!(directory=prepend_s(d, file)))
188 : {
189 0 : log_out_of_memory(__func__);
190 0 : goto end;
191 : }
192 :
193 1971 : if(lstat(directory, &statp))
194 : {
195 : // path does not exist.
196 664 : ret=RECDEL_OK;
197 664 : goto end;
198 : }
199 :
200 1307 : if(!(dirp=opendir(directory)))
201 : {
202 0 : logp("opendir %s: %s\n", directory, strerror(errno));
203 0 : goto end;
204 : }
205 :
206 2614 : if(!(entry=(struct dirent *)
207 2614 : malloc_w(sizeof(struct dirent)+name_max+100, __func__)))
208 0 : goto end;
209 :
210 : while(1)
211 : {
212 6175 : if(readdir_r(dirp, entry, &result) || !result)
213 : {
214 : // Got to the end of the directory.
215 1307 : ret=RECDEL_OK;
216 1307 : break;
217 : }
218 :
219 4868 : if(!entry->d_ino
220 4868 : || !strcmp(entry->d_name, ".")
221 3561 : || !strcmp(entry->d_name, ".."))
222 2614 : continue;
223 2254 : free_w(&fullpath);
224 2254 : if(!(fullpath=prepend_s(directory, entry->d_name)))
225 0 : goto end;
226 :
227 2254 : if(is_dir(fullpath, entry)>0)
228 : {
229 : int r;
230 955 : if((r=do_recursive_delete(directory, entry->d_name,
231 955 : delfiles, name_max))==RECDEL_ERROR)
232 0 : goto end;
233 : // do not overwrite ret with OK if it previously
234 : // had ENTRIES_REMAINING
235 955 : if(r==RECDEL_ENTRIES_REMAINING) ret=r;
236 : }
237 1299 : else if(delfiles)
238 : {
239 1299 : 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 0 : ret=RECDEL_ENTRIES_REMAINING;
249 : }
250 : }
251 :
252 1307 : 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 1971 : if(dirp) closedir(dirp);
259 1971 : free_w(&fullpath);
260 1971 : free_w(&directory);
261 1971 : free_v((void **)&entry);
262 6839 : return ret;
263 : }
264 :
265 1016 : static int do_recursive_delete_w(const char *path, uint8_t delfiles)
266 : {
267 : int32_t name_max;
268 1016 : get_max(&name_max, _PC_NAME_MAX);
269 1016 : return do_recursive_delete(path, NULL, delfiles, name_max);
270 : }
271 :
272 1016 : 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 1016 : 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 1016 : 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 0 : return 0;
301 : }
302 :
303 24 : static void init_max(const char *path,
304 : uint32_t *max, int what, uint32_t default_max)
305 : {
306 24 : *max=pathconf(path?path:".", what);
307 24 : if(*max<default_max) *max=default_max;
308 24 : }
309 :
310 12 : int init_fs_max(const char *path)
311 : {
312 : struct stat statp;
313 12 : 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 12 : init_max(path, &fs_path_max, _PC_PATH_MAX, 1024);
320 12 : init_max(path, &fs_name_max, _PC_NAME_MAX, 255);
321 12 : fs_full_path_max=fs_path_max+fs_name_max;
322 12 : return 0;
323 : }
324 :
325 0 : int looks_like_tmp_or_hidden_file(const char *filename)
326 : {
327 0 : if(!filename) return 0;
328 0 : if(filename[0]=='.' // Also avoids '.' and '..'.
329 : // I am told that emacs tmp files end with '~'.
330 0 : || filename[strlen(filename)-1]=='~')
331 0 : return 1;
332 0 : return 0;
333 : }
334 :
335 191 : 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 191 : int allocated=0;
340 191 : struct dirent **ntmp=NULL;
341 191 : struct dirent *entry=NULL;
342 191 : struct dirent *result=NULL;
343 :
344 : // This here is doing a funky kind of scandir/alphasort
345 : // that can also run on Windows.
346 : while(1)
347 : {
348 : char *p;
349 3496 : if(!(entry=(struct dirent *)malloc_w(
350 3496 : sizeof(struct dirent)+fs_name_max+100, __func__)))
351 0 : goto error;
352 1748 : status=readdir_r(directory, entry, &result);
353 1748 : if(status || !result)
354 : {
355 191 : free_v((void **)&entry);
356 191 : break;
357 : }
358 :
359 1557 : p=entry->d_name;
360 : ASSERT(fs_name_max+1 > (int)sizeof(struct dirent)+strlen(p));
361 :
362 1557 : if(!p
363 1557 : || !strcmp(p, ".")
364 1366 : || !strcmp(p, ".."))
365 : {
366 382 : free_v((void **)&entry);
367 382 : continue;
368 : }
369 :
370 1175 : if(*count==allocated)
371 : {
372 199 : if(!allocated) allocated=10;
373 16 : else allocated*=2;
374 :
375 199 : if(!(ntmp=(struct dirent **)
376 199 : realloc_w(*nl, allocated*sizeof(**nl), __func__)))
377 0 : goto error;
378 199 : *nl=ntmp;
379 : }
380 1175 : (*nl)[(*count)++]=entry;
381 : }
382 191 : if(*nl && compar) qsort(*nl, *count, sizeof(**nl), compar);
383 191 : return 0;
384 : error:
385 0 : free_v((void **)&entry);
386 0 : if(*nl)
387 : {
388 : int i;
389 0 : for(i=0; i<*count; i++)
390 0 : free_v((void **)&((*nl)[i]));
391 0 : free_v((void **)nl);
392 : }
393 1557 : return -1;
394 : }
395 :
396 191 : static int entries_in_directory(const char *path, struct dirent ***nl,
397 : int *count, int atime,
398 : int (*compar)(const struct dirent **, const struct dirent **))
399 : {
400 191 : int ret=0;
401 191 : DIR *directory=NULL;
402 :
403 191 : if(!fs_name_max)
404 : {
405 : // Get system path and filename maximum lengths.
406 : // FIX THIS: maybe this should be done every time a file system
407 : // is crossed?
408 12 : if(init_fs_max(path)) return -1;
409 : }
410 : #if defined(O_DIRECTORY) && defined(O_NOATIME)
411 191 : int dfd=-1;
412 573 : if((dfd=open(path, O_RDONLY|O_DIRECTORY|atime?0:O_NOATIME))<0
413 382 : || !(directory=fdopendir(dfd)))
414 : #else
415 : // Mac OS X appears to have no O_NOATIME and no fdopendir(), so it should
416 : // end up using opendir() here.
417 : if(!(directory=opendir(path)))
418 : #endif
419 : {
420 : #if defined(O_DIRECTORY) && defined(O_NOATIME)
421 0 : close_fd(&dfd);
422 : #endif
423 0 : ret=1;
424 : }
425 : else
426 : {
427 191 : if(do_get_entries_in_directory(directory, nl, count,
428 191 : (int (*)(const void *, const void *))compar))
429 0 : ret=-1;
430 : }
431 191 : if(directory) closedir(directory);
432 191 : return ret;
433 : }
434 :
435 76 : static int my_alphasort(const struct dirent **a, const struct dirent **b)
436 : {
437 76 : return strcmp((*a)->d_name, (*b)->d_name);
438 : }
439 :
440 2202 : static int rev_alphasort(const struct dirent **a, const struct dirent **b)
441 : {
442 2202 : return strcmp((*a)->d_name, (*b)->d_name)*-1;
443 : }
444 :
445 23 : int entries_in_directory_alphasort(const char *path, struct dirent ***nl,
446 : int *count, int atime)
447 : {
448 23 : return entries_in_directory(path, nl, count, atime, my_alphasort);
449 : }
450 :
451 168 : int entries_in_directory_alphasort_rev(const char *path, struct dirent ***nl,
452 : int *count, int atime)
453 : {
454 168 : return entries_in_directory(path, nl, count, atime, rev_alphasort);
455 : }
456 :
457 0 : int entries_in_directory_no_sort(const char *path, struct dirent ***nl,
458 : int *count, int atime)
459 : {
460 0 : return entries_in_directory(path, nl, count, atime, NULL);
461 : }
|