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