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