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