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