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