continue calling waitpid() while still reapingchildren (patch from
[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
b389939f 480static void glob_expand_one(char *s, char **argv, int *argc, int maxargs, int sanitize_paths)
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
488 glob_t globbuf;
489 int i;
490
e42c9458
AT
491 if (!*s) s = ".";
492
44e2e578
DD
493 s = strdup(s);
494 sanitize_path(s);
495 argv[*argc] = s;
087bf010 496
874895d5
AT
497 memset(&globbuf, 0, sizeof(globbuf));
498 glob(argv[*argc], 0, NULL, &globbuf);
499 if (globbuf.gl_pathc == 0) {
500 (*argc)++;
501 globfree(&globbuf);
502 return;
503 }
504 for (i=0; i<(maxargs - (*argc)) && i<globbuf.gl_pathc;i++) {
505 if (i == 0) free(argv[*argc]);
506 argv[(*argc) + i] = strdup(globbuf.gl_pathv[i]);
507 if (!argv[(*argc) + i]) out_of_memory("glob_expand");
508 }
509 globfree(&globbuf);
510 (*argc) += i;
511#endif
512}
5a96ee05 513
b389939f 514void glob_expand(char *base1, char **argv, int *argc, int maxargs, int sanitize_paths)
087bf010
AT
515{
516 char *s = argv[*argc];
517 char *p, *q;
ba5e128d 518 char *base = base1;
087bf010
AT
519
520 if (!s || !*s) return;
521
e42c9458
AT
522 if (strncmp(s, base, strlen(base)) == 0) {
523 s += strlen(base);
524 }
525
087bf010
AT
526 s = strdup(s);
527 if (!s) out_of_memory("glob_expand");
528
ba5e128d
AT
529 base = (char *)malloc(strlen(base1)+3);
530 if (!base) out_of_memory("glob_expand");
531
532 sprintf(base," %s/", base1);
533
087bf010
AT
534 q = s;
535 while ((p = strstr(q,base)) && ((*argc) < maxargs)) {
ba5e128d
AT
536 /* split it at this point */
537 *p = 0;
b389939f 538 glob_expand_one(q, argv, argc, maxargs, sanitize_paths);
ba5e128d 539 q = p+strlen(base);
087bf010
AT
540 }
541
b389939f 542 if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs, sanitize_paths);
087bf010
AT
543
544 free(s);
ba5e128d 545 free(base);
087bf010 546}
5a96ee05
AT
547
548/*******************************************************************
549 convert a string to lower case
550********************************************************************/
551void strlower(char *s)
552{
553 while (*s) {
554 if (isupper(*s)) *s = tolower(*s);
555 s++;
556 }
557}
e42c9458 558
37f9805d
AT
559/* this is like vsnprintf but it always null terminates, so you
560 can fit at most n-1 chars in */
e42c9458
AT
561int vslprintf(char *str, int n, const char *format, va_list ap)
562{
e42c9458 563 int ret = vsnprintf(str, n, format, ap);
37f9805d
AT
564 if (ret >= n || ret < 0) {
565 str[n-1] = 0;
e42c9458
AT
566 return -1;
567 }
568 str[ret] = 0;
569 return ret;
e42c9458
AT
570}
571
572
573/* like snprintf but always null terminates */
574int slprintf(char *str, int n, char *format, ...)
575{
576 va_list ap;
577 int ret;
578
579 va_start(ap, format);
580 ret = vslprintf(str,n,format,ap);
581 va_end(ap);
582 return ret;
583}
8d9dc9f9 584
fe8c0a98
AT
585
586void *Realloc(void *p, int size)
587{
588 if (!p) return (void *)malloc(size);
589 return (void *)realloc(p, size);
590}
5243c216
AT
591
592
593void clean_fname(char *name)
594{
595 char *p;
596 int l;
597 int modified = 1;
598
599 if (!name) return;
600
601 while (modified) {
602 modified = 0;
603
604 if ((p=strstr(name,"/./"))) {
605 modified = 1;
606 while (*p) {
607 p[0] = p[2];
608 p++;
609 }
610 }
611
612 if ((p=strstr(name,"//"))) {
613 modified = 1;
614 while (*p) {
615 p[0] = p[1];
616 p++;
617 }
618 }
619
620 if (strncmp(p=name,"./",2) == 0) {
621 modified = 1;
622 do {
623 p[0] = p[2];
624 } while (*p++);
625 }
626
627 l = strlen(p=name);
628 if (l > 1 && p[l-1] == '/') {
629 modified = 1;
630 p[l-1] = 0;
631 }
632 }
633}
634
1b8e662a
DD
635/*
636 * Make path appear as if a chroot had occurred:
1b8e662a
DD
637 * 1. remove leading "/" (or replace with "." if at end)
638 * 2. remove leading ".." components
639 * 3. delete any other "<dir>/.." (recursively)
b5f9e67d
DD
640 * While we're at it, remove double slashes and "." components like
641 * clean_fname does(), but DON'T remove a trailing slash because that
642 * is sometimes significant on command line arguments.
44e2e578 643 * Can only shrink paths, so sanitizes in place.
1b8e662a
DD
644 * Contributed by Dave Dykstra <dwd@bell-labs.com>
645 */
646
44e2e578 647void sanitize_path(char *p)
1b8e662a 648{
44e2e578 649 char *start, *sanp;
1b8e662a 650
44e2e578
DD
651 start = p;
652 sanp = p;
b5f9e67d
DD
653 while (*p == '/') {
654 /* remove leading slashes */
655 p++;
656 }
1b8e662a 657 while (*p != '\0') {
b5f9e67d 658 /* this loop iterates once per filename component in p.
44e2e578 659 * both p (and sanp if the original had a slash) should
b5f9e67d
DD
660 * always be left pointing after a slash
661 */
662 if ((*p == '.') && ((*(p+1) == '/') || (*(p+1) == '\0'))) {
663 /* skip "." component */
664 while (*++p == '/') {
665 /* skip following slashes */
666 ;
667 }
668 } else if ((*p == '.') && (*(p+1) == '.') &&
1b8e662a 669 ((*(p+2) == '/') || (*(p+2) == '\0'))) {
b5f9e67d 670 /* skip ".." component followed by slash or end */
1b8e662a 671 p += 2;
b5f9e67d
DD
672 if (*p == '/')
673 p++;
44e2e578
DD
674 if (sanp != start) {
675 /* back up sanp one level */
676 --sanp; /* now pointing at slash */
677 while ((sanp > start) && (*(sanp - 1) != '/')) {
b5f9e67d 678 /* skip back up to slash */
44e2e578 679 sanp--;
b5f9e67d 680 }
1b8e662a
DD
681 }
682 } else {
1b8e662a 683 while (1) {
b5f9e67d 684 /* copy one component through next slash */
44e2e578 685 *sanp++ = *p++;
b5f9e67d
DD
686 if ((*p == '\0') || (*(p-1) == '/')) {
687 while (*p == '/') {
688 /* skip multiple slashes */
689 p++;
690 }
1b8e662a 691 break;
b5f9e67d 692 }
1b8e662a
DD
693 }
694 }
695 }
44e2e578 696 if (sanp == start) {
b5f9e67d 697 /* ended up with nothing, so put in "." component */
44e2e578 698 *sanp++ = '.';
b5f9e67d 699 }
44e2e578 700 *sanp = '\0';
1b8e662a
DD
701}
702
5243c216
AT
703
704static char curr_dir[MAXPATHLEN];
705
706/* like chdir() but can be reversed with pop_dir() if save is set. It
707 is also much faster as it remembers where we have been */
708char *push_dir(char *dir, int save)
709{
710 char *ret = curr_dir;
711 static int initialised;
712
713 if (!initialised) {
714 initialised = 1;
715 getcwd(curr_dir, sizeof(curr_dir)-1);
716 }
717
c226b7c2
DD
718 if (!dir) return NULL; /* this call was probably just to initialize */
719
5243c216
AT
720 if (chdir(dir)) return NULL;
721
722 if (save) {
723 ret = strdup(curr_dir);
724 }
725
726 if (*dir == '/') {
37f9805d 727 strlcpy(curr_dir, dir, sizeof(curr_dir));
5243c216 728 } else {
37f9805d
AT
729 strlcat(curr_dir,"/", sizeof(curr_dir));
730 strlcat(curr_dir,dir, sizeof(curr_dir));
5243c216
AT
731 }
732
733 clean_fname(curr_dir);
734
735 return ret;
736}
737
738/* reverse a push_dir call */
739int pop_dir(char *dir)
740{
741 int ret;
742
743 ret = chdir(dir);
744 if (ret) {
745 free(dir);
746 return ret;
747 }
748
37f9805d 749 strlcpy(curr_dir, dir, sizeof(curr_dir));
5243c216
AT
750
751 free(dir);
752
753 return 0;
754}
aa9b77a5
AT
755
756/* we need to supply our own strcmp function for file list comparisons
757 to ensure that signed/unsigned usage is consistent between machines. */
758int u_strcmp(const char *cs1, const char *cs2)
759{
5a788ade
AT
760 const uchar *s1 = (const uchar *)cs1;
761 const uchar *s2 = (const uchar *)cs2;
aa9b77a5
AT
762
763 while (*s1 && *s2 && (*s1 == *s2)) {
764 s1++; s2++;
765 }
766
767 return (int)*s1 - (int)*s2;
768}
eb86d661 769
43b06eea 770static OFF_T last_ofs;
eb86d661
AT
771
772void end_progress(void)
773{
774 extern int do_progress, am_server;
775
776 if (do_progress && !am_server) {
777 rprintf(FINFO,"\n");
778 }
43b06eea 779 last_ofs = 0;
eb86d661
AT
780}
781
782void show_progress(OFF_T ofs, OFF_T size)
783{
784 extern int do_progress, am_server;
785
786 if (do_progress && !am_server) {
43b06eea
AT
787 if (ofs > last_ofs + 1000) {
788 int pct = (int)((100.0*ofs)/size);
eb86d661 789 rprintf(FINFO,"%.0f (%d%%)\r", (double)ofs, pct);
43b06eea 790 last_ofs = ofs;
eb86d661
AT
791 }
792 }
793}
4b957c22
AT
794
795/* determine if a symlink points outside the current directory tree */
796int unsafe_symlink(char *dest, char *src)
797{
798 char *tok;
799 int depth = 0;
800
801 /* all absolute and null symlinks are unsafe */
802 if (!dest || !(*dest) || (*dest == '/')) return 1;
803
804 src = strdup(src);
805 if (!src) out_of_memory("unsafe_symlink");
806
807 /* find out what our safety margin is */
808 for (tok=strtok(src,"/"); tok; tok=strtok(NULL,"/")) {
809 if (strcmp(tok,"..") == 0) {
810 depth=0;
811 } else if (strcmp(tok,".") == 0) {
812 /* nothing */
813 } else {
814 depth++;
815 }
816 }
817 free(src);
818
819 /* drop by one to account for the filename portion */
820 depth--;
821
822 dest = strdup(dest);
823 if (!dest) out_of_memory("unsafe_symlink");
824
825 for (tok=strtok(dest,"/"); tok; tok=strtok(NULL,"/")) {
826 if (strcmp(tok,"..") == 0) {
827 depth--;
828 } else if (strcmp(tok,".") == 0) {
829 /* nothing */
830 } else {
831 depth++;
832 }
833 /* if at any point we go outside the current directory then
834 stop - it is unsafe */
835 if (depth < 0) break;
836 }
837
838 free(dest);
839 return (depth < 0);
840}
375a4556 841
f7632fc6
AT
842
843/****************************************************************************
844 return the date and time as a string
845****************************************************************************/
846char *timestring(time_t t)
847{
848 static char TimeBuf[200];
849 struct tm *tm = localtime(&t);
850
851#ifdef HAVE_STRFTIME
852 strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %T",tm);
853#else
37f9805d 854 strlcpy(TimeBuf, asctime(tm), sizeof(TimeBuf));
f7632fc6
AT
855#endif
856
857 if (TimeBuf[strlen(TimeBuf)-1] == '\n') {
858 TimeBuf[strlen(TimeBuf)-1] = 0;
859 }
860
861 return(TimeBuf);
862}
863