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 
close_fd(int * fd)17 void close_fd(int *fd)
18 {
19 	if(!fd || *fd<0) return;
20 	//logp("closing %d\n", *fd);
21 	close(*fd);
22 	*fd=-1;
23 }
24 
is_dir_lstat(const char * path)25 int is_dir_lstat(const char *path)
26 {
27 	struct stat buf;
28 	if(lstat(path, &buf))
29 		return -1;
30 	return S_ISDIR(buf.st_mode);
31 }
32 
is_reg_lstat(const char * path)33 int is_reg_lstat(const char *path)
34 {
35 	struct stat buf;
36 	if(lstat(path, &buf))
37 		return -1;
38 	return S_ISREG(buf.st_mode);
39 }
40 
is_dir(const char * path,struct dirent * d)41 int is_dir(const char *path, struct dirent *d)
42 {
43 #ifdef _DIRENT_HAVE_D_TYPE
44 	// Faster evaluation on most systems.
45 	switch(d->d_type)
46 	{
47 		case DT_DIR:
48 			return 1;
49 		case DT_UNKNOWN:
50 			break;
51 		default:
52 			return 0;
53 	}
54 #endif
55 	return is_dir_lstat(path);
56 }
57 
mkpath(char ** rpath,const char * limit)58 int mkpath(char **rpath, const char *limit)
59 {
60 	int ret=-1;
61 	char *cp=NULL;
62 	struct stat buf;
63 
64 	if((cp=strrchr(*rpath, '/')))
65 	{
66 		*cp='\0';
67 #ifdef HAVE_WIN32
68 		if(strlen(*rpath)==2 && (*rpath)[1]==':')
69 		{
70 			// We are down to the drive letter, which is OK.
71 		} else
72 #endif
73 		if(!**rpath)
74 		{
75 			// We are down to the root, which is OK.
76 		}
77 		else if(lstat(*rpath, &buf))
78 		{
79 			// does not exist - recurse further down, then come
80 			// back and try to mkdir it.
81 			if(mkpath(rpath, limit)) goto end;
82 
83 			// Require that the user has set up the required paths
84 			// on the server correctly. I have seen problems with
85 			// part of the path being a temporary symlink that
86 			// gets replaced by burp with a proper directory.
87 			// Allow it to create the actual directory specified,
88 			// though.
89 
90 			// That is, if limit is:
91 			// /var/spool/burp
92 			// and /var/spool exists, the directory will be
93 			// created.
94 			// If only /var exists, the directory will not be
95 			// created.
96 
97 			// Caller can give limit=NULL to create the whole
98 			// path with no limit, as in a restore.
99 			if(limit && pathcmp(*rpath, limit)<0)
100 			{
101 				logp("will not mkdir %s\n", *rpath);
102 				goto end;
103 			}
104 			if(mkdir(*rpath, 0777))
105 			{
106 				logp("could not mkdir %s: %s\n", *rpath, strerror(errno));
107 				goto end;
108 			}
109 		}
110 		else if(S_ISDIR(buf.st_mode))
111 		{
112 			// Is a directory - can put the slash back and return.
113 		}
114 		else if(S_ISLNK(buf.st_mode))
115 		{
116 			// to help with the 'current' symlink
117 		}
118 		else
119 		{
120 			// something funny going on
121 			logp("warning: wanted '%s' to be a directory\n",
122 				*rpath);
123 		}
124 	}
125 
126 	ret=0;
127 end:
128 	if(cp) *cp='/';
129 	return ret;
130 }
131 
build_path(const char * datadir,const char * fname,char ** rpath,const char * limit)132 int build_path(const char *datadir, const char *fname, char **rpath, const char *limit)
133 {
134 	if(!(*rpath=prepend_s(datadir, fname))) return -1;
135 	if(mkpath(rpath, limit))
136 	{
137 		free_w(rpath);
138 		return -1;
139 	}
140 	return 0;
141 }
142 
do_rename(const char * oldpath,const char * newpath)143 int do_rename(const char *oldpath, const char *newpath)
144 {
145 	// Be careful, this is not actually atomic. Everything that uses this
146 	// needs to deal with the consequences.
147 	if(rename(oldpath, newpath))
148 	{
149 		logp("could not rename '%s' to '%s': %s\n",
150 			oldpath, newpath, strerror(errno));
151 		return -1;
152 	}
153 	return 0;
154 }
155 
build_path_w(const char * path)156 int build_path_w(const char *path)
157 {
158 	int ret;
159 	char *rpath=NULL;
160 	ret=build_path(path, "", &rpath, NULL);
161 	free_w(&rpath);
162 	return ret;
163 }
164 
165 #define RECDEL_ERROR			-1
166 #define RECDEL_OK			0
167 #define RECDEL_ENTRIES_REMAINING	1
168 
get_max(int32_t * max,int32_t default_max)169 static void get_max(int32_t *max, int32_t default_max)
170 {
171 	*max = pathconf(".", default_max);
172 	if(*max < 1024) *max = 1024;
173 	// Add for EOS.
174 	(*max)++;
175 }
176 
do_recursive_delete(const char * d,const char * file,uint8_t delfiles,int32_t name_max,uint8_t ignore_not_empty_errors)177 static int do_recursive_delete(const char *d, const char *file,
178 	uint8_t delfiles, int32_t name_max,
179 	uint8_t ignore_not_empty_errors)
180 {
181 	int ret=RECDEL_ERROR;
182 	DIR *dirp=NULL;
183 	struct dirent *entry=NULL;
184 	struct stat statp;
185 	char *directory=NULL;
186 	char *fullpath=NULL;
187 
188 	if(!file)
189 	{
190 		if(!(directory=prepend_s(d, "")))
191 			goto end;
192 	}
193 	else if(!(directory=prepend_s(d, file)))
194 	{
195 		log_out_of_memory(__func__);
196 		goto end;
197 	}
198 
199 	if(lstat(directory, &statp))
200 	{
201 		// path does not exist.
202 		ret=RECDEL_OK;
203 		goto end;
204 	}
205 
206 	if(!(dirp=opendir(directory)))
207 	{
208 		logp("opendir %s in %s: %s\n",
209 			directory, __func__, strerror(errno));
210 		goto end;
211 	}
212 
213 	while(1)
214 	{
215 		errno=0;
216 		if(!(entry=readdir(dirp)))
217 		{
218 			if(errno)
219 			{
220 				logp("error in readdir in %s: %s\n",
221 					__func__, strerror(errno));
222 				goto end;
223 			}
224 			// Got to the end of the directory.
225 			ret=RECDEL_OK;
226 			break;
227 		}
228 
229 		if(!filter_dot(entry))
230 			continue;
231 		free_w(&fullpath);
232 		if(!(fullpath=prepend_s(directory, entry->d_name)))
233 			goto end;
234 
235 		if(is_dir(fullpath, entry)>0)
236 		{
237 			int r;
238 			if((r=do_recursive_delete(directory, entry->d_name,
239 				delfiles, name_max,
240 				ignore_not_empty_errors))==RECDEL_ERROR)
241 					goto end;
242 			// do not overwrite ret with OK if it previously
243 			// had ENTRIES_REMAINING
244 			if(r==RECDEL_ENTRIES_REMAINING) ret=r;
245 		}
246 		else if(delfiles)
247 		{
248 			if(unlink(fullpath))
249 			{
250 				logp("unlink %s: %s\n",
251 					fullpath, strerror(errno));
252 				ret=RECDEL_ENTRIES_REMAINING;
253 			}
254 		}
255 		else
256 		{
257 			ret=RECDEL_ENTRIES_REMAINING;
258 		}
259 	}
260 
261 	if(ret==RECDEL_OK && rmdir(directory))
262 	{
263 		if(errno!=ENOTEMPTY || !ignore_not_empty_errors)
264 		{
265 			logp("rmdir %s: %s\n", directory, strerror(errno));
266 			ret=RECDEL_ERROR;
267 		}
268 	}
269 end:
270 	if(dirp) closedir(dirp);
271 	free_w(&fullpath);
272 	free_w(&directory);
273 	return ret;
274 }
275 
do_recursive_delete_w(const char * path,uint8_t delfiles,uint8_t ignore_not_empty_errors)276 static int do_recursive_delete_w(const char *path, uint8_t delfiles,
277 	uint8_t ignore_not_empty_errors)
278 {
279 	int32_t name_max;
280 	get_max(&name_max, _PC_NAME_MAX);
281 	return do_recursive_delete(path,
282 		NULL, delfiles, name_max, ignore_not_empty_errors);
283 }
284 
recursive_delete(const char * path)285 int recursive_delete(const char *path)
286 {
287 	struct stat statp;
288 	// We might have been given a file entry, instead of a directory.
289 	if(!lstat(path, &statp) && !S_ISDIR(statp.st_mode))
290 	{
291 		if(unlink(path))
292 		{
293 			logp("unlink %s: %s\n", path, strerror(errno));
294 			return RECDEL_ENTRIES_REMAINING;
295 		}
296 	}
297 	return do_recursive_delete_w(path, 1, 0/*ignore_not_empty_errors*/);
298 }
299 
recursive_delete_dirs_only(const char * path)300 int recursive_delete_dirs_only(const char *path)
301 {
302 	return do_recursive_delete_w(path, 0, 0/*ignore_not_empty_errors*/);
303 }
304 
recursive_delete_dirs_only_no_warnings(const char * path)305 int recursive_delete_dirs_only_no_warnings(const char *path)
306 {
307 	return do_recursive_delete_w(path, 0, 1/*ignore_not_empty_errors*/);
308 }
309 
unlink_w(const char * path,const char * func)310 int unlink_w(const char *path, const char *func)
311 {
312 	if(unlink(path))
313 	{
314 		logp("unlink(%s) called from %s(): %s\n",
315 			path, func, strerror(errno));
316 		return -1;
317 	}
318 	return 0;
319 }
320 
init_max(const char * path,uint32_t * max,int what,uint32_t default_max)321 static void init_max(const char *path,
322 	uint32_t *max, int what, uint32_t default_max)
323 {
324 	*max=pathconf(path?path:".", what);
325 	if(*max<default_max) *max=default_max;
326 }
327 
init_fs_max(const char * path)328 int init_fs_max(const char *path)
329 {
330 	struct stat statp;
331 	if(stat(path, &statp))
332 	{
333 		logp("Path %s does not exist in %s\n", path, __func__);
334 		return -1;
335 	}
336 	// Get system path and filename maximum lengths.
337 	init_max(path, &fs_path_max, _PC_PATH_MAX, 1024);
338 	init_max(path, &fs_name_max, _PC_NAME_MAX, 255);
339 	fs_full_path_max=fs_path_max+fs_name_max;
340 	return 0;
341 }
342 
do_get_entries_in_directory(DIR * directory,char *** nl,int * count,int (* compar)(const void *,const void *))343 static int do_get_entries_in_directory(DIR *directory, char ***nl,
344 	int *count, int (*compar)(const void *, const void *))
345 {
346 	int allocated=0;
347 	char **ntmp=NULL;
348 	struct dirent *result=NULL;
349 
350 	*count=0;
351 
352 	// This here is doing a funky kind of scandir/alphasort
353 	// that can also run on Windows.
354 	while(1)
355 	{
356 		errno=0;
357 		if(!(result=readdir(directory)))
358 		{
359 			if(errno)
360 			{
361 				logp("error in readdir: %s\n",
362 					strerror(errno));
363 				goto error;
364 			}
365 			break;
366 		}
367 
368 		if(!filter_dot(result))
369 			continue;
370 
371 		if(*count==allocated)
372 		{
373 			if(!allocated) allocated=10;
374 			else allocated*=2;
375 
376 			if(!(ntmp=(char **)
377 			  realloc_w(*nl, allocated*sizeof(**nl), __func__)))
378 				goto error;
379 			*nl=ntmp;
380 		}
381 		if(!((*nl)[(*count)++]=strdup_w(result->d_name, __func__)))
382 			goto error;
383 	}
384 	if(*nl && compar)
385 		qsort(*nl, *count, sizeof(**nl), compar);
386 	return 0;
387 error:
388 	if(*nl)
389 	{
390 		int i;
391 		for(i=0; i<*count; i++)
392 			free_w(&((*nl)[i]));
393 		free_v((void **)nl);
394 	}
395 	return -1;
396 }
397 
entries_in_directory(const char * path,char *** nl,int * count,int atime,int follow_symlinks,int (* compar)(const char **,const char **))398 static int entries_in_directory(const char *path, char ***nl,
399 	int *count, int atime, int follow_symlinks,
400 	int (*compar)(const char **, const char **))
401 {
402 	int ret=0;
403 	DIR *directory=NULL;
404 
405 	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 		if(init_fs_max(path)) return -1;
411 	}
412 #if defined(O_DIRECTORY) && defined(O_NOATIME)
413 	int dfd=-1;
414 	if((dfd=open(path, O_RDONLY|O_DIRECTORY|(atime?0:O_NOATIME)
415 #ifdef O_NOFOLLOW
416 	  |(follow_symlinks?0:O_NOFOLLOW)
417 #endif
418 	  ))<0
419 	  || !(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 		if(do_get_entries_in_directory(directory, nl, count,
434 			(int (*)(const void *, const void *))compar))
435 				ret=-1;
436 	}
437 	if(directory) closedir(directory);
438 	return ret;
439 }
440 
filter_dot(const struct dirent * d)441 int filter_dot(const struct dirent *d)
442 {
443 	if(!d
444 	  || !strcmp(d->d_name, ".")
445 	  || !strcmp(d->d_name, ".."))
446 		return 0;
447 	return 1;
448 }
449 
my_alphasort(const char ** a,const char ** b)450 static int my_alphasort(const char **a, const char **b)
451 {
452 	return pathcmp(*a, *b);
453 }
454 
entries_in_directory_alphasort(const char * path,char *** nl,int * count,int atime,int follow_symlinks)455 int entries_in_directory_alphasort(const char *path, char ***nl,
456 	int *count, int atime, int follow_symlinks)
457 {
458 	return entries_in_directory(path, nl, count, atime, follow_symlinks,
459 		my_alphasort);
460 }
461 
462 #define FULL_CHUNK      4096
463 
files_equal(const char * opath,const char * npath,int compressed)464 int files_equal(const char *opath, const char *npath, int compressed)
465 {
466 	int ret=0;
467 	size_t ogot;
468 	size_t ngot;
469 	unsigned int i=0;
470 	struct fzp *ofp=NULL;
471 	struct fzp *nfp=NULL;
472 	static char obuf[FULL_CHUNK];
473 	static char nbuf[FULL_CHUNK];
474 
475 	if(compressed)
476 	{
477 		ofp=fzp_gzopen(opath, "rb");
478 		nfp=fzp_gzopen(npath, "rb");
479 	}
480 	else
481 	{
482 		ofp=fzp_open(opath, "rb");
483 		nfp=fzp_open(npath, "rb");
484 	}
485 
486 	if(!ofp && !nfp)
487 	{
488 		ret=1;
489 		goto end;
490 	}
491 	if(!ofp && nfp)
492 		goto end;
493 	if(!nfp && ofp)
494 		goto end;
495 
496 	while(1)
497 	{
498 		ogot=fzp_read(ofp, obuf, FULL_CHUNK);
499 		ngot=fzp_read(nfp, nbuf, FULL_CHUNK);
500 		if(ogot!=ngot)
501 			goto end;
502 		for(i=0; i<ogot; i++)
503 		{
504 			if(obuf[i]!=nbuf[i])
505 				goto end;
506 		}
507 		if(ogot<FULL_CHUNK)
508 			break;
509 	}
510 	ret=1;
511 end:
512 	fzp_close(&ofp);
513 	fzp_close(&nfp);
514 	return ret;
515 }
516 
517 #ifndef HAVE_WIN32
mksock(const char * path)518 int mksock(const char *path)
519 {
520 	int fd=-1;
521 	int ret=-1;
522 	struct sockaddr_un addr;
523 	memset(&addr, 0, sizeof(addr));
524 	addr.sun_family=AF_UNIX;
525 	strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
526 	if((fd=socket(addr.sun_family, SOCK_STREAM, 0))<0
527 	  || (bind(fd, (struct sockaddr *)&addr, sizeof(addr)))<0)
528 		goto end;
529 	ret=0;
530 end:
531 	if(fd>=0) close(fd);
532 	return ret;
533 }
534 
is_lnk_lstat(const char * path)535 int is_lnk_lstat(const char *path)
536 {
537 	struct stat buf;
538 	if(lstat(path, &buf))
539 		return -1;
540 	return S_ISLNK(buf.st_mode);
541 }
542 
is_lnk_valid(const char * path)543 int is_lnk_valid(const char *path)
544 {
545 	struct stat buf;
546 	if(stat(path, &buf))
547 		return 0;
548 	return 1;
549 }
550 
do_symlink(const char * oldpath,const char * newpath)551 int do_symlink(const char *oldpath, const char *newpath)
552 {
553 	if(!symlink(oldpath, newpath))
554 		return 0;
555 	logp("could not symlink '%s' to '%s': %s\n",
556 		newpath, oldpath, strerror(errno));
557 	return -1;
558 }
559 
do_readlink(const char * path,char buf[],size_t buflen)560 static int do_readlink(const char *path, char buf[], size_t buflen)
561 {
562 	ssize_t len;
563 	if((len=readlink(path, buf, buflen-1))<0)
564 		return -1;
565 	buf[len]='\0';
566 	return 0;
567 }
568 
readlink_w(const char * path,char buf[],size_t buflen)569 int readlink_w(const char *path, char buf[], size_t buflen)
570 {
571 	struct stat statp;
572 	if(lstat(path, &statp))
573 		return -1;
574 	if(S_ISLNK(statp.st_mode))
575 		return do_readlink(path, buf, buflen);
576 	return -1;
577 }
578 
readlink_w_in_dir(const char * dir,const char * lnk,char buf[],size_t buflen)579 int readlink_w_in_dir(const char *dir, const char *lnk,
580 	char buf[], size_t buflen)
581 {
582 	char *tmp=NULL;
583 	if(!(tmp=prepend_s(dir, lnk)))
584 		return -1;
585 	readlink_w(tmp, buf, buflen);
586 	free_w(&tmp);
587 	return 0;
588 }
589 
590 #endif
591