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