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