Fix significant security holes with "use chroot = no" in an rsync daemon:
[rsync/rsync.git] / util.c
CommitLineData
c627d613
AT
1/*
2 Copyright (C) Andrew Tridgell 1996
3 Copyright (C) Paul Mackerras 1996
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18*/
19
20/*
21 Utilities used in rsync
22
23 tridge, June 1996
24 */
25#include "rsync.h"
26
c7c11a0d
DD
27extern int verbose;
28
a5343e76
AT
29/****************************************************************************
30Set a fd into nonblocking mode. Uses POSIX O_NONBLOCK if available,
31else
32if SYSV use O_NDELAY
33if BSD use FNDELAY
34****************************************************************************/
35int set_nonblocking(int fd)
94481d91 36{
a5343e76
AT
37 int val;
38#ifdef O_NONBLOCK
39#define FLAG_TO_SET O_NONBLOCK
40#else
41#ifdef SYSV
42#define FLAG_TO_SET O_NDELAY
43#else /* BSD */
44#define FLAG_TO_SET FNDELAY
45#endif
46#endif
47
48 if((val = fcntl(fd, F_GETFL, 0)) == -1)
49 return -1;
50 val |= FLAG_TO_SET;
51 return fcntl( fd, F_SETFL, val);
52#undef FLAG_TO_SET
94481d91
AT
53}
54
d9bea2dd 55
c627d613
AT
56/* this is taken from CVS */
57int piped_child(char **command,int *f_in,int *f_out)
58{
59 int pid;
60 int to_child_pipe[2];
61 int from_child_pipe[2];
62
63 if (pipe(to_child_pipe) < 0 ||
64 pipe(from_child_pipe) < 0) {
9486289c 65 rprintf(FERROR,"pipe: %s\n",strerror(errno));
65417579 66 exit_cleanup(RERR_IPC);
c627d613
AT
67 }
68
69
3ba62a83 70 pid = do_fork();
c627d613 71 if (pid < 0) {
9486289c 72 rprintf(FERROR,"fork: %s\n",strerror(errno));
65417579 73 exit_cleanup(RERR_IPC);
c627d613
AT
74 }
75
76 if (pid == 0)
77 {
6574b4f7 78 extern int orig_umask;
c627d613
AT
79 if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
80 close(to_child_pipe[1]) < 0 ||
81 close(from_child_pipe[0]) < 0 ||
82 dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
9486289c 83 rprintf(FERROR,"Failed to dup/close : %s\n",strerror(errno));
65417579 84 exit_cleanup(RERR_IPC);
c627d613 85 }
773f2bd4
AT
86 if (to_child_pipe[0] != STDIN_FILENO) close(to_child_pipe[0]);
87 if (from_child_pipe[1] != STDOUT_FILENO) close(from_child_pipe[1]);
6574b4f7 88 umask(orig_umask);
c627d613 89 execvp(command[0], command);
9486289c 90 rprintf(FERROR,"Failed to exec %s : %s\n",
c627d613 91 command[0],strerror(errno));
65417579 92 exit_cleanup(RERR_IPC);
c627d613
AT
93 }
94
95 if (close(from_child_pipe[1]) < 0 ||
96 close(to_child_pipe[0]) < 0) {
9486289c 97 rprintf(FERROR,"Failed to close : %s\n",strerror(errno));
65417579 98 exit_cleanup(RERR_IPC);
c627d613
AT
99 }
100
101 *f_in = from_child_pipe[0];
102 *f_out = to_child_pipe[1];
3eb38818
AT
103
104 set_nonblocking(*f_in);
105 set_nonblocking(*f_out);
c627d613
AT
106
107 return pid;
108}
109
366345fe
AT
110int local_child(int argc, char **argv,int *f_in,int *f_out)
111{
112 int pid;
113 int to_child_pipe[2];
114 int from_child_pipe[2];
115
116 if (pipe(to_child_pipe) < 0 ||
117 pipe(from_child_pipe) < 0) {
9486289c 118 rprintf(FERROR,"pipe: %s\n",strerror(errno));
65417579 119 exit_cleanup(RERR_IPC);
366345fe
AT
120 }
121
122
123 pid = do_fork();
124 if (pid < 0) {
9486289c 125 rprintf(FERROR,"fork: %s\n",strerror(errno));
65417579 126 exit_cleanup(RERR_IPC);
366345fe
AT
127 }
128
129 if (pid == 0) {
130 extern int am_sender;
131 extern int am_server;
132
133 am_sender = !am_sender;
134 am_server = 1;
135
136 if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
137 close(to_child_pipe[1]) < 0 ||
138 close(from_child_pipe[0]) < 0 ||
139 dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
9486289c 140 rprintf(FERROR,"Failed to dup/close : %s\n",strerror(errno));
65417579 141 exit_cleanup(RERR_IPC);
366345fe
AT
142 }
143 if (to_child_pipe[0] != STDIN_FILENO) close(to_child_pipe[0]);
144 if (from_child_pipe[1] != STDOUT_FILENO) close(from_child_pipe[1]);
9486289c 145 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
366345fe
AT
146 }
147
148 if (close(from_child_pipe[1]) < 0 ||
149 close(to_child_pipe[0]) < 0) {
9486289c 150 rprintf(FERROR,"Failed to close : %s\n",strerror(errno));
65417579 151 exit_cleanup(RERR_IPC);
366345fe
AT
152 }
153
154 *f_in = from_child_pipe[0];
155 *f_out = to_child_pipe[1];
156
157 return pid;
158}
159
160
c627d613
AT
161
162void out_of_memory(char *str)
163{
9486289c 164 rprintf(FERROR,"ERROR: out of memory in %s\n",str);
65417579 165 exit_cleanup(RERR_MALLOC);
575f2fca
AT
166}
167
168void overflow(char *str)
169{
9486289c 170 rprintf(FERROR,"ERROR: buffer overflow in %s\n",str);
65417579 171 exit_cleanup(RERR_MALLOC);
c627d613
AT
172}
173
174
c627d613
AT
175
176int set_modtime(char *fname,time_t modtime)
177{
31e12522
AT
178 extern int dry_run;
179 if (dry_run) return 0;
180 {
1e9f155a 181#ifdef HAVE_UTIMBUF
31e12522
AT
182 struct utimbuf tbuf;
183 tbuf.actime = time(NULL);
184 tbuf.modtime = modtime;
185 return utime(fname,&tbuf);
c627d613 186#elif defined(HAVE_UTIME)
31e12522
AT
187 time_t t[2];
188 t[0] = time(NULL);
189 t[1] = modtime;
190 return utime(fname,t);
c627d613 191#else
31e12522
AT
192 struct timeval t[2];
193 t[0].tv_sec = time(NULL);
194 t[0].tv_usec = 0;
195 t[1].tv_sec = modtime;
196 t[1].tv_usec = 0;
197 return utimes(fname,t);
c627d613 198#endif
31e12522 199 }
c627d613 200}
94481d91 201
720b47f2 202
6574b4f7
AT
203/****************************************************************************
204create any necessary directories in fname. Unfortunately we don't know
205what perms to give the directory when this is called so we need to rely
206on the umask
207****************************************************************************/
208int create_directory_path(char *fname)
209{
210 extern int orig_umask;
211 char *p;
212
213 while (*fname == '/') fname++;
214 while (strncmp(fname,"./",2)==0) fname += 2;
215
216 p = fname;
217 while ((p=strchr(p,'/'))) {
218 *p = 0;
1b2d733a 219 do_mkdir(fname,0777 & ~orig_umask);
6574b4f7
AT
220 *p = '/';
221 p++;
222 }
223 return 0;
224}
950ab32d
AT
225
226
227/* Write LEN bytes at PTR to descriptor DESC, retrying if interrupted.
228 Return LEN upon success, write's (negative) error code otherwise.
229
230 derived from GNU C's cccp.c.
231*/
a5343e76 232static int full_write(int desc, char *ptr, int len)
950ab32d
AT
233{
234 int total_written;
235
236 total_written = 0;
237 while (len > 0) {
238 int written = write (desc, ptr, len);
239 if (written < 0) {
240#ifdef EINTR
241 if (errno == EINTR)
242 continue;
243#endif
244 return written;
245 }
246 total_written += written;
247 ptr += written;
248 len -= written;
249 }
250 return total_written;
251}
252
253/* Read LEN bytes at PTR from descriptor DESC, retrying if interrupted.
254 Return the actual number of bytes read, zero for EOF, or negative
255 for an error.
256
257 derived from GNU C's cccp.c. */
6e4fb64e 258static int safe_read(int desc, char *ptr, int len)
950ab32d
AT
259{
260 int n_chars;
261
262 if (len <= 0)
263 return len;
264
265#ifdef EINTR
266 do {
267 n_chars = read(desc, ptr, len);
268 } while (n_chars < 0 && errno == EINTR);
269#else
270 n_chars = read(desc, ptr, len);
271#endif
272
273 return n_chars;
274}
275
276
277/* copy a file - this is used in conjunction with the --temp-dir option */
278int copy_file(char *source, char *dest, mode_t mode)
279{
280 int ifd;
281 int ofd;
282 char buf[1024 * 8];
283 int len; /* Number of bytes read into `buf'. */
284
285 ifd = open(source, O_RDONLY);
286 if (ifd == -1) {
9486289c 287 rprintf(FERROR,"open %s: %s\n",
950ab32d
AT
288 source,strerror(errno));
289 return -1;
290 }
291
c7c11a0d 292 if (robust_unlink(dest) && errno != ENOENT) {
9486289c 293 rprintf(FERROR,"unlink %s: %s\n",
950ab32d
AT
294 dest,strerror(errno));
295 return -1;
296 }
297
31e12522 298 ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode);
c46ded46 299 if (ofd == -1) {
9486289c 300 rprintf(FERROR,"open %s: %s\n",
950ab32d
AT
301 dest,strerror(errno));
302 close(ifd);
303 return -1;
304 }
305
306 while ((len = safe_read(ifd, buf, sizeof(buf))) > 0) {
307 if (full_write(ofd, buf, len) < 0) {
9486289c 308 rprintf(FERROR,"write %s: %s\n",
950ab32d
AT
309 dest,strerror(errno));
310 close(ifd);
311 close(ofd);
312 return -1;
313 }
314 }
315
316 close(ifd);
317 close(ofd);
318
319 if (len < 0) {
9486289c 320 rprintf(FERROR,"read %s: %s\n",
950ab32d
AT
321 source,strerror(errno));
322 return -1;
323 }
324
325 return 0;
326}
feaa89c4 327
c7c11a0d
DD
328/*
329 Robust unlink: some OS'es (HPUX) refuse to unlink busy files, so
330 rename to <path>/.rsyncNNN instead. Note that successive rsync runs
331 will shuffle the filenames around a bit as long as the file is still
332 busy; this is because this function does not know if the unlink call
333 is due to a new file coming in, or --delete trying to remove old
334 .rsyncNNN files, hence it renames it each time.
335*/
336/* MAX_RENAMES should be 10**MAX_RENAMES_DIGITS */
337#define MAX_RENAMES_DIGITS 3
338#define MAX_RENAMES 1000
339
340int robust_unlink(char *fname)
341{
342#ifndef ETXTBSY
343 return do_unlink(fname);
344#else
345 static int counter = 1;
346 int rc, pos, start;
347 char path[MAXPATHLEN];
348
349 rc = do_unlink(fname);
350 if ((rc == 0) || (errno != ETXTBSY))
351 return rc;
352
353 strlcpy(path, fname, MAXPATHLEN);
354
355 pos = strlen(path);
356 while((path[--pos] != '/') && (pos >= 0))
357 ;
358 ++pos;
359 strlcpy(&path[pos], ".rsync", MAXPATHLEN-pos);
360 pos += sizeof(".rsync")-1;
361
362 if (pos > (MAXPATHLEN-MAX_RENAMES_DIGITS-1)) {
363 errno = ETXTBSY;
364 return -1;
365 }
366
367 /* start where the last one left off to reduce chance of clashes */
368 start = counter;
369 do {
370 sprintf(&path[pos], "%03d", counter);
371 if (++counter >= MAX_RENAMES)
372 counter = 1;
373 } while (((rc = access(path, 0)) == 0) && (counter != start));
374
375 if (verbose > 0)
376 rprintf(FINFO,"renaming %s to %s because of text busy\n",
377 fname, path);
378
379 /* maybe we should return rename()'s exit status? Nah. */
380 if (do_rename(fname, path) != 0) {
381 errno = ETXTBSY;
382 return -1;
383 }
384 return 0;
385#endif
386}
387
388int robust_rename(char *from, char *to)
389{
390#ifndef ETXTBSY
391 return do_rename(from, to);
392#else
393 int rc = do_rename(from, to);
394 if ((rc == 0) || (errno != ETXTBSY))
395 return rc;
396 if (robust_unlink(to) != 0)
397 return -1;
398 return do_rename(from, to);
399#endif
400 }
401
402
feaa89c4
AT
403/* sleep for a while via select */
404void u_sleep(int usec)
405{
406 struct timeval tv;
407
408 tv.tv_sec = 0;
409 tv.tv_usec = usec;
410 select(0, NULL, NULL, NULL, &tv);
411}
3ba62a83
AT
412
413
414static pid_t all_pids[10];
415static int num_pids;
416
417/* fork and record the pid of the child */
418pid_t do_fork(void)
419{
420 pid_t newpid = fork();
421
422 if (newpid) {
423 all_pids[num_pids++] = newpid;
424 }
425 return newpid;
426}
427
428/* kill all children */
429void kill_all(int sig)
430{
431 int i;
432 for (i=0;i<num_pids;i++) {
433 if (all_pids[i] != getpid())
434 kill(all_pids[i], sig);
435 }
436}
9486289c 437
8ef4ffd6
AT
438/* turn a user name into a uid */
439int name_to_uid(char *name, uid_t *uid)
440{
441 struct passwd *pass;
442 if (!name || !*name) return 0;
443 pass = getpwnam(name);
444 if (pass) {
445 *uid = pass->pw_uid;
446 return 1;
447 }
448 return 0;
449}
450
451/* turn a group name into a gid */
452int name_to_gid(char *name, gid_t *gid)
453{
454 struct group *grp;
455 if (!name || !*name) return 0;
456 grp = getgrnam(name);
457 if (grp) {
458 *gid = grp->gr_gid;
459 return 1;
460 }
461 return 0;
462}
463
ff8b29b8 464
31593dd6
AT
465/* lock a byte range in a open file */
466int lock_range(int fd, int offset, int len)
0c515f17 467{
31593dd6 468 struct flock lock;
0c515f17 469
31593dd6
AT
470 lock.l_type = F_WRLCK;
471 lock.l_whence = SEEK_SET;
472 lock.l_start = offset;
473 lock.l_len = len;
474 lock.l_pid = 0;
475
476 return fcntl(fd,F_SETLK,&lock) == 0;
0c515f17 477}
874895d5
AT
478
479
cb13abfe 480static void glob_expand_one(char *s, char **argv, int *argc, int maxargs)
874895d5 481{
932be9aa 482#if !(defined(HAVE_GLOB) && defined(HAVE_GLOB_H))
e42c9458 483 if (!*s) s = ".";
087bf010 484 argv[*argc] = strdup(s);
874895d5
AT
485 (*argc)++;
486 return;
487#else
cb13abfe 488 extern int sanitize_paths;
874895d5
AT
489 glob_t globbuf;
490 int i;
491
e42c9458
AT
492 if (!*s) s = ".";
493
cb13abfe
DD
494 argv[*argc] = strdup(s);
495 if (sanitize_paths) {
496 sanitize_path(argv[*argc], NULL);
497 }
087bf010 498
874895d5
AT
499 memset(&globbuf, 0, sizeof(globbuf));
500 glob(argv[*argc], 0, NULL, &globbuf);
501 if (globbuf.gl_pathc == 0) {
502 (*argc)++;
503 globfree(&globbuf);
504 return;
505 }
506 for (i=0; i<(maxargs - (*argc)) && i<globbuf.gl_pathc;i++) {
507 if (i == 0) free(argv[*argc]);
508 argv[(*argc) + i] = strdup(globbuf.gl_pathv[i]);
509 if (!argv[(*argc) + i]) out_of_memory("glob_expand");
510 }
511 globfree(&globbuf);
512 (*argc) += i;
513#endif
514}
5a96ee05 515
cb13abfe 516void glob_expand(char *base1, char **argv, int *argc, int maxargs)
087bf010
AT
517{
518 char *s = argv[*argc];
519 char *p, *q;
ba5e128d 520 char *base = base1;
087bf010
AT
521
522 if (!s || !*s) return;
523
e42c9458
AT
524 if (strncmp(s, base, strlen(base)) == 0) {
525 s += strlen(base);
526 }
527
087bf010
AT
528 s = strdup(s);
529 if (!s) out_of_memory("glob_expand");
530
ba5e128d
AT
531 base = (char *)malloc(strlen(base1)+3);
532 if (!base) out_of_memory("glob_expand");
533
534 sprintf(base," %s/", base1);
535
087bf010
AT
536 q = s;
537 while ((p = strstr(q,base)) && ((*argc) < maxargs)) {
ba5e128d
AT
538 /* split it at this point */
539 *p = 0;
cb13abfe 540 glob_expand_one(q, argv, argc, maxargs);
ba5e128d 541 q = p+strlen(base);
087bf010
AT
542 }
543
cb13abfe 544 if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs);
087bf010
AT
545
546 free(s);
ba5e128d 547 free(base);
087bf010 548}
5a96ee05
AT
549
550/*******************************************************************
551 convert a string to lower case
552********************************************************************/
553void strlower(char *s)
554{
555 while (*s) {
556 if (isupper(*s)) *s = tolower(*s);
557 s++;
558 }
559}
e42c9458 560
37f9805d
AT
561/* this is like vsnprintf but it always null terminates, so you
562 can fit at most n-1 chars in */
e42c9458
AT
563int vslprintf(char *str, int n, const char *format, va_list ap)
564{
e42c9458 565 int ret = vsnprintf(str, n, format, ap);
37f9805d
AT
566 if (ret >= n || ret < 0) {
567 str[n-1] = 0;
e42c9458
AT
568 return -1;
569 }
570 str[ret] = 0;
571 return ret;
e42c9458
AT
572}
573
574
575/* like snprintf but always null terminates */
576int slprintf(char *str, int n, char *format, ...)
577{
578 va_list ap;
579 int ret;
580
581 va_start(ap, format);
582 ret = vslprintf(str,n,format,ap);
583 va_end(ap);
584 return ret;
585}
8d9dc9f9 586
fe8c0a98
AT
587
588void *Realloc(void *p, int size)
589{
590 if (!p) return (void *)malloc(size);
591 return (void *)realloc(p, size);
592}
5243c216
AT
593
594
595void clean_fname(char *name)
596{
597 char *p;
598 int l;
599 int modified = 1;
600
601 if (!name) return;
602
603 while (modified) {
604 modified = 0;
605
606 if ((p=strstr(name,"/./"))) {
607 modified = 1;
608 while (*p) {
609 p[0] = p[2];
610 p++;
611 }
612 }
613
614 if ((p=strstr(name,"//"))) {
615 modified = 1;
616 while (*p) {
617 p[0] = p[1];
618 p++;
619 }
620 }
621
622 if (strncmp(p=name,"./",2) == 0) {
623 modified = 1;
624 do {
625 p[0] = p[2];
626 } while (*p++);
627 }
628
629 l = strlen(p=name);
630 if (l > 1 && p[l-1] == '/') {
631 modified = 1;
632 p[l-1] = 0;
633 }
634 }
635}
636
1b8e662a
DD
637/*
638 * Make path appear as if a chroot had occurred:
1b8e662a 639 * 1. remove leading "/" (or replace with "." if at end)
cb13abfe 640 * 2. remove leading ".." components (except those allowed by "reldir")
1b8e662a 641 * 3. delete any other "<dir>/.." (recursively)
cb13abfe
DD
642 * If "reldir" is non-null, it is a sanitized directory that the path will be
643 * relative to, so allow as many ".." at the beginning of the path as
644 * there are components in reldir.
b5f9e67d
DD
645 * While we're at it, remove double slashes and "." components like
646 * clean_fname does(), but DON'T remove a trailing slash because that
647 * is sometimes significant on command line arguments.
44e2e578 648 * Can only shrink paths, so sanitizes in place.
1b8e662a
DD
649 * Contributed by Dave Dykstra <dwd@bell-labs.com>
650 */
651
cb13abfe 652void sanitize_path(char *p, char *reldir)
1b8e662a 653{
44e2e578 654 char *start, *sanp;
cb13abfe
DD
655 int depth = 0;
656 int allowdotdot = 0;
657
658 if (reldir) {
659 depth++;
660 while (*reldir) {
661 if (*reldir++ == '/') {
662 depth++;
663 }
664 }
665 }
44e2e578
DD
666 start = p;
667 sanp = p;
b5f9e67d
DD
668 while (*p == '/') {
669 /* remove leading slashes */
670 p++;
671 }
1b8e662a 672 while (*p != '\0') {
b5f9e67d 673 /* this loop iterates once per filename component in p.
44e2e578 674 * both p (and sanp if the original had a slash) should
b5f9e67d
DD
675 * always be left pointing after a slash
676 */
677 if ((*p == '.') && ((*(p+1) == '/') || (*(p+1) == '\0'))) {
678 /* skip "." component */
679 while (*++p == '/') {
680 /* skip following slashes */
681 ;
682 }
cb13abfe
DD
683 continue;
684 }
685 allowdotdot = 0;
686 if ((*p == '.') && (*(p+1) == '.') &&
1b8e662a 687 ((*(p+2) == '/') || (*(p+2) == '\0'))) {
cb13abfe
DD
688 /* ".." component followed by slash or end */
689 if ((depth > 0) && (sanp == start)) {
690 /* allow depth levels of .. at the beginning */
691 --depth;
692 allowdotdot = 1;
693 } else {
694 p += 2;
695 if (*p == '/')
696 p++;
697 if (sanp != start) {
698 /* back up sanp one level */
699 --sanp; /* now pointing at slash */
700 while ((sanp > start) && (*(sanp - 1) != '/')) {
701 /* skip back up to slash */
702 sanp--;
703 }
b5f9e67d 704 }
cb13abfe 705 continue;
1b8e662a 706 }
cb13abfe
DD
707 }
708 while (1) {
709 /* copy one component through next slash */
710 *sanp++ = *p++;
711 if ((*p == '\0') || (*(p-1) == '/')) {
712 while (*p == '/') {
713 /* skip multiple slashes */
714 p++;
b5f9e67d 715 }
cb13abfe 716 break;
1b8e662a
DD
717 }
718 }
cb13abfe
DD
719 if (allowdotdot) {
720 /* move the virtual beginning to leave the .. alone */
721 start = sanp;
722 }
1b8e662a 723 }
cb13abfe 724 if ((sanp == start) && !allowdotdot) {
b5f9e67d 725 /* ended up with nothing, so put in "." component */
44e2e578 726 *sanp++ = '.';
b5f9e67d 727 }
44e2e578 728 *sanp = '\0';
1b8e662a
DD
729}
730
5243c216
AT
731
732static char curr_dir[MAXPATHLEN];
733
734/* like chdir() but can be reversed with pop_dir() if save is set. It
735 is also much faster as it remembers where we have been */
736char *push_dir(char *dir, int save)
737{
738 char *ret = curr_dir;
739 static int initialised;
740
741 if (!initialised) {
742 initialised = 1;
743 getcwd(curr_dir, sizeof(curr_dir)-1);
744 }
745
c226b7c2
DD
746 if (!dir) return NULL; /* this call was probably just to initialize */
747
5243c216
AT
748 if (chdir(dir)) return NULL;
749
750 if (save) {
751 ret = strdup(curr_dir);
752 }
753
754 if (*dir == '/') {
37f9805d 755 strlcpy(curr_dir, dir, sizeof(curr_dir));
5243c216 756 } else {
37f9805d
AT
757 strlcat(curr_dir,"/", sizeof(curr_dir));
758 strlcat(curr_dir,dir, sizeof(curr_dir));
5243c216
AT
759 }
760
761 clean_fname(curr_dir);
762
763 return ret;
764}
765
766/* reverse a push_dir call */
767int pop_dir(char *dir)
768{
769 int ret;
770
771 ret = chdir(dir);
772 if (ret) {
773 free(dir);
774 return ret;
775 }
776
37f9805d 777 strlcpy(curr_dir, dir, sizeof(curr_dir));
5243c216
AT
778
779 free(dir);
780
781 return 0;
782}
aa9b77a5
AT
783
784/* we need to supply our own strcmp function for file list comparisons
785 to ensure that signed/unsigned usage is consistent between machines. */
786int u_strcmp(const char *cs1, const char *cs2)
787{
5a788ade
AT
788 const uchar *s1 = (const uchar *)cs1;
789 const uchar *s2 = (const uchar *)cs2;
aa9b77a5
AT
790
791 while (*s1 && *s2 && (*s1 == *s2)) {
792 s1++; s2++;
793 }
794
795 return (int)*s1 - (int)*s2;
796}
eb86d661 797
43b06eea 798static OFF_T last_ofs;
eb86d661
AT
799
800void end_progress(void)
801{
802 extern int do_progress, am_server;
803
804 if (do_progress && !am_server) {
805 rprintf(FINFO,"\n");
806 }
43b06eea 807 last_ofs = 0;
eb86d661
AT
808}
809
810void show_progress(OFF_T ofs, OFF_T size)
811{
812 extern int do_progress, am_server;
813
814 if (do_progress && !am_server) {
43b06eea
AT
815 if (ofs > last_ofs + 1000) {
816 int pct = (int)((100.0*ofs)/size);
eb86d661 817 rprintf(FINFO,"%.0f (%d%%)\r", (double)ofs, pct);
43b06eea 818 last_ofs = ofs;
eb86d661
AT
819 }
820 }
821}
4b957c22
AT
822
823/* determine if a symlink points outside the current directory tree */
824int unsafe_symlink(char *dest, char *src)
825{
826 char *tok;
827 int depth = 0;
828
829 /* all absolute and null symlinks are unsafe */
830 if (!dest || !(*dest) || (*dest == '/')) return 1;
831
832 src = strdup(src);
833 if (!src) out_of_memory("unsafe_symlink");
834
835 /* find out what our safety margin is */
836 for (tok=strtok(src,"/"); tok; tok=strtok(NULL,"/")) {
837 if (strcmp(tok,"..") == 0) {
838 depth=0;
839 } else if (strcmp(tok,".") == 0) {
840 /* nothing */
841 } else {
842 depth++;
843 }
844 }
845 free(src);
846
847 /* drop by one to account for the filename portion */
848 depth--;
849
850 dest = strdup(dest);
851 if (!dest) out_of_memory("unsafe_symlink");
852
853 for (tok=strtok(dest,"/"); tok; tok=strtok(NULL,"/")) {
854 if (strcmp(tok,"..") == 0) {
855 depth--;
856 } else if (strcmp(tok,".") == 0) {
857 /* nothing */
858 } else {
859 depth++;
860 }
861 /* if at any point we go outside the current directory then
862 stop - it is unsafe */
863 if (depth < 0) break;
864 }
865
866 free(dest);
867 return (depth < 0);
868}
375a4556 869
f7632fc6
AT
870
871/****************************************************************************
872 return the date and time as a string
873****************************************************************************/
874char *timestring(time_t t)
875{
876 static char TimeBuf[200];
877 struct tm *tm = localtime(&t);
878
879#ifdef HAVE_STRFTIME
880 strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %T",tm);
881#else
37f9805d 882 strlcpy(TimeBuf, asctime(tm), sizeof(TimeBuf));
f7632fc6
AT
883#endif
884
885 if (TimeBuf[strlen(TimeBuf)-1] == '\n') {
886 TimeBuf[strlen(TimeBuf)-1] = 0;
887 }
888
889 return(TimeBuf);
890}
891