1 //
2 // mir.c
3 //
4 // Oliver Fromme <olli@fromme.com>
5 // @(#)$Id: mir.c,v 1.36 1998/12/04 09:04:43 olli Exp $
6 //
7
8 static const char cvsid[]
9 = "@(#)$Id: mir.c,v 1.36 1998/12/04 09:04:43 olli Exp $";
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <sys/time.h>
18 #include <dirent.h>
19 #include <limits.h>
20 #include <time.h>
21 #include <utime.h>
22 #include <errno.h>
23 extern int errno;
24
25 #include "omi.h"
26 #include "mir.h"
27 #include "pro.h"
28 #include "rex.h"
29
30 static int
iterate_dir(char * dir,jobfunc * job,void * jobdata)31 iterate_dir (char *dir, jobfunc *job, void *jobdata)
32 {
33 //
34 // Calls the specified job function for each entry
35 // in the specified directory. The result of the
36 // job function is ignored.
37 // returns 0 on success, -1 on error.
38 //
39
40 DIR *mydir;
41 struct dirent *entry;
42 char *newpath;
43 int dirlen; // length of "dir" + "/"
44 int namelen, namespace;
45
46 if (!(mydir = opendir(dir))) {
47 //
48 // If we're in test mode, ignore non-existent
49 // directories (because directories are not
50 // created in test mode).
51 //
52
53 if ((cc->flags & MIRROR_TEST) && errno == ENOENT)
54 return 0;
55 fprintf (stderr, "%s: opendir(%s): %s\n", me, dir, serror());
56 cs->errors++;
57 return -1;
58 }
59 dirlen = strlen(dir) + 1;
60 newpath = tmalloc(dirlen + 1);
61 strcpy (newpath, dir);
62 newpath[dirlen - 1] = '/';
63 newpath[dirlen] = '\0';
64 namespace = 0;
65 while ((entry = readdir(mydir))) {
66 //
67 // Be careful to ignore the "." and ".."
68 // entries, otherwise we might get into an
69 // infinite loop somewhere.
70 //
71
72 if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' ||
73 (entry->d_name[1] == '.' && entry->d_name[2] == '\0')))
74 continue;
75 namelen = strlen(entry->d_name);
76 if (namelen > namespace) {
77 newpath = realloc(newpath, dirlen + namelen + 1);
78 if (!newpath)
79 out_of_memory();
80 namespace = namelen;
81 }
82 strcpy (newpath + dirlen, entry->d_name);
83 job (newpath, jobdata);
84 }
85 free (newpath);
86 closedir (mydir);
87 return 0;
88 }
89
90 static int
time_diff(time_t ltime,time_t rtime,omifile * of)91 time_diff (time_t ltime, time_t rtime, omifile *of)
92 {
93 //
94 // Compares the given local and remote time stamps.
95 // Returns 0 if they are equal, within the precision
96 // value given by of->tprec (or if the local time is
97 // newer than remote).
98 // Returns 1 if they are different, but within the
99 // maximum deviation (or if the IGNORETIME option
100 // is set).
101 // Returns 2 if they are completely different, and
102 // the IGNORETIME option is not set.
103 //
104
105 time_t diff;
106
107 if (ltime > rtime)
108 if (cc->flags && MIRROR_GETOLDERFILES)
109 diff = ltime - rtime;
110 else
111 return 0;
112 else
113 diff = rtime - ltime;
114 if (diff > of->tprec)
115 if (!(cc->flags && MIRROR_IGNORETIME) &&
116 diff > of->tprec + cc->deviation)
117 return 2;
118 else
119 return 1;
120 else
121 return 0;
122 }
123
124 static char *
timereport(time_t t)125 timereport (time_t t)
126 {
127 static char buff[12];
128
129 // Note: Some versions of gcc print a warning if %y
130 // is used. Oh how I hate that brain-dead GNU crap.
131
132 if (cc->flags & MIRROR_LOCALTIME)
133 strftime (buff, 12, "%y%m%d%H%M", localtime(&t));
134 else
135 strftime (buff, 12, "%y%m%d%H%M", gmtime(&t));
136 return buff;
137 }
138
139 static char *
modereport(mode_t m)140 modereport (mode_t m)
141 {
142 static char buff[12];
143
144 strcpy (buff, "----------");
145 if ((m & S_IFMT) == S_IFDIR) buff[0] = 'd';
146 if (m & S_IRUSR) buff[1] = 'r';
147 if (m & S_IWUSR) buff[2] = 'w';
148 if (m & S_IXUSR) buff[3] = 'x';
149 if (m & S_IRGRP) buff[4] = 'r';
150 if (m & S_IWGRP) buff[5] = 'w';
151 if (m & S_IXGRP) buff[6] = 'x';
152 if (m & S_IROTH) buff[7] = 'r';
153 if (m & S_IWOTH) buff[8] = 'w';
154 if (m & S_IXOTH) buff[9] = 'x';
155 return buff;
156 }
157
158 #define WHY_NEW 1
159 #define WHY_GONE 2
160 #define WHY_TIME 3
161 #define WHY_SIZE 4
162 #define WHY_LINK 5
163 #define WHY_MODE 6
164
165 static void
report(int why,long old,long new,char * name)166 report (int why, long old, long new, char *name)
167 {
168 static omisite *lastsite = NULL;
169 char whychar = 0;
170 char *fname, *tmps;
171 int rootlen;
172
173 if (!cc->havereporthead) {
174 cc->havereporthead = TRUE;
175 putc ('\n', stderr);
176 if (lastsite != ct) {
177 lastsite = ct;
178 fprintf (stderr, "Report for server %s\n", ct->name);
179 }
180 fprintf (stderr, "Mirror %s -> %s\n",
181 cp->remotedir, cp->localdir);
182 }
183
184 rootlen = strlen(cp->localdir);
185 if (!strncmp(name, cp->localdir, rootlen)) {
186 //
187 // Cut off the root of the
188 // local tree.
189 //
190 fname = name + rootlen;
191 while (*fname == '/')
192 fname++;
193 if (!*fname)
194 fname = ".";
195 }
196 else
197 fname = name;
198
199 switch (why) {
200 case WHY_LINK:
201 fprintf (stderr, "* <link> %10s %s\n",
202 timereport(new), fname);
203 break;
204 case WHY_NEW:
205 whychar = '+';
206 /* FALLTHROUGH */
207 case WHY_GONE:
208 if (!whychar)
209 whychar = '-';
210 if (old == -1)
211 fprintf (stderr, "%c <dir> %10s %s\n",
212 whychar, timereport(new), fname);
213 else if (old == -2)
214 fprintf (stderr, "%c <link> %10s %s\n",
215 whychar, timereport(new), fname);
216 else
217 fprintf (stderr, "%c %10ld %10s %s\n",
218 whychar, old, timereport(new), fname);
219 break;
220 case WHY_TIME:
221 if (!(tmps = strdup(timereport(old))))
222 out_of_memory();
223 fprintf (stderr, "* %10s -> %10s %s\n",
224 tmps, timereport(new), fname);
225 free (tmps);
226 break;
227 case WHY_SIZE:
228 if (old < new)
229 whychar = '>';
230 else
231 whychar = '<';
232 fprintf (stderr, "%c %10ld -> %10ld %s\n",
233 whychar, old, new, fname);
234 case WHY_MODE:
235 if (!(tmps = strdup(modereport(old))))
236 out_of_memory();
237 fprintf (stderr, "* %10s -> %10s %s\n",
238 tmps, modereport(new), fname);
239 free (tmps);
240 }
241 }
242
243 static bool
tonne(char * path,void * allowreport)244 tonne (char *path, void *allowreport)
245 {
246 //
247 // Removes the specified files (or at least tries to).
248 // If "path" is a directory, tries to recursively
249 // remove the complete tree. If "path" is a symlink,
250 // only the link is removed.
251 // Returns TRUE on success, FALSE on error.
252 //
253
254 struct stat st;
255 int result = 0;
256
257 if (!cc->removelimit)
258 return TRUE;
259 if (lstat(path, &st) < 0) {
260 fprintf (stderr, "%s: lstat(%s): %s\n", me, path, serror());
261 cs->errors++;
262 return FALSE;
263 }
264 if ((st.st_mode & S_IFMT) == S_IFDIR) {
265 if (cc->flags & MIRROR_PRESERVEDIRS) {
266 if (cc->debug != DEBUG_NONE) {
267 fprintf (stderr,
268 "%s: warning: directory %s:\n", me, path);
269 fprintf (stderr,
270 "will not be removed locally, although it"
271 " disappeared from the remote site.\n");
272 }
273 //
274 // The user set the PRESERVEDIRS flag
275 // and wants it this way, so we return
276 // "success".
277 //
278 return TRUE;
279 }
280 iterate_dir (path, (jobfunc *) tonne, allowreport);
281 if (cc->debug & DEBUG_REPORT)
282 report (WHY_GONE, -1, st.st_mtime, path);
283 if (cc->debug & DEBUG_REMOVE_DIRS)
284 fprintf (stderr, "rmdir %s\n", path);
285 cs->gonedirs++;
286 if ((cc->flags & MIRROR_TEST))
287 result = 0;
288 else
289 if ((result = rmdir(path)) < 0) {
290 fprintf (stderr, "%s: rmdir(%s): %s\n", me,
291 (cc->debug & DEBUG_REMOVE_DIRS) ? "" : path,
292 serror());
293 cs->errors++;
294 }
295 }
296 else {
297 int islink, debuginfo;
298
299 if ((islink = (st.st_mode & S_IFMT) == S_IFLNK))
300 cs->gonelinks++;
301 else {
302 cs->gonefiles++;
303 cs->gonebytes += st.st_size;
304 cs->growbytes -= st.st_size;
305 }
306 debuginfo = cc->debug &
307 (islink ? DEBUG_REMOVE_LINKS : DEBUG_REMOVE_FILES);
308 if (allowreport && cc->debug & DEBUG_REPORT)
309 report (WHY_GONE, islink ? -2 : st.st_size,
310 st.st_mtime, path);
311 if (debuginfo)
312 fprintf (stderr, "unlink %s\n", path);
313 if ((cc->flags & MIRROR_TEST))
314 result = 0;
315 else
316 if ((result = unlink(path)) < 0) {
317 fprintf (stderr, "%s: unlink(%s): %s\n",
318 me, debuginfo ? "" : path, serror());
319 cs->errors++;
320 }
321 }
322 return result >= 0;
323 }
324
325 int
mkdir_parents(char * path)326 mkdir_parents (char *path)
327 {
328 //
329 // Creates the parent directories of the given path,
330 // if necessary (note: it doesn't try to mkdir path
331 // itself).
332 //
333
334 char *parent, *slash;
335 struct stat st;
336
337 if (!(slash = strrchr(path, '/')) || slash == path)
338 return 0;
339 if (!(parent = strndup(path, slash - path)))
340 out_of_memory();
341
342 //
343 // *TODO* We're following local symlinks here.
344 // We have to think about whether it makes
345 // sense or not (I think it does).
346 //
347 if (stat(parent, &st) < 0) {
348 if (mkdir_parents(parent) < 0) {
349 free (parent);
350 return -1;
351 }
352 if (cc->debug & DEBUG_REPORT)
353 report (WHY_NEW, -1, now, parent);
354 if (cc->debug & DEBUG_CREATE_DIRS)
355 fprintf (stderr, "mkdir %s\n", parent);
356 cs->newdirs++;
357 if (!(cc->flags & MIRROR_TEST) && mkdir(parent, 0700) < 0) {
358 fprintf (stderr, "%s: mkdir(%s): %s\n", me,
359 (cc->debug & DEBUG_CREATE_DIRS) ? "" : parent,
360 serror());
361 cs->errors++;
362 free (parent);
363 return -1;
364 }
365 if (cc->changegroup) {
366 if (cc->debug & DEBUG_CHX)
367 fprintf (stderr, "chown(\"%s\", -1, %d)\n",
368 parent, cc->newgroup);
369 if (!(cc->flags & MIRROR_TEST))
370 chown (parent, -1, cc->newgroup);
371 }
372 if (cc->debug & DEBUG_CHX)
373 fprintf (stderr, "chmod(\"%s\", 0%o)\n",
374 parent, cc->perms_dir);
375 if (!(cc->flags & MIRROR_TEST))
376 chmod (parent, cc->perms_dir);
377 }
378 free (parent);
379 return 0;
380 }
381
382 static int
check_mkdir(omifile * od,char * path,mode_t * oldmode)383 check_mkdir (omifile *od, char *path, mode_t *oldmode)
384 {
385 //
386 // Creates the specified directory, if it does not
387 // already exist. If there is already an entry with
388 // that name which is not a directory (and not a
389 // symlink to a directory), it is tried to be removed
390 // first.
391 //
392 // Return values:
393 // -1 error
394 // 0 success, the directory already exists
395 // 1 success, dir exists, mtime has to be updated
396 // 2 success, dir exists, mtime & modes have to be updated
397 // 3 success, dir did not exist and has been created
398 //
399
400 struct stat st;
401 mode_t perms;
402
403 //
404 // *TODO* We are following local symlinks here.
405 // This should be an option.
406 //
407 if (stat(path, &st) >= 0)
408 if ((st.st_mode & S_IFMT) == S_IFDIR) {
409 od->flags |= OFLAG_LEXISTS;
410 if ((cc->flags & MIRROR_PERMISSIONS)
411 && !(od->flags & OFLAG_BADPERM))
412 perms = od->mode & ACCESSPERMS;
413 else
414 perms = cc->perms_dir;
415 if ((st.st_mode & ALLPERMS) != perms) {
416 *oldmode = st.st_mode;
417 return 2;
418 }
419 else if (od->mtime &&
420 time_diff(st.st_mtime, od->mtime, od) >= 1)
421 return 1;
422 else
423 return 0;
424 }
425 else
426 tonne (path, path);
427 if (cc->debug & DEBUG_REPORT)
428 report (WHY_NEW, -1, od->mtime, path);
429 if (cc->debug & DEBUG_CREATE_DIRS)
430 fprintf (stderr, "mkdir %s\n", path);
431 cs->newdirs++;
432 if (!(cc->flags & MIRROR_TEST) && mkdir(path, 0700) < 0) {
433 fprintf (stderr, "%s: mkdir(%s): %s\n", me,
434 (cc->debug & DEBUG_CREATE_DIRS) ? "" : path,
435 serror());
436 cs->errors++;
437 return -1;
438 }
439 if (cc->changegroup) {
440 if (cc->debug & DEBUG_CHX)
441 fprintf (stderr, "chown(\"%s\", -1, %d)\n",
442 path, cc->newgroup);
443 if (!(cc->flags & MIRROR_TEST))
444 chown (path, -1, cc->newgroup);
445 }
446 return 3;
447 }
448
449 int entries_total; // total number of (local) entries
450 simple_list removal_list; // list of entries queued for removal
451
452 static void
queue_tonne(char * path)453 queue_tonne (char *path)
454 {
455 //
456 // Queues a file for removal.
457 //
458
459 simple_list_add_string (&removal_list, path);
460 }
461
462 static int
perform_tonne(char * path)463 perform_tonne (char *path)
464 {
465 //
466 // Performs all file removals that have been
467 // queued, if allowed by the removelimit.
468 //
469 // Returns 1 if at least one file has been
470 // removed, otherwise 0.
471 //
472
473 int entries_tonne;
474 int changes;
475
476 changes = 0;
477 entries_tonne = simple_list_count(&removal_list);
478 if (entries_total && (cc->removelimit > 99 ||
479 ((100 * entries_tonne) / entries_total < cc->removelimit))) {
480 if ((changes = entries_tonne) > 0)
481 simple_list_traverse (&removal_list, (jobfunc *) tonne,
482 &removal_list, TRAVERSE_IGNORERESULT);
483 }
484 else
485 if (entries_tonne && cc->debug != DEBUG_NONE) {
486 fprintf (stderr,
487 "%s: warning: directory %s:\n", me, path);
488 fprintf (stderr,
489 "Number of disappeared files exceeds removal "
490 "limit of %d%%: no files removed.\n",
491 cc->removelimit);
492 }
493 simple_list_done (&removal_list);
494 return changes;
495 }
496
497 static int
match_locexclude(char * path)498 match_locexclude (char *path)
499 {
500 //
501 // Returns MATCH_EXCLUDE or MATCH_INCLUDE.
502 //
503
504 int result;
505
506 if (!(result = simple_list_traverse(globalconfig.locexclude,
507 (jobfunc *) regex_job, path, 0))) {
508 if (!(result = simple_list_traverse(ct->conf->locexclude,
509 (jobfunc *) regex_job, path, 0))) {
510 if (!(result = simple_list_traverse(cc->locexclude,
511 (jobfunc *) regex_job, path, 0))) {
512 if (cc->debug & DEBUG_LOCAL_REGEX)
513 fprintf (stderr, "local %s: %s\n",
514 matchdesc[MATCH_INCLUDE], path);
515 return MATCH_INCLUDE;
516 }
517 }
518 }
519 if ((cc->debug & DEBUG_LOCAL_REGEX) && result)
520 fprintf (stderr, "local %s: %s\n", matchdesc[result], path);
521 return result;
522 }
523
524 static int
check_cleanfile(char * name,omifile * omidir)525 check_cleanfile (char *name, omifile *omidir)
526 {
527 //
528 // Try to locate the local file "name" in the remote
529 // directory. If not found, or if the remote file
530 // has an incompatible type (e.g. directory <-> plain
531 // file), then remove the local file.
532 //
533
534 struct stat st;
535 omifile *of;
536 char *basename;
537 int i;
538
539 if (match_locexclude(name) == MATCH_EXCLUDE)
540 return 0;
541
542 entries_total++;
543 if ((basename = strrchr(name, '/')))
544 basename++;
545 else
546 basename = name;
547
548 //
549 // We perform a linear search to locate the name in
550 // "omidir". Note that we cannot assume that omidir
551 // is sorted. It might be more efficient to qsort()
552 // omidir first in check_cleandir() and then perform
553 // a binary search here, especially if the directory
554 // is very large.
555 //
556
557 of = (omifile *) omidir->data;
558 for (i = 0; i < omidir->size; i++, of++)
559 if (!strcmp(of->name, basename)
560 && !(of->flags & OFLAG_EXCLUDE))
561 break;
562 if (i >= omidir->size) {
563 //
564 // If not found in remote dir, remove it.
565 //
566 queue_tonne (name);
567 return 0;
568 }
569
570 //
571 // The file exists both locally and on the remote
572 // site. Further handling depends on the type of
573 // the files.
574 //
575 // If the remote file is a symlink, we have to mirror
576 // it as such, because following remote symlinks is
577 // difficult (and normally we don't want to follow
578 // remote symlinks anyway).
579 //
580 // *TODO* We are following local symlinks here
581 // (except if the remote file is a symlink,
582 // too). This should be an option.
583 //
584
585 if ((of->mode & S_IFMT) == S_IFLNK) {
586 //
587 // If the remote file is a symlink, the local
588 // file must be a symlink, too, otherwise
589 // remove it.
590 //
591 if (lstat(name, &st) < 0) {
592 //
593 // Trouble. Bad luck.
594 // Probably "permission denied".
595 //
596 fprintf (stderr, "%s: lstat(%s): %s\n",
597 me, name, serror());
598 cs->errors++;
599 return -1;
600 }
601 if ((st.st_mode & S_IFMT) != S_IFLNK) {
602 queue_tonne (name);
603 }
604 else
605 of->flags |= OFLAG_LEXISTS;
606 return 0;
607 }
608
609 //
610 // *TODO* *CHANGED* lstat() instead of stat():
611 // Do not follow local symlinks
612 // by default!
613 //
614 if (lstat(name, &st) < 0) {
615 //
616 // If the stat fails (although we know that
617 // the file exists), it's probably a problem
618 // with the permissions. We can't do much
619 // about that.
620 //
621 fprintf (stderr, "%s: stat(%s): %s\n", me, name, serror());
622 cs->errors++;
623 return -1;
624 }
625 if ((of->mode & S_IFMT) != (st.st_mode & S_IFMT)) {
626 //
627 // If the remote file is a directory and the
628 // local file is a plain file, or vice versa,
629 // remove the local file.
630 //
631 queue_tonne (name);
632 }
633 else
634 of->flags |= OFLAG_LEXISTS;
635 return 0;
636 }
637
638 static int
check_cleandir(char * path,omifile * omidir)639 check_cleandir (char *path, omifile *omidir)
640 {
641 //
642 // Remove entries in the specified local directory
643 // which are not present on the remote site, or which
644 // have incompatible types.
645 //
646 // Return values:
647 // 0 no files removed.
648 // 1 one or more files have been removed.
649 //
650
651 entries_total = 0;
652 simple_list_init (&removal_list);
653 iterate_dir (path, (jobfunc *) check_cleanfile, omidir);
654 return perform_tonne(path);
655 }
656
657 static int
update_mtime(char * name,omifile * of)658 update_mtime (char *name, omifile *of)
659 {
660 struct utimbuf times;
661 int result;
662
663 if ((cc->flags & MIRROR_DONTTOUCH) || !of->mtime)
664 return 0;
665 times.actime = of->mtime;
666 times.modtime = of->mtime;
667 if (cc->debug & DEBUG_CHX)
668 fprintf (stderr, "utime(\"%s\", [%ld])\n",
669 name, (long) times.modtime);
670 if ((cc->flags & MIRROR_TEST))
671 result = 0;
672 else
673 if ((result = utime(name, ×)) < 0) {
674 fprintf (stderr, "%s: utime(): %s\n", me, serror());
675 cs->errors++;
676 }
677 return result;
678 }
679
680 static int
retrieve(ftpstat * fs,char * rname,char * lname,omifile * of)681 retrieve (ftpstat *fs, char *rname, char *lname, omifile *of)
682 {
683 //
684 // Retrieve the remote file "rname" to the local file
685 // "lname". It is a plain file (no directory and no
686 // symlink).
687 //
688 // We first create the local file under a temporary
689 // name, which we rename to "lname" when the transfer
690 // is done successfully. (This causes the mtime of
691 // the local directory to be changed, so we have to
692 // reset it after we're done with that directory.)
693 //
694 // Return values:
695 // -3 error, stop
696 // -2 error, and we did retry
697 // -1 error, no retry was done, or rename() failed
698 // 0 success
699 //
700 // *TODO* The error handling definitely needs to be
701 // improved!!!
702 //
703
704 char *tmpname, *cptr;
705 ftpreply *fr;
706 int result;
707 mode_t perms;
708
709 tmpname = tmalloc(strlen(lname) + 10);
710 if ((cptr = strrchr(lname, '/')))
711 strncpy (tmpname, lname, ++cptr - lname);
712 else
713 cptr = lname;
714 tmpname[cptr - lname] = 0;
715 strcat (tmpname, ".in.");
716 strcat (tmpname, cptr);
717 strcat (tmpname, ".omi.");
718 if (cc->debug & DEBUG_CREATE_FILES)
719 fprintf (stderr, "creating %s\n", lname);
720 if (of->flags & OFLAG_LEXISTS) {
721 cs->updfiles++;
722 cs->updbytes += of->size;
723 }
724 else {
725 cs->newfiles++;
726 cs->newbytes += of->size;
727 }
728 cs->growbytes += of->size;
729 if (!(cc->flags & MIRROR_TEST)) {
730 setsize_progress (of->size);
731 fr = ftp_retrieve(fs, rname, tmpname);
732 if (fr->code >= 400) {
733 free (tmpname);
734 if (fr->code % 100 != 99) {
735 fprintf (stderr,
736 "%s: error retrieving file %s\n",
737 me, rname);
738 fprintf (stderr,
739 "%s: [%d] %s\n", cptr, fr->code, fr->line);
740 }
741 cs->errors++;
742 if (fr->retry == RETRY_NO)
743 result = -1;
744 else if (fr->retry == RETRY_STOP)
745 result = -3;
746 else
747 result = -2;
748 ftp_freereply (fr);
749 return result;
750 }
751 ftp_freereply (fr);
752 }
753 if (cc->changegroup) {
754 if (cc->debug & DEBUG_CHX)
755 fprintf (stderr, "chown(\"%s\", -1, %d)\n",
756 tmpname, cc->newgroup);
757 if (!(cc->flags & MIRROR_TEST))
758 chown (tmpname, -1, cc->newgroup);
759 }
760 if ((cc->flags & MIRROR_PERMISSIONS) && !(of->flags & OFLAG_BADPERM))
761 perms = of->mode & ACCESSPERMS;
762 else
763 perms = cc->perms_file;
764 if (cc->debug & DEBUG_CHX)
765 fprintf (stderr, "chmod(\"%s\", 0%o)\n",
766 tmpname, perms);
767 if (!(cc->flags & MIRROR_TEST))
768 chmod (tmpname, perms);
769 update_mtime (tmpname, of);
770 if (cc->debug & DEBUG_CHX)
771 fprintf (stderr, "rename(\"%s\", \"%s\")\n", tmpname, lname);
772 if ((cc->flags & MIRROR_TEST))
773 result = 0;
774 else
775 result = rename(tmpname, lname);
776 free (tmpname);
777 return result;
778 }
779
780 static int
check_symlink(char * name,omifile * of)781 check_symlink (char *name, omifile *of)
782 {
783 //
784 // Check if the remote symlink already exists locally
785 // and points to the same target. If not, create it.
786 //
787 // Return values:
788 // < 0 error
789 // 0 no change
790 // 1 symlink is new or has changed
791 //
792
793 int namesize;
794 char linkname[PATH_MAX + 1];
795 int result;
796
797 if ((namesize = readlink(name, linkname, PATH_MAX)) >= 0) {
798 linkname[namesize] = '\0';
799 if (!strcmp(linkname, (char *) of->data)) {
800 //
801 // The symlink already exists locally
802 // and points to the same target.
803 //
804 return 0;
805 }
806 else {
807 //
808 // There is a symlink with that name,
809 // but it points to a different
810 // target, so remove it.
811 //
812 if (cc->debug & DEBUG_REPORT)
813 report (WHY_LINK, -2, of->mtime, name);
814 tonne (name, NULL);
815 cs->gonelinks--;
816 cs->updlinks++;
817 }
818 }
819 else {
820 cs->newlinks++;
821 if (cc->debug & DEBUG_REPORT)
822 report (WHY_NEW, -2, of->mtime, name);
823 }
824 if (cc->debug & DEBUG_CREATE_LINKS)
825 fprintf (stderr, "symlink %s -> %s\n",
826 name, (char *) of->data);
827 if ((cc->flags & MIRROR_TEST))
828 result = 0;
829 else
830 if ((result = symlink((char *) of->data, name)) < 0) {
831 fprintf (stderr, "%s: symlink(%s): %s\n", me,
832 (cc->debug & DEBUG_CREATE_LINKS) ? "" : name,
833 serror());
834 cs->errors++;
835 return -1;
836 }
837 return 1;
838 }
839
840 static int
check_retrieve(ftpstat * fs,char * rpath,char * lpath,omifile * omidir)841 check_retrieve (ftpstat *fs, char *rpath, char *lpath, omifile *omidir)
842 {
843 //
844 // Retrieve all files from "omidir" that are not
845 // present locally, or that have changed. Also
846 // recurse into subdirectories as necessary.
847 //
848 // Return values:
849 // < 0 error
850 // 0 nothing changed
851 // 1 at least one new or updated file
852 //
853
854 omifile *of;
855 char *lname, *rname;
856 struct stat st;
857 int i, result, changed;
858 mode_t perms;
859
860 changed = 0;
861 of = (omifile *) omidir->data;
862 for (i = 0; i < omidir->size; i++, of++) {
863 if (of->flags & OFLAG_EXCLUDE)
864 continue;
865 //
866 // *TODO* This lname/rname stuff could be
867 // optimized like in iterate_dir().
868 //
869 lname = tmalloc(strlen(lpath) + strlen(of->name) + 2);
870 strcpy (lname, lpath);
871 strcat (lname, "/");
872 strcat (lname, of->name);
873 rname = tmalloc(strlen(rpath) + strlen(of->name) + 2);
874 strcpy (rname, rpath);
875 strcat (rname, "/");
876 strcat (rname, of->name);
877 if ((of->mode & S_IFMT) == S_IFDIR) {
878 //
879 // If the remote file has type
880 // "directory", recurse into it.
881 // It doesn't matter whether it
882 // exists locally or not, and which
883 // type it has, since mirror_dir()
884 // cares for that.
885 //
886 result = mirror_dir(fs, rname, lname, of);
887 if (result < -1) {
888 free (lname);
889 free (rname);
890 return result;
891 }
892 if (result >= 1)
893 changed = 1;
894 }
895 else if ((of->mode & S_IFMT) == S_IFLNK) {
896 //
897 // If the remote file has type
898 // "symlink", duplicate it locally
899 // if it doesn't already exist and
900 // point to the same target.
901 //
902 cs->totallinks++;
903 if (check_symlink(lname, of) >= 1)
904 changed = 1;
905 }
906 else {
907 //
908 // The remote file is a plain file.
909 //
910 cs->totalfiles++;
911 cs->totalbytes += of->size;
912 if (stat(lname, &st) < 0) {
913 //
914 // The remote file is a plain file.
915 // Since it doesn't exist locally,
916 // get it.
917 //
918 if (cc->debug & DEBUG_REASONING)
919 fprintf (stderr, "new file %s\n",
920 lname);
921 if (cc->debug & DEBUG_REPORT)
922 report (WHY_NEW, of->size, of->mtime, lname);
923 changed = 1;
924 result = retrieve(fs, rname, lname, of);
925 if (result < -1) {
926 free (lname);
927 free (rname);
928 return result;
929 }
930 }
931 else if (st.st_size != of->size) {
932 //
933 // It's a plain file that is both
934 // present on the local and remote
935 // site, but with different sizes.
936 // Get the file from the remote site.
937 // Note that we do not have to remove
938 // the local file, as it will be
939 // overwritten when the retrieval is
940 // done.
941 //
942 // *TODO* In a later version, it might
943 // be useful to try a "reget" if
944 // the local file is smaller
945 // than the remote file, but
946 // with the same mtime. This
947 // could at least be an option.
948 // Of course we have to check
949 // first that the remote server
950 // supports the REST command
951 // appropriately.
952 //
953 if (cc->debug & DEBUG_REASONING)
954 fprintf (stderr, "size mismatch for %s\n",
955 lname);
956 if (cc->debug & DEBUG_REPORT)
957 report (WHY_SIZE,
958 st.st_size, of->size, lname);
959 cs->growbytes -= st.st_size;
960 //
961 // We have to set `changed' to 1 in
962 // this case, too, because retrieve()
963 // changes the mtime of the local
964 // directory!
965 //
966 changed = 1;
967 result = retrieve(fs, rname, lname, of);
968 if (result < -1) {
969 free (lname);
970 free (rname);
971 return result;
972 }
973 }
974 else if ((result =
975 time_diff(st.st_mtime, of->mtime, of)) > 1) {
976 //
977 // The size is the same, but the
978 // mtimes differ. Get the file.
979 //
980 if (cc->debug & DEBUG_REASONING)
981 fprintf (stderr, "mtime mismatch for %s\n",
982 lname);
983 if (cc->debug & DEBUG_REPORT)
984 report (WHY_TIME,
985 st.st_mtime, of->mtime, lname);
986 cs->growbytes -= st.st_size;
987 changed = 1; // Se above!
988 result = retrieve(fs, rname, lname, of);
989 if (result < -1) {
990 free (lname);
991 free (rname);
992 return result;
993 }
994 }
995 else {
996 if ((cc->flags & MIRROR_PERMISSIONS)
997 && !(of->flags & OFLAG_BADPERM))
998 perms = of->mode & ACCESSPERMS;
999 else
1000 perms = cc->perms_file;
1001 if ((st.st_mode & ALLPERMS) != perms) {
1002 cs->permfiles++;
1003 if (cc->debug & DEBUG_REPORT)
1004 report (WHY_MODE,
1005 st.st_mode, perms, lname);
1006 if (cc->debug & DEBUG_CHX)
1007 fprintf (stderr, "chmod(\"%s\", 0%o)\n",
1008 lname, perms);
1009 if (!(cc->flags & MIRROR_TEST))
1010 chmod (lname, perms);
1011 }
1012 if (result == 1) // result from time_diff()
1013 update_mtime (lname, of);
1014 }
1015 }
1016 free (rname);
1017 free (lname);
1018 }
1019 return changed;
1020 }
1021
1022 typedef int sortjob(const void *, const void *);
1023
1024 static int
timesortjob(const omifile * a,const omifile * b)1025 timesortjob (const omifile *a, const omifile *b)
1026 {
1027 //
1028 // Sort two files according to the timestamp,
1029 // so that the newer one comes first.
1030 //
1031 // If there are files with the EXCLUDE flag,
1032 // they should come last.
1033 //
1034
1035 bool excla, exclb;
1036
1037 excla = (a->flags & OFLAG_EXCLUDE) != 0;
1038 exclb = (b->flags & OFLAG_EXCLUDE) != 0;
1039 if (excla != exclb)
1040 if (excla)
1041 return 1;
1042 else
1043 return -1;
1044
1045 if (a->mtime > b->mtime)
1046 return -1;
1047 if (a->mtime < b->mtime)
1048 return 1;
1049 return 0;
1050 }
1051
1052 static void
filter_dir(omifile * od)1053 filter_dir (omifile *od)
1054 {
1055 omifile *of;
1056 int i;
1057
1058 if (cc->newest && od->size > cc->newest) {
1059 //
1060 // First sort the directory entries by timestamps,
1061 // newest first.
1062 //
1063
1064 qsort (od->data, od->size, sizeof(omifile),
1065 (sortjob *) timesortjob);
1066
1067 //
1068 // Now we only need the first cc->newest entries,
1069 // so we discard all the rest.
1070 //
1071
1072 of = (omifile *) od->data + cc->newest;
1073 for (i = cc->newest; i < od->size; i++, of++) {
1074 if (of->name)
1075 free (of->name);
1076 switch (of->mode & S_IFMT) {
1077 case S_IFDIR:
1078 free_tree (of);
1079 break;
1080 case S_IFLNK:
1081 if (of->data)
1082 free (of->data);
1083 }
1084 }
1085 od->size = cc->newest;
1086 if (!(od->data = realloc(od->data, od->size * sizeof(omifile))))
1087 out_of_memory();
1088 }
1089 }
1090
1091 int
mirror_dir(ftpstat * fs,char * rpath,char * lpath,omifile * omidir)1092 mirror_dir (ftpstat *fs, char *rpath, char *lpath, omifile *omidir)
1093 {
1094 //
1095 // Mirror the remote directory "rpath" (the tree
1096 // structure is given in "omidir") to the local
1097 // directory "lpath".
1098 //
1099 // Return values
1100 // < 0 error
1101 // 0 the directory already existed locally
1102 // 1 it's a new directory
1103 //
1104
1105 int retrresult, cleanresult, makeresult;
1106 mode_t perms, oldmode;
1107
1108 cs->totaldirs++;
1109
1110 //
1111 // Filter entries in this directory, if necessary.
1112 //
1113
1114 filter_dir (omidir);
1115
1116 //
1117 // First, create the local directory if it doesn't
1118 // already exist.
1119 //
1120
1121 if ((makeresult = check_mkdir(omidir, lpath, &oldmode)) < 0) {
1122 fprintf (stderr, "%s: Can't create local directory, "
1123 "skipping directory tree.\n", me);
1124 return -1;
1125 }
1126
1127 //
1128 // Second, iterate the local directory, compare
1129 // each entry with the "omidir", and remove local
1130 // files not present on the remote server.
1131 //
1132
1133 if ((cleanresult = check_cleandir(lpath, omidir)) < 0)
1134 return cleanresult;
1135
1136 //
1137 // Third, iterate the "omidir", compare every entry
1138 // with the local file (if present), and download it
1139 // if necessary. Also recurse into subdirectories.
1140 //
1141
1142 if ((retrresult = check_retrieve(fs, rpath, lpath, omidir)) < 0)
1143 return retrresult;
1144
1145 if ((omidir->flags & OFLAG_LEXISTS) && (cleanresult || retrresult))
1146 cs->upddirs++;
1147
1148 //
1149 // Finally, update permission modes and time stamps
1150 // of the local directory if anything has changed
1151 // inside, or if the local directory's timestamp is
1152 // different from the remote one's.
1153 //
1154
1155 if (makeresult >= 2) {
1156 if ((cc->flags & MIRROR_PERMISSIONS)
1157 && !(omidir->flags & OFLAG_BADPERM))
1158 perms = omidir->mode & ACCESSPERMS;
1159 else
1160 perms = cc->perms_dir;
1161 if (makeresult < 3) {
1162 cs->permdirs++;
1163 if (cc->debug & DEBUG_REPORT)
1164 report (WHY_MODE,
1165 oldmode, perms | S_IFDIR, lpath);
1166 }
1167 if (cc->debug & DEBUG_CHX)
1168 fprintf (stderr, "chmod(\"%s\", 0%o)\n",
1169 lpath, perms);
1170 if (!(cc->flags & MIRROR_TEST))
1171 chmod (lpath, perms);
1172 }
1173 if (makeresult || cleanresult || retrresult)
1174 update_mtime (lpath, omidir);
1175 return makeresult >= 3;
1176 }
1177
1178 //--
1179