2 * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/types.h>
55 /* Internal error codes. */
56 #define UPDATER_ERR_PROTO (-1) /* Protocol error. */
57 #define UPDATER_ERR_MSG (-2) /* Error is in updater->errmsg. */
58 #define UPDATER_ERR_READ (-3) /* Error reading from server. */
59 #define UPDATER_ERR_DELETELIM (-4) /* File deletion limit exceeded. */
63 /* Everything needed to update a file. */
65 struct statusrec srbuf;
69 char *coname; /* Points somewhere in destpath. */
73 /* Those are only used for diff updating. */
82 struct config *config;
88 static struct file_update *fup_new(struct coll *, struct status *);
89 static int fup_prepare(struct file_update *, char *, int);
90 static void fup_cleanup(struct file_update *);
91 static void fup_free(struct file_update *);
93 static void updater_prunedirs(char *, char *);
94 static int updater_batch(struct updater *, int);
95 static int updater_docoll(struct updater *, struct file_update *, int);
96 static int updater_delete(struct updater *, struct file_update *);
97 static void updater_deletefile(const char *);
98 static int updater_checkout(struct updater *, struct file_update *, int);
99 static int updater_addfile(struct updater *, struct file_update *,
101 int updater_addelta(struct rcsfile *, struct stream *, char *);
102 static int updater_setattrs(struct updater *, struct file_update *,
103 char *, char *, char *, char *, char *, struct fattr *);
104 static int updater_setdirattrs(struct updater *, struct coll *,
105 struct file_update *, char *, char *);
106 static int updater_updatefile(struct updater *, struct file_update *fup,
108 static int updater_updatenode(struct updater *, struct coll *,
109 struct file_update *, char *, char *);
110 static int updater_diff(struct updater *, struct file_update *);
111 static int updater_diff_batch(struct updater *, struct file_update *);
112 static int updater_diff_apply(struct updater *, struct file_update *,
114 static int updater_rcsedit(struct updater *, struct file_update *, char *,
116 int updater_append_file(struct updater *, struct file_update *,
118 static int updater_rsync(struct updater *, struct file_update *, size_t);
119 static int updater_read_checkout(struct stream *, struct stream *);
121 static struct file_update *
122 fup_new(struct coll *coll, struct status *st)
124 struct file_update *fup;
126 fup = xmalloc(sizeof(struct file_update));
127 memset(fup, 0, sizeof(*fup));
134 fup_prepare(struct file_update *fup, char *name, int attic)
140 fup->origpath = NULL;
142 if (coll->co_options & CO_CHECKOUTMODE)
143 fup->destpath = checkoutpath(coll->co_prefix, name);
145 fup->destpath = cvspath(coll->co_prefix, name, attic);
146 fup->origpath = atticpath(coll->co_prefix, name);
147 /* If they're equal, we don't need special care. */
148 if (fup->origpath != NULL &&
149 strcmp(fup->origpath, fup->destpath) == 0) {
151 fup->origpath = NULL;
155 if (fup->destpath == NULL)
157 fup->coname = fup->destpath + coll->co_prefixlen + 1;
161 /* Called after each file update to reinit the structure. */
163 fup_cleanup(struct file_update *fup)
165 struct statusrec *sr;
169 if (fup->destpath != NULL) {
171 fup->destpath = NULL;
173 if (fup->temppath != NULL) {
175 fup->temppath = NULL;
177 if (fup->origpath != NULL) {
179 fup->origpath = NULL;
182 if (fup->author != NULL) {
187 if (fup->wantmd5 != NULL) {
191 if (fup->orig != NULL) {
192 stream_close(fup->orig);
195 if (fup->to != NULL) {
196 stream_close(fup->to);
199 if (sr->sr_file != NULL)
201 if (sr->sr_tag != NULL)
203 if (sr->sr_date != NULL)
205 if (sr->sr_revnum != NULL)
207 if (sr->sr_revdate != NULL)
208 free(sr->sr_revdate);
209 fattr_free(sr->sr_clientattr);
210 fattr_free(sr->sr_serverattr);
211 memset(sr, 0, sizeof(*sr));
215 fup_free(struct file_update *fup)
225 struct thread_args *args;
226 struct updater upbuf, *up;
232 up->config = args->config;
237 error = updater_batch(up, 0);
240 * Make sure to close the fixups even in case of an error,
241 * so that the lister thread doesn't block indefinitely.
243 fixups_close(up->config->fixups);
245 error = updater_batch(up, 1);
247 case UPDATER_ERR_PROTO:
248 xasprintf(&args->errmsg, "Updater failed: Protocol error");
249 args->status = STATUS_FAILURE;
251 case UPDATER_ERR_MSG:
252 xasprintf(&args->errmsg, "Updater failed: %s", up->errmsg);
254 args->status = STATUS_FAILURE;
256 case UPDATER_ERR_READ:
257 if (stream_eof(up->rd)) {
258 xasprintf(&args->errmsg, "Updater failed: "
259 "Premature EOF from server");
261 xasprintf(&args->errmsg, "Updater failed: "
262 "Network read failure: %s", strerror(errno));
264 args->status = STATUS_TRANSIENTFAILURE;
266 case UPDATER_ERR_DELETELIM:
267 xasprintf(&args->errmsg, "Updater failed: "
268 "File deletion limit exceeded");
269 args->status = STATUS_FAILURE;
273 args->status = STATUS_SUCCESS;
279 updater_batch(struct updater *up, int isfixups)
284 struct file_update *fup;
285 char *line, *cmd, *errmsg, *collname, *release;
289 STAILQ_FOREACH(coll, &up->config->colls, co_next) {
290 if (coll->co_options & CO_SKIP)
292 umask(coll->co_umask);
293 line = stream_getln(rd, NULL);
295 return (UPDATER_ERR_READ);
296 cmd = proto_get_ascii(&line);
297 collname = proto_get_ascii(&line);
298 release = proto_get_ascii(&line);
299 if (release == NULL || line != NULL)
300 return (UPDATER_ERR_PROTO);
301 if (strcmp(cmd, "COLL") != 0 ||
302 strcmp(collname, coll->co_name) != 0 ||
303 strcmp(release, coll->co_release) != 0)
304 return (UPDATER_ERR_PROTO);
307 lprintf(1, "Updating collection %s/%s\n", coll->co_name,
310 if (coll->co_options & CO_COMPRESS)
311 stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL);
313 st = status_open(coll, coll->co_scantime, &errmsg);
316 return (UPDATER_ERR_MSG);
318 fup = fup_new(coll, st);
319 error = updater_docoll(up, fup, isfixups);
320 status_close(st, &errmsg);
322 if (errmsg != NULL) {
323 /* Discard previous error. */
324 if (up->errmsg != NULL)
327 return (UPDATER_ERR_MSG);
332 if (coll->co_options & CO_COMPRESS)
333 stream_filter_stop(rd);
335 line = stream_getln(rd, NULL);
337 return (UPDATER_ERR_READ);
338 if (strcmp(line, ".") != 0)
339 return (UPDATER_ERR_PROTO);
344 updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
348 struct statusrec srbuf, *sr;
349 struct fattr *rcsattr, *tmp;
350 char *attr, *cmd, *blocksize, *line, *msg;
351 char *name, *tag, *date, *revdate;
352 char *expand, *wantmd5, *revnum;
353 char *optstr, *rcsopt, *pos;
356 int attic, error, needfixupmsg;
361 needfixupmsg = isfixups;
362 while ((line = stream_getln(rd, NULL)) != NULL) {
363 if (strcmp(line, ".") == 0)
365 memset(&srbuf, 0, sizeof(srbuf));
367 lprintf(1, "Applying fixups for collection %s/%s\n",
368 coll->co_name, coll->co_release);
371 cmd = proto_get_ascii(&line);
372 if (cmd == NULL || strlen(cmd) != 1)
373 return (UPDATER_ERR_PROTO);
376 /* Update recorded information for checked-out file. */
377 name = proto_get_ascii(&line);
378 tag = proto_get_ascii(&line);
379 date = proto_get_ascii(&line);
380 revnum = proto_get_ascii(&line);
381 revdate = proto_get_ascii(&line);
382 attr = proto_get_ascii(&line);
383 if (attr == NULL || line != NULL)
384 return (UPDATER_ERR_PROTO);
386 rcsattr = fattr_decode(attr);
388 return (UPDATER_ERR_PROTO);
390 error = fup_prepare(fup, name, 0);
392 return (UPDATER_ERR_PROTO);
393 error = updater_setattrs(up, fup, name, tag, date,
394 revnum, revdate, rcsattr);
400 /* Checkout dead file. */
401 name = proto_get_ascii(&line);
402 tag = proto_get_ascii(&line);
403 date = proto_get_ascii(&line);
404 attr = proto_get_ascii(&line);
405 if (attr == NULL || line != NULL)
406 return (UPDATER_ERR_PROTO);
408 error = fup_prepare(fup, name, 0);
410 return (UPDATER_ERR_PROTO);
411 /* Theoritically, the file does not exist on the client.
412 Just to make sure, we'll delete it here, if it
414 if (access(fup->destpath, F_OK) == 0) {
415 error = updater_delete(up, fup);
421 sr->sr_type = SR_CHECKOUTDEAD;
425 sr->sr_serverattr = fattr_decode(attr);
426 if (sr->sr_serverattr == NULL)
427 return (UPDATER_ERR_PROTO);
429 error = status_put(fup->st, sr);
430 fattr_free(sr->sr_serverattr);
432 up->errmsg = status_errmsg(fup->st);
433 return (UPDATER_ERR_MSG);
437 /* Update live checked-out file. */
438 name = proto_get_ascii(&line);
439 tag = proto_get_ascii(&line);
440 date = proto_get_ascii(&line);
441 proto_get_ascii(&line); /* XXX - oldRevNum */
442 proto_get_ascii(&line); /* XXX - fromAttic */
443 proto_get_ascii(&line); /* XXX - logLines */
444 expand = proto_get_ascii(&line);
445 attr = proto_get_ascii(&line);
446 wantmd5 = proto_get_ascii(&line);
447 if (wantmd5 == NULL || line != NULL)
448 return (UPDATER_ERR_PROTO);
451 sr->sr_type = SR_CHECKOUTLIVE;
452 sr->sr_file = xstrdup(name);
453 sr->sr_date = xstrdup(date);
454 sr->sr_tag = xstrdup(tag);
455 sr->sr_serverattr = fattr_decode(attr);
456 if (sr->sr_serverattr == NULL)
457 return (UPDATER_ERR_PROTO);
459 fup->expand = keyword_decode_expand(expand);
460 if (fup->expand == -1)
461 return (UPDATER_ERR_PROTO);
462 error = fup_prepare(fup, name, 0);
464 return (UPDATER_ERR_PROTO);
466 fup->wantmd5 = xstrdup(wantmd5);
467 fup->temppath = tempname(fup->destpath);
468 error = updater_diff(up, fup);
473 /* Update dead checked-out file. */
474 name = proto_get_ascii(&line);
475 tag = proto_get_ascii(&line);
476 date = proto_get_ascii(&line);
477 attr = proto_get_ascii(&line);
478 if (attr == NULL || line != NULL)
479 return (UPDATER_ERR_PROTO);
481 error = fup_prepare(fup, name, 0);
483 return (UPDATER_ERR_PROTO);
484 error = updater_delete(up, fup);
488 sr->sr_type = SR_CHECKOUTDEAD;
492 sr->sr_serverattr = fattr_decode(attr);
493 if (sr->sr_serverattr == NULL)
494 return (UPDATER_ERR_PROTO);
495 error = status_put(fup->st, sr);
496 fattr_free(sr->sr_serverattr);
498 up->errmsg = status_errmsg(fup->st);
499 return (UPDATER_ERR_MSG);
505 name = proto_get_ascii(&line);
506 tag = proto_get_ascii(&line);
507 date = proto_get_ascii(&line);
508 revnum = proto_get_ascii(&line);
509 revdate = proto_get_ascii(&line);
510 attr = proto_get_ascii(&line);
511 if (attr == NULL || line != NULL)
512 return (UPDATER_ERR_PROTO);
515 sr->sr_type = SR_CHECKOUTLIVE;
516 sr->sr_file = xstrdup(name);
517 sr->sr_tag = xstrdup(tag);
518 sr->sr_date = xstrdup(date);
519 sr->sr_revnum = xstrdup(revnum);
520 sr->sr_revdate = xstrdup(revdate);
521 sr->sr_serverattr = fattr_decode(attr);
522 if (sr->sr_serverattr == NULL)
523 return (UPDATER_ERR_PROTO);
525 t = rcsdatetotime(revdate);
527 return (UPDATER_ERR_PROTO);
529 sr->sr_clientattr = fattr_new(FT_FILE, t);
530 tmp = fattr_forcheckout(sr->sr_serverattr,
532 fattr_override(sr->sr_clientattr, tmp, FA_MASK);
534 fattr_mergedefault(sr->sr_clientattr);
535 error = fup_prepare(fup, name, 0);
537 return (UPDATER_ERR_PROTO);
538 fup->temppath = tempname(fup->destpath);
540 error = updater_checkout(up, fup, 1);
542 error = updater_checkout(up, fup, 0);
548 name = proto_get_ascii(&line);
549 if (name == NULL || line != NULL)
550 return (UPDATER_ERR_PROTO);
551 error = fup_prepare(fup, name, 0);
553 return (UPDATER_ERR_PROTO);
554 error = updater_delete(up, fup);
557 error = status_delete(fup->st, name, 0);
559 up->errmsg = status_errmsg(fup->st);
560 return (UPDATER_ERR_MSG);
566 name = proto_get_ascii(&line);
567 attr = proto_get_ascii(&line);
568 if (name == NULL || attr == NULL || line != NULL)
569 return (UPDATER_ERR_PROTO);
570 attic = (cmd[0] == 'a');
571 error = fup_prepare(fup, name, attic);
573 return (UPDATER_ERR_PROTO);
575 fup->temppath = tempname(fup->destpath);
577 sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE;
578 sr->sr_file = xstrdup(name);
579 sr->sr_serverattr = fattr_decode(attr);
580 if (sr->sr_serverattr == NULL)
581 return (UPDATER_ERR_PROTO);
583 lprintf(1, " Create %s -> Attic\n", name);
585 lprintf(1, " Create %s\n", name);
586 error = updater_addfile(up, fup, attr, 0);
591 name = proto_get_ascii(&line);
592 attr = proto_get_ascii(&line);
593 blocksize = proto_get_ascii(&line);
594 wantmd5 = proto_get_ascii(&line);
595 if (name == NULL || attr == NULL || blocksize == NULL ||
597 return (UPDATER_ERR_PROTO);
599 error = fup_prepare(fup, name, 0);
601 return (UPDATER_ERR_PROTO);
602 fup->wantmd5 = xstrdup(wantmd5);
603 fup->temppath = tempname(fup->destpath);
605 sr->sr_file = xstrdup(name);
606 sr->sr_serverattr = fattr_decode(attr);
607 sr->sr_type = SR_FILELIVE;
608 if (sr->sr_serverattr == NULL)
609 return (UPDATER_ERR_PROTO);
610 error = updater_rsync(up, fup, strtol(blocksize, NULL,
617 * Create directory and add DirDown entry in status
620 name = proto_get_ascii(&line);
621 if (name == NULL || line != NULL)
622 return (UPDATER_ERR_PROTO);
623 error = fup_prepare(fup, name, 0);
625 return (UPDATER_ERR_PROTO);
627 sr->sr_type = SR_DIRDOWN;
628 sr->sr_file = xstrdup(name);
629 sr->sr_serverattr = NULL;
630 sr->sr_clientattr = fattr_new(FT_DIRECTORY, -1);
631 fattr_mergedefault(sr->sr_clientattr);
633 error = mkdirhier(fup->destpath, coll->co_umask);
635 return (UPDATER_ERR_PROTO);
636 if (access(fup->destpath, F_OK) != 0) {
637 lprintf(1, " Mkdir %s\n", name);
638 error = fattr_makenode(sr->sr_clientattr,
641 return (UPDATER_ERR_PROTO);
643 error = status_put(fup->st, sr);
645 up->errmsg = status_errmsg(fup->st);
646 return (UPDATER_ERR_MSG);
650 /* Remove DirDown entry in status file. */
651 name = proto_get_ascii(&line);
652 if (name == NULL || line != NULL)
653 return (UPDATER_ERR_PROTO);
654 error = fup_prepare(fup, name, 0);
656 return (UPDATER_ERR_PROTO);
657 error = status_delete(fup->st, name, 0);
659 up->errmsg = status_errmsg(fup->st);
660 return (UPDATER_ERR_MSG);
665 * Set attributes of directory and update DirUp entry in
668 name = proto_get_ascii(&line);
670 return (UPDATER_ERR_PROTO);
671 attr = proto_get_ascii(&line);
672 if (attr == NULL || line != NULL)
673 return (UPDATER_ERR_PROTO);
674 error = fup_prepare(fup, name, 0);
676 return (UPDATER_ERR_PROTO);
677 error = updater_setdirattrs(up, coll, fup, name, attr);
683 * Remove directory and delete its DirUp entry in status
686 name = proto_get_ascii(&line);
687 if (name == NULL || line != NULL)
688 return (UPDATER_ERR_PROTO);
689 error = fup_prepare(fup, name, 0);
691 return (UPDATER_ERR_PROTO);
692 lprintf(1, " Rmdir %s\n", name);
693 updater_deletefile(fup->destpath);
694 error = status_delete(fup->st, name, 0);
696 up->errmsg = status_errmsg(fup->st);
697 return (UPDATER_ERR_MSG);
702 name = proto_get_ascii(&line);
704 return (UPDATER_ERR_PROTO);
705 attr = proto_get_ascii(&line);
706 if (attr == NULL || line != NULL)
707 return (UPDATER_ERR_PROTO);
708 attic = (cmd[0] == 'l');
710 sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE;
711 sr->sr_file = xstrdup(name);
712 sr->sr_serverattr = fattr_decode(attr);
713 sr->sr_clientattr = fattr_decode(attr);
714 if (sr->sr_serverattr == NULL ||
715 sr->sr_clientattr == NULL)
716 return (UPDATER_ERR_PROTO);
718 /* Save space. Described in detail in updatefile. */
719 if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT)
720 || fattr_getlinkcount(sr->sr_clientattr) <= 1)
721 fattr_maskout(sr->sr_clientattr,
723 fattr_maskout(sr->sr_clientattr, FA_FLAGS);
724 error = status_put(fup->st, sr);
726 up->errmsg = status_errmsg(fup->st);
727 return (UPDATER_ERR_MSG);
732 name = proto_get_ascii(&line);
733 attr = proto_get_ascii(&line);
734 if (name == NULL || attr == NULL || line != NULL)
735 return (UPDATER_ERR_PROTO);
736 attic = (cmd[0] == 'n');
737 error = fup_prepare(fup, name, attic);
739 return (UPDATER_ERR_PROTO);
741 sr->sr_type = (attic ? SR_FILEDEAD : SR_FILELIVE);
742 sr->sr_file = xstrdup(name);
743 sr->sr_serverattr = fattr_decode(attr);
744 sr->sr_clientattr = fattr_new(FT_SYMLINK, -1);
745 fattr_mergedefault(sr->sr_clientattr);
746 fattr_maskout(sr->sr_clientattr, FA_FLAGS);
747 error = updater_updatenode(up, coll, fup, name, attr);
753 name = proto_get_ascii(&line);
754 attr = proto_get_ascii(&line);
755 optstr = proto_get_ascii(&line);
756 wantmd5 = proto_get_ascii(&line);
757 rcsopt = NULL; /* XXX: Not supported. */
758 if (attr == NULL || line != NULL || wantmd5 == NULL)
759 return (UPDATER_ERR_PROTO);
760 attic = (cmd[0] == 'v');
761 error = fup_prepare(fup, name, attic);
763 return (UPDATER_ERR_PROTO);
764 fup->temppath = tempname(fup->destpath);
765 fup->wantmd5 = xstrdup(wantmd5);
767 sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE;
768 sr->sr_file = xstrdup(name);
769 sr->sr_serverattr = fattr_decode(attr);
770 if (sr->sr_serverattr == NULL)
771 return (UPDATER_ERR_PROTO);
774 error = updater_rcsedit(up, fup, name, rcsopt);
780 name = proto_get_ascii(&line);
781 attr = proto_get_ascii(&line);
782 if (name == NULL || attr == NULL || line != NULL)
783 return (UPDATER_ERR_PROTO);
784 attic = (cmd[0] == 'x');
785 error = fup_prepare(fup, name, attic);
787 return (UPDATER_ERR_PROTO);
789 fup->temppath = tempname(fup->destpath);
791 sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE;
792 sr->sr_file = xstrdup(name);
793 sr->sr_serverattr = fattr_decode(attr);
794 if (sr->sr_serverattr == NULL)
795 return (UPDATER_ERR_PROTO);
796 lprintf(1, " Fixup %s\n", name);
797 error = updater_addfile(up, fup, attr, 1);
802 name = proto_get_ascii(&line);
803 attr = proto_get_ascii(&line);
804 pos = proto_get_ascii(&line);
805 if (name == NULL || attr == NULL || pos == NULL ||
807 return (UPDATER_ERR_PROTO);
808 error = fup_prepare(fup, name, 0);
809 fup->temppath = tempname(fup->destpath);
811 sr->sr_type = SR_FILELIVE;
812 sr->sr_file = xstrdup(name);
813 sr->sr_serverattr = fattr_decode(attr);
814 if (sr->sr_serverattr == NULL)
815 return (UPDATER_ERR_PROTO);
816 position = strtol(pos, NULL, 10);
817 lprintf(1, " Append to %s\n", name);
818 error = updater_append_file(up, fup, position);
823 /* Warning from server. */
824 msg = proto_get_rest(&line);
826 return (UPDATER_ERR_PROTO);
827 lprintf(-1, "Server warning: %s\n", msg);
830 return (UPDATER_ERR_PROTO);
835 return (UPDATER_ERR_READ);
841 updater_delete(struct updater *up, struct file_update *fup)
843 struct config *config;
848 if (coll->co_options & CO_DELETE) {
849 lprintf(1, " Delete %s\n", fup->coname);
850 if (config->deletelim >= 0 &&
851 up->deletecount >= config->deletelim)
852 return (UPDATER_ERR_DELETELIM);
854 updater_deletefile(fup->destpath);
855 if (coll->co_options & CO_CHECKOUTMODE)
856 updater_prunedirs(coll->co_prefix, fup->destpath);
858 lprintf(1," NoDelete %s\n", fup->coname);
864 updater_deletefile(const char *path)
868 error = fattr_delete(path);
869 if (error && errno != ENOENT) {
870 lprintf(-1, "Cannot delete \"%s\": %s\n",
871 path, strerror(errno));
876 updater_setattrs(struct updater *up, struct file_update *fup, char *name,
877 char *tag, char *date, char *revnum, char *revdate, struct fattr *rcsattr)
882 struct fattr *fileattr, *fa;
888 path = fup->destpath;
890 fileattr = fattr_frompath(path, FATTR_NOFOLLOW);
891 if (fileattr == NULL) {
892 /* The file has vanished. */
893 error = status_delete(st, name, 0);
895 up->errmsg = status_errmsg(st);
896 return (UPDATER_ERR_MSG);
900 fa = fattr_forcheckout(rcsattr, coll->co_umask);
901 fattr_override(fileattr, fa, FA_MASK);
904 rv = fattr_install(fileattr, path, NULL);
906 lprintf(1, " SetAttrs %s\n", fup->coname);
907 fattr_free(fileattr);
908 xasprintf(&up->errmsg, "Cannot set attributes for \"%s\": %s",
909 path, strerror(errno));
910 return (UPDATER_ERR_MSG);
913 lprintf(1, " SetAttrs %s\n", fup->coname);
914 fattr_free(fileattr);
915 fileattr = fattr_frompath(path, FATTR_NOFOLLOW);
916 if (fileattr == NULL) {
917 /* We're being very unlucky. */
918 error = status_delete(st, name, 0);
920 up->errmsg = status_errmsg(st);
921 return (UPDATER_ERR_MSG);
927 fattr_maskout(fileattr, FA_COIGNORE);
929 sr.sr_type = SR_CHECKOUTLIVE;
933 sr.sr_revnum = revnum;
934 sr.sr_revdate = revdate;
935 sr.sr_clientattr = fileattr;
936 sr.sr_serverattr = rcsattr;
938 error = status_put(st, &sr);
939 fattr_free(fileattr);
941 up->errmsg = status_errmsg(st);
942 return (UPDATER_ERR_MSG);
948 updater_updatefile(struct updater *up, struct file_update *fup,
949 const char *md5, int isfixup)
953 struct statusrec *sr;
954 struct fattr *fileattr;
961 if (strcmp(fup->wantmd5, md5) != 0) {
963 lprintf(-1, "%s: Checksum mismatch -- "
964 "file not updated\n", fup->destpath);
966 lprintf(-1, "%s: Checksum mismatch -- "
967 "will transfer entire file\n", fup->destpath);
968 fixups_put(up->config->fixups, fup->coll, sr->sr_file);
970 if (coll->co_options & CO_KEEPBADFILES)
971 lprintf(-1, "Bad version saved in %s\n", fup->temppath);
973 updater_deletefile(fup->temppath);
977 fattr_umask(sr->sr_clientattr, coll->co_umask);
978 rv = fattr_install(sr->sr_clientattr, fup->destpath, fup->temppath);
980 xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s",
981 fup->temppath, fup->destpath, strerror(errno));
982 return (UPDATER_ERR_MSG);
987 * We weren't necessarily able to set all the file attributes to the
988 * desired values, and any executes may have altered the attributes.
989 * To make sure we record the actual attribute values, we fetch
990 * them from the file.
992 * However, we preserve the link count as received from the
993 * server. This is important for preserving hard links in mirror
996 fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
997 if (fileattr == NULL) {
998 xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath,
1000 return (UPDATER_ERR_MSG);
1002 fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT);
1003 fattr_free(sr->sr_clientattr);
1004 sr->sr_clientattr = fileattr;
1007 * To save space, don't write out the device and inode unless
1008 * the link count is greater than 1. These attributes are used
1009 * only for detecting hard links. If the link count is 1 then we
1010 * know there aren't any hard links.
1012 if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) ||
1013 fattr_getlinkcount(sr->sr_clientattr) <= 1)
1014 fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE);
1016 if (coll->co_options & CO_CHECKOUTMODE)
1017 fattr_maskout(sr->sr_clientattr, FA_COIGNORE);
1019 error = status_put(st, sr);
1021 up->errmsg = status_errmsg(st);
1022 return (UPDATER_ERR_MSG);
1028 * Update attributes of a directory.
1031 updater_setdirattrs(struct updater *up, struct coll *coll,
1032 struct file_update *fup, char *name, char *attr)
1034 struct statusrec *sr;
1039 sr->sr_type = SR_DIRUP;
1040 sr->sr_file = xstrdup(name);
1041 sr->sr_clientattr = fattr_decode(attr);
1042 sr->sr_serverattr = fattr_decode(attr);
1043 if (sr->sr_clientattr == NULL || sr->sr_serverattr == NULL)
1044 return (UPDATER_ERR_PROTO);
1045 fattr_mergedefault(sr->sr_clientattr);
1046 fattr_umask(sr->sr_clientattr, coll->co_umask);
1047 rv = fattr_install(sr->sr_clientattr, fup->destpath, NULL);
1048 lprintf(1, " SetAttrs %s\n", name);
1050 xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s",
1051 fup->temppath, fup->destpath, strerror(errno));
1052 return (UPDATER_ERR_MSG);
1055 * Now, make sure they were set and record what was set in the status
1058 fa = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
1060 xasprintf(&up->errmsg, "Cannot open \%s\": %s", fup->destpath,
1062 return (UPDATER_ERR_MSG);
1064 fattr_free(sr->sr_clientattr);
1065 fattr_maskout(fa, FA_FLAGS);
1066 sr->sr_clientattr = fa;
1067 error = status_put(fup->st, sr);
1069 up->errmsg = status_errmsg(fup->st);
1070 return (UPDATER_ERR_MSG);
1077 updater_diff(struct updater *up, struct file_update *fup)
1079 char md5[MD5_DIGEST_SIZE];
1081 struct statusrec *sr;
1082 struct fattr *fa, *tmp;
1083 char *author, *path, *revnum, *revdate;
1089 path = fup->destpath;
1091 lprintf(1, " Edit %s\n", fup->coname);
1092 while ((line = stream_getln(up->rd, NULL)) != NULL) {
1093 if (strcmp(line, ".") == 0)
1095 cmd = proto_get_ascii(&line);
1096 if (cmd == NULL || strcmp(cmd, "D") != 0)
1097 return (UPDATER_ERR_PROTO);
1098 revnum = proto_get_ascii(&line);
1099 proto_get_ascii(&line); /* XXX - diffbase */
1100 revdate = proto_get_ascii(&line);
1101 author = proto_get_ascii(&line);
1102 if (author == NULL || line != NULL)
1103 return (UPDATER_ERR_PROTO);
1104 if (sr->sr_revnum != NULL)
1105 free(sr->sr_revnum);
1106 if (sr->sr_revdate != NULL)
1107 free(sr->sr_revdate);
1108 if (fup->author != NULL)
1110 sr->sr_revnum = xstrdup(revnum);
1111 sr->sr_revdate = xstrdup(revdate);
1112 fup->author = xstrdup(author);
1113 if (fup->orig == NULL) {
1114 /* First patch, the "origin" file is the one we have. */
1115 fup->orig = stream_open_file(path, O_RDONLY);
1116 if (fup->orig == NULL) {
1117 xasprintf(&up->errmsg, "%s: Cannot open: %s",
1118 path, strerror(errno));
1119 return (UPDATER_ERR_MSG);
1122 /* Subsequent patches. */
1123 stream_close(fup->orig);
1124 fup->orig = fup->to;
1125 stream_rewind(fup->orig);
1126 unlink(fup->temppath);
1127 free(fup->temppath);
1128 fup->temppath = tempname(path);
1130 fup->to = stream_open_file(fup->temppath,
1131 O_RDWR | O_CREAT | O_TRUNC, 0600);
1132 if (fup->to == NULL) {
1133 xasprintf(&up->errmsg, "%s: Cannot open: %s",
1134 fup->temppath, strerror(errno));
1135 return (UPDATER_ERR_MSG);
1137 lprintf(2, " Add delta %s %s %s\n", sr->sr_revnum,
1138 sr->sr_revdate, fup->author);
1139 error = updater_diff_batch(up, fup);
1144 return (UPDATER_ERR_READ);
1146 fa = fattr_frompath(path, FATTR_FOLLOW);
1147 tmp = fattr_forcheckout(sr->sr_serverattr, coll->co_umask);
1148 fattr_override(fa, tmp, FA_MASK);
1150 fattr_maskout(fa, FA_MODTIME);
1151 sr->sr_clientattr = fa;
1153 if (MD5_File(fup->temppath, md5) == -1) {
1154 xasprintf(&up->errmsg,
1155 "Cannot calculate checksum for \"%s\": %s",
1156 path, strerror(errno));
1157 return (UPDATER_ERR_MSG);
1159 error = updater_updatefile(up, fup, md5, 0);
1164 * Edit a file and add delta.
1167 updater_diff_batch(struct updater *up, struct file_update *fup)
1170 char *cmd, *line, *state, *tok;
1175 while ((line = stream_getln(rd, NULL)) != NULL) {
1176 if (strcmp(line, ".") == 0)
1178 cmd = proto_get_ascii(&line);
1179 if (cmd == NULL || strlen(cmd) != 1) {
1180 error = UPDATER_ERR_PROTO;
1185 line = stream_getln(rd, NULL);
1186 /* XXX - We're just eating the log for now. */
1187 while (line != NULL && strcmp(line, ".") != 0 &&
1188 strcmp(line, ".+") != 0)
1189 line = stream_getln(rd, NULL);
1191 error = UPDATER_ERR_READ;
1196 tok = proto_get_ascii(&line);
1197 if (tok == NULL || line != NULL) {
1198 error = UPDATER_ERR_PROTO;
1203 state = xstrdup(tok);
1206 error = updater_diff_apply(up, fup, state);
1211 error = UPDATER_ERR_PROTO;
1216 error = UPDATER_ERR_READ;
1229 updater_diff_apply(struct updater *up, struct file_update *fup, char *state)
1231 struct diffinfo dibuf, *di;
1233 struct statusrec *sr;
1240 di->di_rcsfile = sr->sr_file;
1241 di->di_cvsroot = coll->co_cvsroot;
1242 di->di_revnum = sr->sr_revnum;
1243 di->di_revdate = sr->sr_revdate;
1244 di->di_author = fup->author;
1245 di->di_tag = sr->sr_tag;
1246 di->di_state = state;
1247 di->di_expand = fup->expand;
1249 error = diff_apply(up->rd, fup->orig, fup->to, coll->co_keyword, di, 1);
1251 /* XXX Bad error message */
1252 xasprintf(&up->errmsg, "Bad diff from server");
1253 return (UPDATER_ERR_MSG);
1258 /* Update or create a node. */
1260 updater_updatenode(struct updater *up, struct coll *coll,
1261 struct file_update *fup, char *name, char *attr)
1263 struct fattr *fa, *fileattr;
1265 struct statusrec *sr;
1270 fa = fattr_decode(attr);
1272 if (fattr_type(fa) == FT_SYMLINK) {
1273 lprintf(1, " Symlink %s -> %s\n", name,
1274 fattr_getlinktarget(fa));
1276 lprintf(1, " Mknod %s\n", name);
1279 /* Create directory. */
1280 error = mkdirhier(fup->destpath, coll->co_umask);
1282 return (UPDATER_ERR_PROTO);
1284 /* If it does not exist, create it. */
1285 if (access(fup->destpath, F_OK) != 0)
1286 fattr_makenode(fa, fup->destpath);
1289 * Coming from attic? I don't think this is a problem since we have
1290 * determined attic before we call this function (Look at UpdateNode in
1293 fattr_umask(fa, coll->co_umask);
1294 rv = fattr_install(fa, fup->destpath, fup->temppath);
1296 xasprintf(&up->errmsg, "Cannot update attributes on "
1297 "\"%s\": %s", fup->destpath, strerror(errno));
1298 return (UPDATER_ERR_MSG);
1301 * XXX: Executes not implemented. Have not encountered much use for it
1305 * We weren't necessarily able to set all the file attributes to the
1306 * desired values, and any executes may have altered the attributes.
1307 * To make sure we record the actual attribute values, we fetch
1308 * them from the file.
1310 * However, we preserve the link count as received from the
1311 * server. This is important for preserving hard links in mirror
1314 fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
1315 if (fileattr == NULL) {
1316 xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath,
1318 return (UPDATER_ERR_MSG);
1320 fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT);
1321 fattr_free(sr->sr_clientattr);
1322 sr->sr_clientattr = fileattr;
1325 * To save space, don't write out the device and inode unless
1326 * the link count is greater than 1. These attributes are used
1327 * only for detecting hard links. If the link count is 1 then we
1328 * know there aren't any hard links.
1330 if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) ||
1331 fattr_getlinkcount(sr->sr_clientattr) <= 1)
1332 fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE);
1334 /* If it is a symlink, write only out it's path. */
1335 if (fattr_type(fa) == FT_SYMLINK) {
1336 fattr_maskout(sr->sr_clientattr, ~(FA_FILETYPE |
1339 fattr_maskout(sr->sr_clientattr, FA_FLAGS);
1340 error = status_put(st, sr);
1342 up->errmsg = status_errmsg(st);
1343 return (UPDATER_ERR_MSG);
1351 * Fetches a new file in CVS mode.
1354 updater_addfile(struct updater *up, struct file_update *fup, char *attr,
1359 struct statusrec *sr;
1362 char md5[MD5_DIGEST_SIZE];
1364 off_t fsize, remains;
1365 char *cmd, *line, *path;
1369 path = fup->destpath;
1371 fa = fattr_decode(attr);
1372 fsize = fattr_filesize(fa);
1374 error = mkdirhier(path, coll->co_umask);
1376 return (UPDATER_ERR_PROTO);
1377 to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, 0755);
1379 xasprintf(&up->errmsg, "%s: Cannot create: %s",
1380 fup->temppath, strerror(errno));
1381 return (UPDATER_ERR_MSG);
1383 stream_filter_start(to, STREAM_FILTER_MD5, md5);
1386 nread = stream_read(up->rd, buf, (BUFSIZE > remains ?
1387 remains : BUFSIZE));
1389 return (UPDATER_ERR_PROTO);
1391 if (stream_write(to, buf, nread) == -1)
1393 } while (remains > 0);
1395 line = stream_getln(up->rd, NULL);
1397 return (UPDATER_ERR_PROTO);
1398 /* Check for EOF. */
1399 if (!(*line == '.' || (strncmp(line, ".<", 2) != 0)))
1400 return (UPDATER_ERR_PROTO);
1401 line = stream_getln(up->rd, NULL);
1403 return (UPDATER_ERR_PROTO);
1405 cmd = proto_get_ascii(&line);
1406 fup->wantmd5 = proto_get_ascii(&line);
1407 if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0)
1408 return (UPDATER_ERR_PROTO);
1410 sr->sr_clientattr = fattr_frompath(fup->temppath, FATTR_NOFOLLOW);
1411 if (sr->sr_clientattr == NULL)
1412 return (UPDATER_ERR_PROTO);
1413 fattr_override(sr->sr_clientattr, sr->sr_serverattr,
1414 FA_MODTIME | FA_MASK);
1415 error = updater_updatefile(up, fup, md5, isfixup);
1416 fup->wantmd5 = NULL; /* So that it doesn't get freed. */
1419 xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1421 return (UPDATER_ERR_MSG);
1425 updater_checkout(struct updater *up, struct file_update *fup, int isfixup)
1427 char md5[MD5_DIGEST_SIZE];
1428 struct statusrec *sr;
1433 char *cmd, *path, *line;
1438 path = fup->destpath;
1441 lprintf(1, " Fixup %s\n", fup->coname);
1443 lprintf(1, " Checkout %s\n", fup->coname);
1444 error = mkdirhier(path, coll->co_umask);
1446 xasprintf(&up->errmsg,
1447 "Cannot create directories leading to \"%s\": %s",
1448 path, strerror(errno));
1449 return (UPDATER_ERR_MSG);
1452 to = stream_open_file(fup->temppath,
1453 O_WRONLY | O_CREAT | O_TRUNC, 0600);
1455 xasprintf(&up->errmsg, "%s: Cannot create: %s",
1456 fup->temppath, strerror(errno));
1457 return (UPDATER_ERR_MSG);
1459 stream_filter_start(to, STREAM_FILTER_MD5, md5);
1460 line = stream_getln(up->rd, &size);
1462 while (line != NULL) {
1463 if (line[size - 1] == '\n')
1465 if ((size == 1 && *line == '.') ||
1466 (size == 2 && memcmp(line, ".+", 2) == 0))
1468 if (size >= 2 && memcmp(line, "..", 2) == 0) {
1473 nbytes = stream_write(to, "\n", 1);
1477 nbytes = stream_write(to, line, size);
1480 line = stream_getln(up->rd, &size);
1485 return (UPDATER_ERR_READ);
1487 if (size == 1 && *line == '.') {
1488 nbytes = stream_write(to, "\n", 1);
1493 /* Get the checksum line. */
1494 line = stream_getln(up->rd, NULL);
1496 return (UPDATER_ERR_READ);
1497 cmd = proto_get_ascii(&line);
1498 fup->wantmd5 = proto_get_ascii(&line);
1499 if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0)
1500 return (UPDATER_ERR_PROTO);
1501 error = updater_updatefile(up, fup, md5, isfixup);
1502 fup->wantmd5 = NULL; /* So that it doesn't get freed. */
1507 xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1509 return (UPDATER_ERR_MSG);
1513 * Remove all empty directories below file.
1514 * This function will trash the path passed to it.
1517 updater_prunedirs(char *base, char *file)
1522 while ((cp = strrchr(file, '/')) != NULL) {
1524 if (strcmp(base, file) == 0)
1526 error = rmdir(file);
1536 updater_rcsedit(struct updater *up, struct file_update *fup, char *name,
1540 struct stream *dest;
1541 struct statusrec *sr;
1544 struct fattr *oldfattr;
1545 char md5[MD5_DIGEST_SIZE];
1546 char *branch, *cmd, *expand, *line, *path, *revnum, *tag, *temppath;
1552 temppath = fup->temppath;
1553 path = fup->origpath != NULL ? fup->origpath : fup->destpath;
1556 /* If the path is new, we must create the Attic dir if needed. */
1557 if (fup->origpath != NULL) {
1558 error = mkdirhier(fup->destpath, coll->co_umask);
1560 xasprintf(&up->errmsg, "Unable to create Attic dir for "
1561 "%s\n", fup->origpath);
1562 return (UPDATER_ERR_MSG);
1566 * XXX: we could avoid parsing overhead if we're reading ahead before we
1569 oldfattr = fattr_frompath(path, FATTR_NOFOLLOW);
1570 if (oldfattr == NULL) {
1571 xasprintf(&up->errmsg, "%s: Cannot get attributes: %s", path,
1573 return (UPDATER_ERR_MSG);
1575 fattr_merge(sr->sr_serverattr, oldfattr);
1578 /* Macro for making touching an RCS file faster. */
1579 #define UPDATER_OPENRCS(rf, up, path, name, cvsroot, tag) do { \
1580 if ((rf) == NULL) { \
1581 lprintf(1, " Edit %s", fup->coname); \
1583 lprintf(1, " -> Attic"); \
1585 (rf) = rcsfile_frompath((path), (name), (cvsroot), \
1587 if ((rf) == NULL) { \
1588 xasprintf(&(up)->errmsg, \
1589 "Error reading rcsfile %s\n", (name)); \
1590 return (UPDATER_ERR_MSG); \
1595 while ((line = stream_getln(up->rd, NULL)) != NULL) {
1596 if (strcmp(line, ".") == 0)
1598 cmd = proto_get_ascii(&line);
1600 lprintf(-1, "Error editing %s\n", name);
1601 return (UPDATER_ERR_PROTO);
1605 branch = proto_get_ascii(&line);
1606 if (branch == NULL || line != NULL)
1607 return (UPDATER_ERR_PROTO);
1608 UPDATER_OPENRCS(rf, up, path, name,
1609 coll->co_cvsroot, coll->co_tag);
1612 UPDATER_OPENRCS(rf, up, path, name,
1613 coll->co_cvsroot, coll->co_tag);
1614 rcsfile_setval(rf, RCSFILE_BRANCH, NULL);
1617 UPDATER_OPENRCS(rf, up, path, name,
1618 coll->co_cvsroot, coll->co_tag);
1619 error = updater_addelta(rf, up->rd, line);
1624 revnum = proto_get_ascii(&line);
1625 if (revnum == NULL || line != NULL)
1626 return (UPDATER_ERR_PROTO);
1627 UPDATER_OPENRCS(rf, up, path, name,
1628 coll->co_cvsroot, coll->co_tag);
1629 rcsfile_deleterev(rf, revnum);
1632 expand = proto_get_ascii(&line);
1633 if (expand == NULL || line != NULL)
1634 return (UPDATER_ERR_PROTO);
1635 UPDATER_OPENRCS(rf, up, path, name,
1636 coll->co_cvsroot, coll->co_tag);
1637 rcsfile_setval(rf, RCSFILE_EXPAND, expand);
1640 tag = proto_get_ascii(&line);
1641 revnum = proto_get_ascii(&line);
1642 if (tag == NULL || revnum == NULL ||
1644 return (UPDATER_ERR_PROTO);
1645 UPDATER_OPENRCS(rf, up, path, name,
1646 coll->co_cvsroot, coll->co_tag);
1647 rcsfile_addtag(rf, tag, revnum);
1650 tag = proto_get_ascii(&line);
1651 revnum = proto_get_ascii(&line);
1652 if (tag == NULL || revnum == NULL ||
1654 return (UPDATER_ERR_PROTO);
1655 UPDATER_OPENRCS(rf, up, path, name,
1656 coll->co_cvsroot, coll->co_tag);
1657 rcsfile_deletetag(rf, tag, revnum);
1660 return (UPDATER_ERR_PROTO);
1665 fattr_maskout(oldfattr, ~FA_MODTIME);
1666 if (fattr_equal(oldfattr, sr->sr_serverattr))
1667 lprintf(1, " SetAttrs %s", fup->coname);
1669 lprintf(1, " Touch %s", fup->coname);
1670 /* Install new attributes. */
1671 fattr_umask(sr->sr_serverattr, coll->co_umask);
1672 fattr_install(sr->sr_serverattr, fup->destpath, NULL);
1674 lprintf(1, " -> Attic");
1676 fattr_free(oldfattr);
1680 /* Write and rename temp file. */
1681 dest = stream_open_file(fup->temppath,
1682 O_RDWR | O_CREAT | O_TRUNC, 0600);
1684 xasprintf(&up->errmsg, "Error opening file %s for writing: %s\n",
1685 fup->temppath, strerror(errno));
1686 return (UPDATER_ERR_MSG);
1688 stream_filter_start(dest, STREAM_FILTER_MD5RCS, md5);
1689 error = rcsfile_write(rf, dest);
1693 xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1695 return (UPDATER_ERR_MSG);
1699 sr->sr_clientattr = fattr_frompath(path, FATTR_NOFOLLOW);
1700 if (sr->sr_clientattr == NULL) {
1701 xasprintf(&up->errmsg, "%s: Cannot get attributes: %s",
1702 fup->destpath, strerror(errno));
1703 return (UPDATER_ERR_MSG);
1705 fattr_override(sr->sr_clientattr, sr->sr_serverattr,
1706 FA_MODTIME | FA_MASK);
1708 error = updater_updatefile(up, fup, md5, 0);
1709 fup->wantmd5 = NULL; /* So that it doesn't get freed. */
1713 /* Record its attributes since we touched it. */
1714 if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) ||
1715 fattr_getlinkcount(sr->sr_clientattr) <= 1)
1716 fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE);
1717 error = status_put(st, sr);
1719 up->errmsg = status_errmsg(st);
1720 return (UPDATER_ERR_MSG);
1724 /* In this case, we need to remove the old file afterwards. */
1725 /* XXX: Can we be sure that a file not edited is moved? I don't think
1726 * this is a problem, since if a file is moved, it should be edited to
1727 * show if it's dead or not.
1729 if (fup->origpath != NULL)
1730 updater_deletefile(fup->origpath);
1735 * Add a delta to a RCS file.
1738 updater_addelta(struct rcsfile *rf, struct stream *rd, char *cmdline)
1742 char *author, *cmd, *diffbase, *line, *logline;
1743 char *revdate, *revnum, *state, *textline;
1745 revnum = proto_get_ascii(&cmdline);
1746 diffbase = proto_get_ascii(&cmdline);
1747 revdate = proto_get_ascii(&cmdline);
1748 author = proto_get_ascii(&cmdline);
1751 if (revnum == NULL || revdate == NULL || author == NULL)
1752 return (UPDATER_ERR_PROTO);
1754 /* First add the delta so we have it. */
1755 d = rcsfile_addelta(rf, revnum, revdate, author, diffbase);
1757 lprintf(-1, "Error adding delta %s\n", revnum);
1758 return (UPDATER_ERR_READ);
1760 while ((line = stream_getln(rd, NULL)) != NULL) {
1761 if (strcmp(line, ".") == 0)
1763 cmd = proto_get_ascii(&line);
1766 /* Do the same as in 'C' command. */
1767 logline = stream_getln(rd, &size);
1768 while (logline != NULL) {
1769 if (size == 2 && *logline == '.')
1772 memcmp(logline, ".+", 2) == 0) {
1773 rcsdelta_truncatelog(d, -1);
1777 memcmp(logline, "..", 2) == 0) {
1781 if (rcsdelta_appendlog(d, logline, size)
1784 logline = stream_getln(rd, &size);
1789 /* XXX: Not supported. */
1792 state = proto_get_ascii(&line);
1794 return (UPDATER_ERR_PROTO);
1795 rcsdelta_setstate(d, state);
1798 /* Do the same as in 'C' command. */
1799 textline = stream_getln(rd, &size);
1800 while (textline != NULL) {
1801 if (size == 2 && *textline == '.')
1804 memcmp(textline, ".+", 2) == 0) {
1805 /* Truncate newline. */
1806 rcsdelta_truncatetext(d, -1);
1810 memcmp(textline, "..", 2) == 0) {
1814 if (rcsdelta_appendtext(d, textline,
1817 textline = stream_getln(rd, &size);
1827 updater_append_file(struct updater *up, struct file_update *fup, off_t pos)
1831 struct statusrec *sr;
1834 char buf[BUFSIZE], md5[MD5_DIGEST_SIZE];
1839 fa = sr->sr_serverattr;
1840 to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC,
1843 xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->temppath,
1845 return (UPDATER_ERR_MSG);
1847 fd = open(fup->destpath, O_RDONLY);
1849 xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->destpath,
1851 return (UPDATER_ERR_MSG);
1854 stream_filter_start(to, STREAM_FILTER_MD5, md5);
1855 /* First write the existing content. */
1856 while ((nread = read(fd, buf, BUFSIZE)) > 0) {
1857 if (stream_write(to, buf, nread) == -1)
1861 xasprintf(&up->errmsg, "%s: Error reading: %s",
1863 return (UPDATER_ERR_MSG);
1867 bytes = fattr_filesize(fa) - pos;
1868 /* Append the new data. */
1870 nread = stream_read(up->rd, buf,
1871 (BUFSIZE > bytes) ? bytes : BUFSIZE);
1873 return (UPDATER_ERR_PROTO);
1875 if (stream_write(to, buf, nread) == -1)
1877 } while (bytes > 0);
1880 line = stream_getln(up->rd, NULL);
1882 return (UPDATER_ERR_PROTO);
1883 /* Check for EOF. */
1884 if (!(*line == '.' || (strncmp(line, ".<", 2) != 0)))
1885 return (UPDATER_ERR_PROTO);
1886 line = stream_getln(up->rd, NULL);
1888 return (UPDATER_ERR_PROTO);
1890 cmd = proto_get_ascii(&line);
1891 fup->wantmd5 = proto_get_ascii(&line);
1892 if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0)
1893 return (UPDATER_ERR_PROTO);
1895 sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
1896 if (sr->sr_clientattr == NULL)
1897 return (UPDATER_ERR_PROTO);
1898 fattr_override(sr->sr_clientattr, sr->sr_serverattr,
1899 FA_MODTIME | FA_MASK);
1900 error = updater_updatefile(up, fup, md5, 0);
1901 fup->wantmd5 = NULL; /* So that it doesn't get freed. */
1904 xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1906 return (UPDATER_ERR_MSG);
1910 * Read file data from stream of checkout commands, and write it to the
1914 updater_read_checkout(struct stream *src, struct stream *dest)
1922 line = stream_getln(src, &size);
1923 while (line != NULL) {
1924 if (line[size - 1] == '\n')
1926 if ((size == 1 && *line == '.') ||
1927 (size == 2 && strncmp(line, ".+", 2) == 0))
1929 if (size >= 2 && strncmp(line, "..", 2) == 0) {
1934 nbytes = stream_write(dest, "\n", 1);
1936 return (UPDATER_ERR_MSG);
1938 nbytes = stream_write(dest, line, size);
1940 return (UPDATER_ERR_MSG);
1941 line = stream_getln(src, &size);
1945 return (UPDATER_ERR_READ);
1946 if (size == 1 && *line == '.') {
1947 nbytes = stream_write(dest, "\n", 1);
1949 return (UPDATER_ERR_MSG);
1954 /* Update file using the rsync protocol. */
1956 updater_rsync(struct updater *up, struct file_update *fup, size_t blocksize)
1958 struct statusrec *sr;
1960 char md5[MD5_DIGEST_SIZE];
1962 size_t blocknum, blockstart, blockcount;
1968 lprintf(1, " Rsync %s\n", fup->coname);
1969 /* First open all files that we are going to work on. */
1970 to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC,
1973 xasprintf(&up->errmsg, "%s: Cannot create: %s",
1974 fup->temppath, strerror(errno));
1975 return (UPDATER_ERR_MSG);
1977 orig = open(fup->destpath, O_RDONLY);
1979 xasprintf(&up->errmsg, "%s: Cannot open: %s",
1980 fup->destpath, strerror(errno));
1981 return (UPDATER_ERR_MSG);
1983 stream_filter_start(to, STREAM_FILTER_MD5, md5);
1985 error = updater_read_checkout(up->rd, to);
1987 xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1992 /* Buffer must contain blocksize bytes. */
1993 buf = xmalloc(blocksize);
1994 /* Done with the initial text, read and write chunks. */
1995 line = stream_getln(up->rd, NULL);
1996 while (line != NULL) {
1997 if (strcmp(line, ".") == 0)
1999 error = UPDATER_ERR_PROTO;
2000 if (proto_get_sizet(&line, &blockstart, 10) != 0)
2002 if (proto_get_sizet(&line, &blockcount, 10) != 0)
2004 /* Read blocks from original file. */
2005 lseek(orig, (blocksize * blockstart), SEEK_SET);
2006 error = UPDATER_ERR_MSG;
2007 for (blocknum = 0; blocknum < blockcount; blocknum++) {
2008 nbytes = read(orig, buf, blocksize);
2010 xasprintf(&up->errmsg, "%s: Cannot read: %s",
2011 fup->destpath, strerror(errno));
2014 nbytes = stream_write(to, buf, nbytes);
2016 xasprintf(&up->errmsg, "%s: Cannot write: %s",
2017 fup->temppath, strerror(errno));
2021 /* Get the remaining text from the server. */
2022 error = updater_read_checkout(up->rd, to);
2024 xasprintf(&up->errmsg, "%s: Cannot write: %s",
2025 fup->temppath, strerror(errno));
2028 line = stream_getln(up->rd, NULL);
2033 sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
2034 if (sr->sr_clientattr == NULL)
2035 return (UPDATER_ERR_PROTO);
2036 fattr_override(sr->sr_clientattr, sr->sr_serverattr,
2037 FA_MODTIME | FA_MASK);
2039 error = updater_updatefile(up, fup, md5, 0);
2040 fup->wantmd5 = NULL; /* So that it doesn't get freed. */