Correct over-zealous edit for UNICOS.
[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
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
28extern int verbose;
29
30
31/****************************************************************************
32Set a fd into nonblocking mode
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
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
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 */
67int fd_pair(int fd[2])
68{
69 int ret;
70
71#if HAVE_SOCKETPAIR
72 ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
73#else
74 ret = pipe(fd);
75#endif
76
77 if (ret == 0) {
78 set_nonblocking(fd[0]);
79 set_nonblocking(fd[1]);
80 }
81
82 return ret;
83}
84
85
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 */
96pid_t piped_child(char **command,int *f_in,int *f_out)
97{
98 pid_t pid;
99 int to_child_pipe[2];
100 int from_child_pipe[2];
101 extern int blocking_io;
102
103 if (fd_pair(to_child_pipe) < 0 ||
104 fd_pair(from_child_pipe) < 0) {
105 rprintf(FERROR,"pipe: %s\n",strerror(errno));
106 exit_cleanup(RERR_IPC);
107 }
108
109
110 pid = do_fork();
111 if (pid == -1) {
112 rprintf(FERROR,"fork: %s\n",strerror(errno));
113 exit_cleanup(RERR_IPC);
114 }
115
116 if (pid == 0)
117 {
118 extern int orig_umask;
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) {
123 rprintf(FERROR,"Failed to dup/close : %s\n",strerror(errno));
124 exit_cleanup(RERR_IPC);
125 }
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]);
128 umask(orig_umask);
129 set_blocking(STDIN_FILENO);
130 if (blocking_io) {
131 set_blocking(STDOUT_FILENO);
132 }
133 execvp(command[0], command);
134 rprintf(FERROR,"Failed to exec %s : %s\n",
135 command[0],strerror(errno));
136 exit_cleanup(RERR_IPC);
137 }
138
139 if (close(from_child_pipe[1]) < 0 ||
140 close(to_child_pipe[0]) < 0) {
141 rprintf(FERROR,"Failed to close : %s\n",strerror(errno));
142 exit_cleanup(RERR_IPC);
143 }
144
145 *f_in = from_child_pipe[0];
146 *f_out = to_child_pipe[1];
147
148 return pid;
149}
150
151pid_t local_child(int argc, char **argv,int *f_in,int *f_out)
152{
153 pid_t pid;
154 int to_child_pipe[2];
155 int from_child_pipe[2];
156
157 if (fd_pair(to_child_pipe) < 0 ||
158 fd_pair(from_child_pipe) < 0) {
159 rprintf(FERROR,"pipe: %s\n",strerror(errno));
160 exit_cleanup(RERR_IPC);
161 }
162
163
164 pid = do_fork();
165 if (pid == -1) {
166 rprintf(FERROR,"fork: %s\n",strerror(errno));
167 exit_cleanup(RERR_IPC);
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) {
181 rprintf(FERROR,"Failed to dup/close : %s\n",strerror(errno));
182 exit_cleanup(RERR_IPC);
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]);
186 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
187 }
188
189 if (close(from_child_pipe[1]) < 0 ||
190 close(to_child_pipe[0]) < 0) {
191 rprintf(FERROR,"Failed to close : %s\n",strerror(errno));
192 exit_cleanup(RERR_IPC);
193 }
194
195 *f_in = from_child_pipe[0];
196 *f_out = to_child_pipe[1];
197
198 return pid;
199}
200
201
202
203void out_of_memory(char *str)
204{
205 rprintf(FERROR,"ERROR: out of memory in %s\n",str);
206 exit_cleanup(RERR_MALLOC);
207}
208
209void overflow(char *str)
210{
211 rprintf(FERROR,"ERROR: buffer overflow in %s\n",str);
212 exit_cleanup(RERR_MALLOC);
213}
214
215
216
217int set_modtime(char *fname,time_t modtime)
218{
219 extern int dry_run;
220 if (dry_run) return 0;
221 {
222#ifdef HAVE_UTIMBUF
223 struct utimbuf tbuf;
224 tbuf.actime = time(NULL);
225 tbuf.modtime = modtime;
226 return utime(fname,&tbuf);
227#elif defined(HAVE_UTIME)
228 time_t t[2];
229 t[0] = time(NULL);
230 t[1] = modtime;
231 return utime(fname,t);
232#else
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);
239#endif
240 }
241}
242
243
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;
260 do_mkdir(fname,0777 & ~orig_umask);
261 *p = '/';
262 p++;
263 }
264 return 0;
265}
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*/
273static int full_write(int desc, char *ptr, int len)
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. */
299static int safe_read(int desc, char *ptr, int len)
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
326 ifd = do_open(source, O_RDONLY, 0);
327 if (ifd == -1) {
328 rprintf(FERROR,"open %s: %s\n",
329 source,strerror(errno));
330 return -1;
331 }
332
333 if (robust_unlink(dest) && errno != ENOENT) {
334 rprintf(FERROR,"unlink %s: %s\n",
335 dest,strerror(errno));
336 return -1;
337 }
338
339 ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode);
340 if (ofd == -1) {
341 rprintf(FERROR,"open %s: %s\n",
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) {
349 rprintf(FERROR,"write %s: %s\n",
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) {
361 rprintf(FERROR,"read %s: %s\n",
362 source,strerror(errno));
363 return -1;
364 }
365
366 return 0;
367}
368
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
441}
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}
467
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
494
495/* lock a byte range in a open file */
496int lock_range(int fd, int offset, int len)
497{
498 struct flock lock;
499
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;
507}
508
509
510static void glob_expand_one(char *s, char **argv, int *argc, int maxargs)
511{
512#if !(defined(HAVE_GLOB) && defined(HAVE_GLOB_H))
513 if (!*s) s = ".";
514 argv[*argc] = strdup(s);
515 (*argc)++;
516 return;
517#else
518 extern int sanitize_paths;
519 glob_t globbuf;
520 int i;
521
522 if (!*s) s = ".";
523
524 argv[*argc] = strdup(s);
525 if (sanitize_paths) {
526 sanitize_path(argv[*argc], NULL);
527 }
528
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}
545
546void glob_expand(char *base1, char **argv, int *argc, int maxargs)
547{
548 char *s = argv[*argc];
549 char *p, *q;
550 char *base = base1;
551
552 if (!s || !*s) return;
553
554 if (strncmp(s, base, strlen(base)) == 0) {
555 s += strlen(base);
556 }
557
558 s = strdup(s);
559 if (!s) out_of_memory("glob_expand");
560
561 if (asprintf(&base," %s/", base1) <= 0) out_of_memory("glob_expand");
562
563 q = s;
564 while ((p = strstr(q,base)) && ((*argc) < maxargs)) {
565 /* split it at this point */
566 *p = 0;
567 glob_expand_one(q, argv, argc, maxargs);
568 q = p+strlen(base);
569 }
570
571 if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs);
572
573 free(s);
574 free(base);
575}
576
577/*******************************************************************
578 convert a string to lower case
579********************************************************************/
580void strlower(char *s)
581{
582 while (*s) {
583 if (isupper(*s)) *s = tolower(*s);
584 s++;
585 }
586}
587
588void *Realloc(void *p, int size)
589{
590 if (!p) return (void *)malloc(size);
591 return (void *)realloc(p, size);
592}
593
594
595void clean_fname(char *name)
596{
597 char *p;
598 int l;
599 int modified = 1;
600
601 if (!name) return;
602
603 while (modified) {
604 modified = 0;
605
606 if ((p=strstr(name,"/./"))) {
607 modified = 1;
608 while (*p) {
609 p[0] = p[2];
610 p++;
611 }
612 }
613
614 if ((p=strstr(name,"//"))) {
615 modified = 1;
616 while (*p) {
617 p[0] = p[1];
618 p++;
619 }
620 }
621
622 if (strncmp(p=name,"./",2) == 0) {
623 modified = 1;
624 do {
625 p[0] = p[2];
626 } while (*p++);
627 }
628
629 l = strlen(p=name);
630 if (l > 1 && p[l-1] == '/') {
631 modified = 1;
632 p[l-1] = 0;
633 }
634 }
635}
636
637/*
638 * Make path appear as if a chroot had occurred:
639 * 1. remove leading "/" (or replace with "." if at end)
640 * 2. remove leading ".." components (except those allowed by "reldir")
641 * 3. delete any other "<dir>/.." (recursively)
642 * Can only shrink paths, so sanitizes in place.
643 * While we're at it, remove double slashes and "." components like
644 * clean_fname does(), but DON'T remove a trailing slash because that
645 * is sometimes significant on command line arguments.
646 * If "reldir" is non-null, it is a sanitized directory that the path will be
647 * relative to, so allow as many ".." at the beginning of the path as
648 * there are components in reldir. This is used for symbolic link targets.
649 * If reldir is non-null and the path began with "/", to be completely like
650 * a chroot we should add in depth levels of ".." at the beginning of the
651 * path, but that would blow the assumption that the path doesn't grow and
652 * it is not likely to end up being a valid symlink anyway, so just do
653 * the normal removal of the leading "/" instead.
654 * Contributed by Dave Dykstra <dwd@bell-labs.com>
655 */
656
657void sanitize_path(char *p, char *reldir)
658{
659 char *start, *sanp;
660 int depth = 0;
661 int allowdotdot = 0;
662
663 if (reldir) {
664 depth++;
665 while (*reldir) {
666 if (*reldir++ == '/') {
667 depth++;
668 }
669 }
670 }
671 start = p;
672 sanp = p;
673 while (*p == '/') {
674 /* remove leading slashes */
675 p++;
676 }
677 while (*p != '\0') {
678 /* this loop iterates once per filename component in p.
679 * both p (and sanp if the original had a slash) should
680 * always be left pointing after a slash
681 */
682 if ((*p == '.') && ((*(p+1) == '/') || (*(p+1) == '\0'))) {
683 /* skip "." component */
684 while (*++p == '/') {
685 /* skip following slashes */
686 ;
687 }
688 continue;
689 }
690 allowdotdot = 0;
691 if ((*p == '.') && (*(p+1) == '.') &&
692 ((*(p+2) == '/') || (*(p+2) == '\0'))) {
693 /* ".." component followed by slash or end */
694 if ((depth > 0) && (sanp == start)) {
695 /* allow depth levels of .. at the beginning */
696 --depth;
697 allowdotdot = 1;
698 } else {
699 p += 2;
700 if (*p == '/')
701 p++;
702 if (sanp != start) {
703 /* back up sanp one level */
704 --sanp; /* now pointing at slash */
705 while ((sanp > start) && (*(sanp - 1) != '/')) {
706 /* skip back up to slash */
707 sanp--;
708 }
709 }
710 continue;
711 }
712 }
713 while (1) {
714 /* copy one component through next slash */
715 *sanp++ = *p++;
716 if ((*p == '\0') || (*(p-1) == '/')) {
717 while (*p == '/') {
718 /* skip multiple slashes */
719 p++;
720 }
721 break;
722 }
723 }
724 if (allowdotdot) {
725 /* move the virtual beginning to leave the .. alone */
726 start = sanp;
727 }
728 }
729 if ((sanp == start) && !allowdotdot) {
730 /* ended up with nothing, so put in "." component */
731 /*
732 * note that the !allowdotdot doesn't prevent this from
733 * happening in all allowed ".." situations, but I didn't
734 * think it was worth putting in an extra variable to ensure
735 * it since an extra "." won't hurt in those situations.
736 */
737 *sanp++ = '.';
738 }
739 *sanp = '\0';
740}
741
742
743static char curr_dir[MAXPATHLEN];
744
745/* like chdir() but can be reversed with pop_dir() if save is set. It
746 is also much faster as it remembers where we have been */
747char *push_dir(char *dir, int save)
748{
749 char *ret = curr_dir;
750 static int initialised;
751
752 if (!initialised) {
753 initialised = 1;
754 getcwd(curr_dir, sizeof(curr_dir)-1);
755 }
756
757 if (!dir) return NULL; /* this call was probably just to initialize */
758
759 if (chdir(dir)) return NULL;
760
761 if (save) {
762 ret = strdup(curr_dir);
763 }
764
765 if (*dir == '/') {
766 strlcpy(curr_dir, dir, sizeof(curr_dir));
767 } else {
768 strlcat(curr_dir,"/", sizeof(curr_dir));
769 strlcat(curr_dir,dir, sizeof(curr_dir));
770 }
771
772 clean_fname(curr_dir);
773
774 return ret;
775}
776
777/* reverse a push_dir call */
778int pop_dir(char *dir)
779{
780 int ret;
781
782 ret = chdir(dir);
783 if (ret) {
784 free(dir);
785 return ret;
786 }
787
788 strlcpy(curr_dir, dir, sizeof(curr_dir));
789
790 free(dir);
791
792 return 0;
793}
794
795/* we need to supply our own strcmp function for file list comparisons
796 to ensure that signed/unsigned usage is consistent between machines. */
797int u_strcmp(const char *cs1, const char *cs2)
798{
799 const uchar *s1 = (const uchar *)cs1;
800 const uchar *s2 = (const uchar *)cs2;
801
802 while (*s1 && *s2 && (*s1 == *s2)) {
803 s1++; s2++;
804 }
805
806 return (int)*s1 - (int)*s2;
807}
808
809static OFF_T last_ofs;
810
811void end_progress(OFF_T size)
812{
813 extern int do_progress, am_server;
814
815 if (do_progress && !am_server) {
816 rprintf(FINFO,"%.0f (100%%)\n", (double)size);
817 }
818 last_ofs = 0;
819}
820
821void show_progress(OFF_T ofs, OFF_T size)
822{
823 extern int do_progress, am_server;
824
825 if (do_progress && !am_server) {
826 if (ofs > last_ofs + 1000) {
827 int pct = (int)((100.0*ofs)/size);
828 rprintf(FINFO,"%.0f (%d%%)\r", (double)ofs, pct);
829 last_ofs = ofs;
830 }
831 }
832}
833
834/* determine if a symlink points outside the current directory tree */
835int unsafe_symlink(char *dest, char *src)
836{
837 char *tok;
838 int depth = 0;
839
840 /* all absolute and null symlinks are unsafe */
841 if (!dest || !(*dest) || (*dest == '/')) return 1;
842
843 src = strdup(src);
844 if (!src) out_of_memory("unsafe_symlink");
845
846 /* find out what our safety margin is */
847 for (tok=strtok(src,"/"); tok; tok=strtok(NULL,"/")) {
848 if (strcmp(tok,"..") == 0) {
849 depth=0;
850 } else if (strcmp(tok,".") == 0) {
851 /* nothing */
852 } else {
853 depth++;
854 }
855 }
856 free(src);
857
858 /* drop by one to account for the filename portion */
859 depth--;
860
861 dest = strdup(dest);
862 if (!dest) out_of_memory("unsafe_symlink");
863
864 for (tok=strtok(dest,"/"); tok; tok=strtok(NULL,"/")) {
865 if (strcmp(tok,"..") == 0) {
866 depth--;
867 } else if (strcmp(tok,".") == 0) {
868 /* nothing */
869 } else {
870 depth++;
871 }
872 /* if at any point we go outside the current directory then
873 stop - it is unsafe */
874 if (depth < 0) break;
875 }
876
877 free(dest);
878 return (depth < 0);
879}
880
881
882/****************************************************************************
883 return the date and time as a string
884****************************************************************************/
885char *timestring(time_t t)
886{
887 static char TimeBuf[200];
888 struct tm *tm = localtime(&t);
889
890#ifdef HAVE_STRFTIME
891 strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %T",tm);
892#else
893 strlcpy(TimeBuf, asctime(tm), sizeof(TimeBuf));
894#endif
895
896 if (TimeBuf[strlen(TimeBuf)-1] == '\n') {
897 TimeBuf[strlen(TimeBuf)-1] = 0;
898 }
899
900 return(TimeBuf);
901}
902
903
904/*******************************************************************
905sleep for a specified number of milliseconds
906********************************************************************/
907void msleep(int t)
908{
909 int tdiff=0;
910 struct timeval tval,t1,t2;
911
912 gettimeofday(&t1, NULL);
913 gettimeofday(&t2, NULL);
914
915 while (tdiff < t) {
916 tval.tv_sec = (t-tdiff)/1000;
917 tval.tv_usec = 1000*((t-tdiff)%1000);
918
919 errno = 0;
920 select(0,NULL,NULL, NULL, &tval);
921
922 gettimeofday(&t2, NULL);
923 tdiff = (t2.tv_sec - t1.tv_sec)*1000 +
924 (t2.tv_usec - t1.tv_usec)/1000;
925 }
926}
927
928
929/*******************************************************************
930 Determine if two file modification times are equivalent (either exact
931 or in the modification timestamp window established by --modify-window)
932 Returns 0 if the times should be treated as the same, 1 if the
933 first is later and -1 if the 2nd is later
934 *******************************************************************/
935int cmp_modtime(time_t file1, time_t file2)
936{
937 extern int modify_window;
938
939 if (file2 > file1) {
940 if (file2 - file1 <= modify_window) return 0;
941 return -1;
942 }
943 if (file1 - file2 <= modify_window) return 0;
944 return 1;
945}
946
947
948#ifdef __INSURE__XX
949#include <dlfcn.h>
950
951/*******************************************************************
952This routine is a trick to immediately catch errors when debugging
953with insure. A xterm with a gdb is popped up when insure catches
954a error. It is Linux specific.
955********************************************************************/
956int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6)
957{
958 static int (*fn)();
959 int ret;
960 char *cmd;
961
962 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'",
963 getpid(), getpid(), getpid());
964
965 if (!fn) {
966 static void *h;
967 h = dlopen("/usr/local/parasoft/insure++lite/lib.linux2/libinsure.so", RTLD_LAZY);
968 fn = dlsym(h, "_Insure_trap_error");
969 }
970
971 ret = fn(a1, a2, a3, a4, a5, a6);
972
973 system(cmd);
974
975 free(cmd);
976
977 return ret;
978}
979#endif