]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.bin/csup/updater.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.bin / csup / updater.c
1 /*-
2  * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/types.h>
30 #include <sys/stat.h>
31
32 #include <assert.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <stddef.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41
42 #include "config.h"
43 #include "diff.h"
44 #include "fattr.h"
45 #include "fixups.h"
46 #include "keyword.h"
47 #include "updater.h"
48 #include "misc.h"
49 #include "mux.h"
50 #include "proto.h"
51 #include "rcsfile.h"
52 #include "status.h"
53 #include "stream.h"
54
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. */
60
61 #define BUFSIZE 4096
62
63 /* Everything needed to update a file. */
64 struct file_update {
65         struct statusrec srbuf;
66         char *destpath;
67         char *temppath;
68         char *origpath;
69         char *coname;           /* Points somewhere in destpath. */
70         char *wantmd5;
71         struct coll *coll;
72         struct status *st;
73         /* Those are only used for diff updating. */
74         char *author;
75         struct stream *orig;
76         struct stream *to;
77         int attic;
78         int expand;
79 };
80
81 struct updater {
82         struct config *config;
83         struct stream *rd;
84         char *errmsg;
85         int deletecount;
86 };
87
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 *);
92
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 *,
100                      char *, int);
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,
107                      const char *, int);
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 *,
113                      char *);
114 static int       updater_rcsedit(struct updater *, struct file_update *, char *,
115                      char *);
116 int              updater_append_file(struct updater *, struct file_update *,
117                      off_t);
118 static int       updater_rsync(struct updater *, struct file_update *, size_t);
119 static int       updater_read_checkout(struct stream *, struct stream *);
120
121 static struct file_update *
122 fup_new(struct coll *coll, struct status *st)
123 {
124         struct file_update *fup;
125
126         fup = xmalloc(sizeof(struct file_update));
127         memset(fup, 0, sizeof(*fup));
128         fup->coll = coll;
129         fup->st = st;
130         return (fup);
131 }
132
133 static int
134 fup_prepare(struct file_update *fup, char *name, int attic)
135 {
136         struct coll *coll;
137
138         coll = fup->coll;
139         fup->attic = 0;
140         fup->origpath = NULL;
141
142         if (coll->co_options & CO_CHECKOUTMODE)
143                 fup->destpath = checkoutpath(coll->co_prefix, name);
144         else {
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) {
150                         free(fup->origpath);
151                         fup->origpath = NULL;
152                 }
153                 fup->attic = attic;
154         }
155         if (fup->destpath == NULL)
156                 return (-1);
157         fup->coname = fup->destpath + coll->co_prefixlen + 1;
158         return (0);
159 }
160
161 /* Called after each file update to reinit the structure. */
162 static void
163 fup_cleanup(struct file_update *fup)
164 {
165         struct statusrec *sr;
166
167         sr = &fup->srbuf;
168
169         if (fup->destpath != NULL) {
170                 free(fup->destpath);
171                 fup->destpath = NULL;
172         }
173         if (fup->temppath != NULL) {
174                 free(fup->temppath);
175                 fup->temppath = NULL;
176         }
177         if (fup->origpath != NULL) {
178                 free(fup->origpath);
179                 fup->origpath = NULL;
180         }
181         fup->coname = NULL;
182         if (fup->author != NULL) {
183                 free(fup->author);
184                 fup->author = NULL;
185         }
186         fup->expand = 0;
187         if (fup->wantmd5 != NULL) {
188                 free(fup->wantmd5);
189                 fup->wantmd5 = NULL;
190         }
191         if (fup->orig != NULL) {
192                 stream_close(fup->orig);
193                 fup->orig = NULL;
194         }
195         if (fup->to != NULL) {
196                 stream_close(fup->to);
197                 fup->to = NULL;
198         }
199         if (sr->sr_file != NULL)
200                 free(sr->sr_file);
201         if (sr->sr_tag != NULL)
202                 free(sr->sr_tag);
203         if (sr->sr_date != NULL)
204                 free(sr->sr_date);
205         if (sr->sr_revnum != NULL)
206                 free(sr->sr_revnum);
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));
212 }
213
214 static void
215 fup_free(struct file_update *fup)
216 {
217
218         fup_cleanup(fup);
219         free(fup);
220 }
221
222 void *
223 updater(void *arg)
224 {
225         struct thread_args *args;
226         struct updater upbuf, *up;
227         int error;
228
229         args = arg;
230
231         up = &upbuf;
232         up->config = args->config;
233         up->rd = args->rd;
234         up->errmsg = NULL;
235         up->deletecount = 0;
236
237         error = updater_batch(up, 0);
238
239         /*
240          * Make sure to close the fixups even in case of an error,
241          * so that the detailer thread doesn't block indefinitely.
242          */
243         fixups_close(up->config->fixups);
244         if (!error)
245                 error = updater_batch(up, 1);
246         switch (error) {
247         case UPDATER_ERR_PROTO:
248                 xasprintf(&args->errmsg, "Updater failed: Protocol error");
249                 args->status = STATUS_FAILURE;
250                 break;
251         case UPDATER_ERR_MSG:
252                 xasprintf(&args->errmsg, "Updater failed: %s", up->errmsg);
253                 free(up->errmsg);
254                 args->status = STATUS_FAILURE;
255                 break;
256         case UPDATER_ERR_READ:
257                 if (stream_eof(up->rd)) {
258                         xasprintf(&args->errmsg, "Updater failed: "
259                             "Premature EOF from server");
260                 } else {
261                         xasprintf(&args->errmsg, "Updater failed: "
262                             "Network read failure: %s", strerror(errno));
263                 }
264                 args->status = STATUS_TRANSIENTFAILURE;
265                 break;
266         case UPDATER_ERR_DELETELIM:
267                 xasprintf(&args->errmsg, "Updater failed: "
268                     "File deletion limit exceeded");
269                 args->status = STATUS_FAILURE;
270                 break;
271         default:
272                 assert(error == 0);
273                 args->status = STATUS_SUCCESS;
274         };
275         return (NULL);
276 }
277
278 static int
279 updater_batch(struct updater *up, int isfixups)
280 {
281         struct stream *rd;
282         struct coll *coll;
283         struct status *st;
284         struct file_update *fup;
285         char *line, *cmd, *errmsg, *collname, *release;
286         int error;
287
288         rd = up->rd;
289         STAILQ_FOREACH(coll, &up->config->colls, co_next) {
290                 if (coll->co_options & CO_SKIP)
291                         continue;
292                 umask(coll->co_umask);
293                 line = stream_getln(rd, NULL);
294                 if (line == 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);
305
306                 if (!isfixups)
307                         lprintf(1, "Updating collection %s/%s\n", coll->co_name,
308                             coll->co_release);
309
310                 if (coll->co_options & CO_COMPRESS)
311                         stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL);
312
313                 st = status_open(coll, coll->co_scantime, &errmsg);
314                 if (st == NULL) {
315                         up->errmsg = errmsg;
316                         return (UPDATER_ERR_MSG);
317                 }
318                 fup = fup_new(coll, st);
319                 error = updater_docoll(up, fup, isfixups);
320                 status_close(st, &errmsg);
321                 fup_free(fup);
322                 if (errmsg != NULL) {
323                         /* Discard previous error. */
324                         if (up->errmsg != NULL)
325                                 free(up->errmsg);
326                         up->errmsg = errmsg;
327                         return (UPDATER_ERR_MSG);
328                 }
329                 if (error)
330                         return (error);
331
332                 if (coll->co_options & CO_COMPRESS)
333                         stream_filter_stop(rd);
334         }
335         line = stream_getln(rd, NULL);
336         if (line == NULL)
337                 return (UPDATER_ERR_READ);
338         if (strcmp(line, ".") != 0)
339                 return (UPDATER_ERR_PROTO);
340         return (0);
341 }
342
343 static int
344 updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
345 {
346         struct stream *rd;
347         struct coll *coll;
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;
354         time_t t;
355         off_t position;
356         int attic, error, needfixupmsg;
357
358         error = 0;
359         rd = up->rd;
360         coll = fup->coll;
361         needfixupmsg = isfixups;
362         while ((line = stream_getln(rd, NULL)) != NULL) {
363                 if (strcmp(line, ".") == 0)
364                         break;
365                 memset(&srbuf, 0, sizeof(srbuf));
366                 if (needfixupmsg) {
367                         lprintf(1, "Applying fixups for collection %s/%s\n",
368                             coll->co_name, coll->co_release);
369                         needfixupmsg = 0;
370                 }
371                 cmd = proto_get_ascii(&line);
372                 if (cmd == NULL || strlen(cmd) != 1)
373                         return (UPDATER_ERR_PROTO);
374                 switch (cmd[0]) {
375                 case 'T':
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);
385
386                         rcsattr = fattr_decode(attr);
387                         if (rcsattr == NULL)
388                                 return (UPDATER_ERR_PROTO);
389
390                         error = fup_prepare(fup, name, 0);
391                         if (error)
392                                 return (UPDATER_ERR_PROTO);
393                         error = updater_setattrs(up, fup, name, tag, date,
394                             revnum, revdate, rcsattr);
395                         fattr_free(rcsattr);
396                         if (error)
397                                 return (error);
398                         break;
399                 case 'c':
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);
407
408                         error = fup_prepare(fup, name, 0);
409                         if (error)
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
413                            exists. */
414                         if (access(fup->destpath, F_OK) == 0) {
415                                 error = updater_delete(up, fup);
416                                 if (error)
417                                         return (error);
418                         }
419
420                         sr = &srbuf;
421                         sr->sr_type = SR_CHECKOUTDEAD;
422                         sr->sr_file = name;
423                         sr->sr_tag = tag;
424                         sr->sr_date = date;
425                         sr->sr_serverattr = fattr_decode(attr);
426                         if (sr->sr_serverattr == NULL)
427                                 return (UPDATER_ERR_PROTO);
428
429                         error = status_put(fup->st, sr);
430                         fattr_free(sr->sr_serverattr);
431                         if (error) {
432                                 up->errmsg = status_errmsg(fup->st);
433                                 return (UPDATER_ERR_MSG);
434                         }
435                         break;
436                 case 'U':
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);
449
450                         sr = &fup->srbuf;
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);
458
459                         fup->expand = keyword_decode_expand(expand);
460                         if (fup->expand == -1)
461                                 return (UPDATER_ERR_PROTO);
462                         error = fup_prepare(fup, name, 0);
463                         if (error)
464                                 return (UPDATER_ERR_PROTO);
465
466                         fup->wantmd5 = xstrdup(wantmd5);
467                         fup->temppath = tempname(fup->destpath);
468                         error = updater_diff(up, fup);
469                         if (error)
470                                 return (error);
471                         break;
472                 case 'u':
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);
480
481                         error = fup_prepare(fup, name, 0);
482                         if (error)
483                                 return (UPDATER_ERR_PROTO);
484                         error = updater_delete(up, fup);
485                         if (error)
486                                 return (error);
487                         sr = &srbuf;
488                         sr->sr_type = SR_CHECKOUTDEAD;
489                         sr->sr_file = name;
490                         sr->sr_tag = tag;
491                         sr->sr_date = date;
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);
497                         if (error) {
498                                 up->errmsg = status_errmsg(fup->st);
499                                 return (UPDATER_ERR_MSG);
500                         }
501                         break;
502                 case 'C':
503                 case 'Y':
504                         /* Checkout file. */
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);
513
514                         sr = &fup->srbuf;
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);
524
525                         t = rcsdatetotime(revdate);
526                         if (t == -1)
527                                 return (UPDATER_ERR_PROTO);
528
529                         sr->sr_clientattr = fattr_new(FT_FILE, t);
530                         tmp = fattr_forcheckout(sr->sr_serverattr,
531                             coll->co_umask);
532                         fattr_override(sr->sr_clientattr, tmp, FA_MASK);
533                         fattr_free(tmp);
534                         fattr_mergedefault(sr->sr_clientattr);
535                         error = fup_prepare(fup, name, 0);
536                         if (error)
537                                 return (UPDATER_ERR_PROTO);
538                         fup->temppath = tempname(fup->destpath);
539                         if (*cmd == 'Y')
540                                 error = updater_checkout(up, fup, 1);
541                         else
542                                 error = updater_checkout(up, fup, 0);
543                         if (error)
544                                 return (error);
545                         break;
546                 case 'D':
547                         /* Delete file. */
548                         name = proto_get_ascii(&line);
549                         if (name == NULL || line != NULL)
550                                 return (UPDATER_ERR_PROTO);
551                         error = fup_prepare(fup, name, 0);
552                         if (error)
553                                 return (UPDATER_ERR_PROTO);
554                         error = updater_delete(up, fup);
555                         if (error)
556                                 return (error);
557                         error = status_delete(fup->st, name, 0);
558                         if (error) {
559                                 up->errmsg = status_errmsg(fup->st);
560                                 return (UPDATER_ERR_MSG);
561                         }
562                         break;
563                 case 'A':
564                 case 'a':
565                 case 'R':
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);
572                         if (error)
573                                 return (UPDATER_ERR_PROTO);
574
575                         fup->temppath = tempname(fup->destpath);
576                         sr = &fup->srbuf;
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);
582                         if (attic)
583                                 lprintf(1, " Create %s -> Attic\n", name);
584                         else
585                                 lprintf(1, " Create %s\n", name);
586                         error = updater_addfile(up, fup, attr, 0);
587                         if (error)
588                                 return (error);
589                         break;
590                 case 'r':
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 ||
596                             wantmd5 == NULL) {
597                                 return (UPDATER_ERR_PROTO);
598                         }
599                         error = fup_prepare(fup, name, 0);
600                         if (error)
601                                 return (UPDATER_ERR_PROTO);
602                         fup->wantmd5 = xstrdup(wantmd5);
603                         fup->temppath = tempname(fup->destpath);
604                         sr = &fup->srbuf;
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,
611                             10));
612                         if (error)
613                                 return (error);
614                         break;
615                 case 'I':
616                         /*
617                          * Create directory and add DirDown entry in status
618                          * file.
619                          */
620                         name = proto_get_ascii(&line);
621                         if (name == NULL || line != NULL)
622                                 return (UPDATER_ERR_PROTO);
623                         error = fup_prepare(fup, name, 0);
624                         if (error)
625                                 return (UPDATER_ERR_PROTO);
626                         sr = &fup->srbuf;
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);
632
633                         error = mkdirhier(fup->destpath, coll->co_umask);
634                         if (error)
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,
639                                     fup->destpath);
640                                 if (error)
641                                         return (UPDATER_ERR_PROTO);
642                         }
643                         error = status_put(fup->st, sr);
644                         if (error) {
645                                 up->errmsg = status_errmsg(fup->st);
646                                 return (UPDATER_ERR_MSG);
647                         }
648                         break;
649                 case 'i':
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);
655                         if (error)
656                                 return (UPDATER_ERR_PROTO);
657                         error = status_delete(fup->st, name, 0);
658                         if (error) {
659                                 up->errmsg = status_errmsg(fup->st);
660                                 return (UPDATER_ERR_MSG);
661                         }
662                         break;
663                 case 'J':
664                         /*
665                          * Set attributes of directory and update DirUp entry in
666                          * status file.
667                          */
668                         name = proto_get_ascii(&line);
669                         if (name == NULL)
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);
675                         if (error)
676                                 return (UPDATER_ERR_PROTO);
677                         error = updater_setdirattrs(up, coll, fup, name, attr);
678                         if (error)
679                                 return (error);
680                         break;
681                 case 'j':
682                         /*
683                          * Remove directory and delete its DirUp entry in status
684                          * file.
685                          */
686                         name = proto_get_ascii(&line);
687                         if (name == NULL || line != NULL)
688                                 return (UPDATER_ERR_PROTO);
689                         error = fup_prepare(fup, name, 0);
690                         if (error)
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);
695                         if (error) {
696                                 up->errmsg = status_errmsg(fup->st);
697                                 return (UPDATER_ERR_MSG);
698                         }
699                         break;
700                 case 'L':
701                 case 'l':
702                         name = proto_get_ascii(&line);
703                         if (name == NULL)
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');
709                         sr = &fup->srbuf;
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);
717                         
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,
722                                     FA_DEV | FA_INODE);
723                         fattr_maskout(sr->sr_clientattr, FA_FLAGS);
724                         error = status_put(fup->st, sr);
725                         if (error) {
726                                 up->errmsg = status_errmsg(fup->st);
727                                 return (UPDATER_ERR_MSG);
728                         }
729                         break;
730                 case 'N':
731                 case 'n':
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);
738                         if (error)
739                                 return (UPDATER_ERR_PROTO);
740                         sr = &fup->srbuf;
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);
748                         if (error)
749                                 return (error);
750                         break;
751                 case 'V':
752                 case 'v':
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);
762                         if (error)
763                                 return (UPDATER_ERR_PROTO);
764                         fup->temppath = tempname(fup->destpath);
765                         fup->wantmd5 = xstrdup(wantmd5);
766                         sr = &fup->srbuf;
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);
772
773                         error = updater_rcsedit(up, fup, name, rcsopt);
774                         if (error)
775                                 return (error);
776                         break;
777                 case 'X':
778                 case 'x':
779                         name = proto_get_ascii(&line);
780                         attr = proto_get_ascii(&line);
781                         if (name == NULL || attr == NULL || line != NULL)
782                                 return (UPDATER_ERR_PROTO);
783                         attic = (cmd[0] == 'x');
784                         error = fup_prepare(fup, name, attic);
785                         if (error)
786                                 return (UPDATER_ERR_PROTO);
787
788                         fup->temppath = tempname(fup->destpath);
789                         sr = &fup->srbuf;
790                         sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE;
791                         sr->sr_file = xstrdup(name);
792                         sr->sr_serverattr = fattr_decode(attr);
793                         if (sr->sr_serverattr == NULL)
794                                 return (UPDATER_ERR_PROTO);
795                         lprintf(1, " Fixup %s\n", name);
796                         error = updater_addfile(up, fup, attr, 1);
797                         if (error)
798                                 return (error);
799                         break;
800                 case 'Z':
801                         name = proto_get_ascii(&line);
802                         attr = proto_get_ascii(&line);
803                         pos  = proto_get_ascii(&line);
804                         if (name == NULL || attr == NULL || pos == NULL ||
805                             line != NULL)
806                                 return (UPDATER_ERR_PROTO);
807                         error = fup_prepare(fup, name, 0);
808                         fup->temppath = tempname(fup->destpath);
809                         sr = &fup->srbuf;
810                         sr->sr_type = SR_FILELIVE;
811                         sr->sr_file = xstrdup(name);
812                         sr->sr_serverattr = fattr_decode(attr);
813                         if (sr->sr_serverattr == NULL)
814                                 return (UPDATER_ERR_PROTO);
815                         position = strtol(pos, NULL, 10);
816                         lprintf(1, " Append to %s\n", name);
817                         error = updater_append_file(up, fup, position);
818                         if (error)
819                                 return (error);
820                         break;
821                 case '!':
822                         /* Warning from server. */
823                         msg = proto_get_rest(&line);
824                         if (msg == NULL)
825                                 return (UPDATER_ERR_PROTO);
826                         lprintf(-1, "Server warning: %s\n", msg);
827                         break;
828                 default:
829                         return (UPDATER_ERR_PROTO);
830                 }
831                 fup_cleanup(fup);
832         }
833         if (line == NULL)
834                 return (UPDATER_ERR_READ);
835         return (0);
836 }
837
838 /* Delete file. */
839 static int
840 updater_delete(struct updater *up, struct file_update *fup)
841 {
842         struct config *config;
843         struct coll *coll;
844
845         config = up->config;
846         coll = fup->coll;
847         if (coll->co_options & CO_DELETE) {
848                 lprintf(1, " Delete %s\n", fup->coname);
849                 if (config->deletelim >= 0 &&
850                     up->deletecount >= config->deletelim)
851                         return (UPDATER_ERR_DELETELIM);
852                 up->deletecount++;
853                 updater_deletefile(fup->destpath);
854                 if (coll->co_options & CO_CHECKOUTMODE)
855                         updater_prunedirs(coll->co_prefix, fup->destpath);
856         } else {
857                 lprintf(1," NoDelete %s\n", fup->coname);
858         }
859         return (0);
860 }
861
862 static void
863 updater_deletefile(const char *path)
864 {
865         int error;
866
867         error = fattr_delete(path);
868         if (error && errno != ENOENT) {
869                 lprintf(-1, "Cannot delete \"%s\": %s\n",
870                     path, strerror(errno));
871         }
872 }
873
874 static int
875 updater_setattrs(struct updater *up, struct file_update *fup, char *name,
876     char *tag, char *date, char *revnum, char *revdate, struct fattr *rcsattr)
877 {
878         struct statusrec sr;
879         struct status *st;
880         struct coll *coll;
881         struct fattr *fileattr, *fa;
882         char *path;
883         int error, rv;
884
885         coll = fup->coll;
886         st = fup->st;
887         path = fup->destpath;
888
889         fileattr = fattr_frompath(path, FATTR_NOFOLLOW);
890         if (fileattr == NULL) {
891                 /* The file has vanished. */
892                 error = status_delete(st, name, 0);
893                 if (error) {
894                         up->errmsg = status_errmsg(st);
895                         return (UPDATER_ERR_MSG);
896                 }
897                 return (0);
898         }
899         fa = fattr_forcheckout(rcsattr, coll->co_umask);
900         fattr_override(fileattr, fa, FA_MASK);
901         fattr_free(fa);
902
903         rv = fattr_install(fileattr, path, NULL);
904         if (rv == -1) {
905                 lprintf(1, " SetAttrs %s\n", fup->coname);
906                 fattr_free(fileattr);
907                 xasprintf(&up->errmsg, "Cannot set attributes for \"%s\": %s",
908                     path, strerror(errno));
909                 return (UPDATER_ERR_MSG);
910         }
911         if (rv == 1) {
912                 lprintf(1, " SetAttrs %s\n", fup->coname);
913                 fattr_free(fileattr);
914                 fileattr = fattr_frompath(path, FATTR_NOFOLLOW);
915                 if (fileattr == NULL) {
916                         /* We're being very unlucky. */
917                         error = status_delete(st, name, 0);
918                         if (error) {
919                                 up->errmsg = status_errmsg(st);
920                                 return (UPDATER_ERR_MSG);
921                         }
922                         return (0);
923                 }
924         }
925
926         fattr_maskout(fileattr, FA_COIGNORE);
927
928         sr.sr_type = SR_CHECKOUTLIVE;
929         sr.sr_file = name;
930         sr.sr_tag = tag;
931         sr.sr_date = date;
932         sr.sr_revnum = revnum;
933         sr.sr_revdate = revdate;
934         sr.sr_clientattr = fileattr;
935         sr.sr_serverattr = rcsattr;
936
937         error = status_put(st, &sr);
938         fattr_free(fileattr);
939         if (error) {
940                 up->errmsg = status_errmsg(st);
941                 return (UPDATER_ERR_MSG);
942         }
943         return (0);
944 }
945
946 static int
947 updater_updatefile(struct updater *up, struct file_update *fup,
948     const char *md5, int isfixup)
949 {
950         struct coll *coll;
951         struct status *st;
952         struct statusrec *sr;
953         struct fattr *fileattr;
954         int error, rv;
955
956         coll = fup->coll;
957         sr = &fup->srbuf;
958         st = fup->st;
959
960         if (strcmp(fup->wantmd5, md5) != 0) {
961                 if (isfixup) {
962                         lprintf(-1, "%s: Checksum mismatch -- "
963                             "file not updated\n", fup->destpath);
964                 } else {
965                         lprintf(-1, "%s: Checksum mismatch -- "
966                             "will transfer entire file\n", fup->destpath);
967                         fixups_put(up->config->fixups, fup->coll, sr->sr_file);
968                 }
969                 if (coll->co_options & CO_KEEPBADFILES)
970                         lprintf(-1, "Bad version saved in %s\n", fup->temppath);
971                 else
972                         updater_deletefile(fup->temppath);
973                 return (0);
974         }
975
976         fattr_umask(sr->sr_clientattr, coll->co_umask);
977         rv = fattr_install(sr->sr_clientattr, fup->destpath, fup->temppath);
978         if (rv == -1) {
979                 xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s",
980                     fup->temppath, fup->destpath, strerror(errno));
981                 return (UPDATER_ERR_MSG);
982         }
983
984         /* XXX Executes */
985         /*
986          * We weren't necessarily able to set all the file attributes to the
987          * desired values, and any executes may have altered the attributes.
988          * To make sure we record the actual attribute values, we fetch
989          * them from the file.
990          *
991          * However, we preserve the link count as received from the
992          * server.  This is important for preserving hard links in mirror
993          * mode.
994          */
995         fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
996         if (fileattr == NULL) {
997                 xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath,
998                     strerror(errno));
999                 return (UPDATER_ERR_MSG);
1000         }
1001         fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT);
1002         fattr_free(sr->sr_clientattr);
1003         sr->sr_clientattr = fileattr;
1004
1005         /*
1006          * To save space, don't write out the device and inode unless
1007          * the link count is greater than 1.  These attributes are used
1008          * only for detecting hard links.  If the link count is 1 then we
1009          * know there aren't any hard links.
1010          */
1011         if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) ||
1012             fattr_getlinkcount(sr->sr_clientattr) <= 1)
1013                 fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE);
1014
1015         if (coll->co_options & CO_CHECKOUTMODE)
1016                 fattr_maskout(sr->sr_clientattr, FA_COIGNORE);
1017
1018         error = status_put(st, sr);
1019         if (error) {
1020                 up->errmsg = status_errmsg(st);
1021                 return (UPDATER_ERR_MSG);
1022         }
1023         return (0);
1024 }
1025
1026 /*
1027  * Update attributes of a directory.
1028  */
1029 static int
1030 updater_setdirattrs(struct updater *up, struct coll *coll,
1031     struct file_update *fup, char *name, char *attr)
1032 {
1033         struct statusrec *sr;
1034         struct fattr *fa;
1035         int error, rv;
1036
1037         sr = &fup->srbuf;
1038         sr->sr_type = SR_DIRUP;
1039         sr->sr_file = xstrdup(name);
1040         sr->sr_clientattr = fattr_decode(attr);
1041         sr->sr_serverattr = fattr_decode(attr);
1042         if (sr->sr_clientattr == NULL || sr->sr_serverattr == NULL) 
1043                 return (UPDATER_ERR_PROTO);
1044         fattr_mergedefault(sr->sr_clientattr);
1045         fattr_umask(sr->sr_clientattr, coll->co_umask);
1046         rv = fattr_install(sr->sr_clientattr, fup->destpath, NULL);
1047         lprintf(1, " SetAttrs %s\n", name);
1048         if (rv == -1) {
1049                 xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s",
1050                     fup->temppath, fup->destpath, strerror(errno));
1051                 return (UPDATER_ERR_MSG);
1052         }
1053         /*
1054          * Now, make sure they were set and record what was set in the status
1055          * file.
1056          */
1057         fa = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
1058         if (fa == NULL) {
1059                 xasprintf(&up->errmsg, "Cannot open \%s\": %s", fup->destpath,
1060                     strerror(errno));
1061                 return (UPDATER_ERR_MSG);
1062         }
1063         fattr_free(sr->sr_clientattr);
1064         fattr_maskout(fa, FA_FLAGS);
1065         sr->sr_clientattr = fa;
1066         error = status_put(fup->st, sr);
1067         if (error) {
1068                 up->errmsg = status_errmsg(fup->st);
1069                 return (UPDATER_ERR_MSG);
1070         }
1071
1072         return (0);
1073 }
1074
1075 static int
1076 updater_diff(struct updater *up, struct file_update *fup)
1077 {
1078         char md5[MD5_DIGEST_SIZE];
1079         struct coll *coll;
1080         struct statusrec *sr;
1081         struct fattr *fa, *tmp;
1082         char *author, *path, *revnum, *revdate;
1083         char *line, *cmd;
1084         int error;
1085
1086         coll = fup->coll;
1087         sr = &fup->srbuf;
1088         path = fup->destpath;
1089
1090         lprintf(1, " Edit %s\n", fup->coname);
1091         while ((line = stream_getln(up->rd, NULL)) != NULL) {
1092                 if (strcmp(line, ".") == 0)
1093                         break;
1094                 cmd = proto_get_ascii(&line);
1095                 if (cmd == NULL || strcmp(cmd, "D") != 0)
1096                         return (UPDATER_ERR_PROTO);
1097                 revnum = proto_get_ascii(&line);
1098                 proto_get_ascii(&line); /* XXX - diffbase */
1099                 revdate = proto_get_ascii(&line);
1100                 author = proto_get_ascii(&line);
1101                 if (author == NULL || line != NULL)
1102                         return (UPDATER_ERR_PROTO);
1103                 if (sr->sr_revnum != NULL)
1104                         free(sr->sr_revnum);
1105                 if (sr->sr_revdate != NULL)
1106                         free(sr->sr_revdate);
1107                 if (fup->author != NULL)
1108                         free(fup->author);
1109                 sr->sr_revnum = xstrdup(revnum);
1110                 sr->sr_revdate = xstrdup(revdate);
1111                 fup->author = xstrdup(author);
1112                 if (fup->orig == NULL) {
1113                         /* First patch, the "origin" file is the one we have. */
1114                         fup->orig = stream_open_file(path, O_RDONLY);
1115                         if (fup->orig == NULL) {
1116                                 xasprintf(&up->errmsg, "%s: Cannot open: %s",
1117                                     path, strerror(errno));
1118                                 return (UPDATER_ERR_MSG);
1119                         }
1120                 } else {
1121                         /* Subsequent patches. */
1122                         stream_close(fup->orig);
1123                         fup->orig = fup->to;
1124                         stream_rewind(fup->orig);
1125                         unlink(fup->temppath);
1126                         free(fup->temppath);
1127                         fup->temppath = tempname(path);
1128                 }
1129                 fup->to = stream_open_file(fup->temppath,
1130                     O_RDWR | O_CREAT | O_TRUNC, 0600);
1131                 if (fup->to == NULL) {
1132                         xasprintf(&up->errmsg, "%s: Cannot open: %s",
1133                             fup->temppath, strerror(errno));
1134                         return (UPDATER_ERR_MSG);
1135                 }
1136                 lprintf(2, "  Add delta %s %s %s\n", sr->sr_revnum,
1137                     sr->sr_revdate, fup->author);
1138                 error = updater_diff_batch(up, fup);
1139                 if (error)
1140                         return (error);
1141         }
1142         if (line == NULL)
1143                 return (UPDATER_ERR_READ);
1144
1145         fa = fattr_frompath(path, FATTR_FOLLOW);
1146         tmp = fattr_forcheckout(sr->sr_serverattr, coll->co_umask);
1147         fattr_override(fa, tmp, FA_MASK);
1148         fattr_free(tmp);
1149         fattr_maskout(fa, FA_MODTIME);
1150         sr->sr_clientattr = fa;
1151
1152         if (MD5_File(fup->temppath, md5) == -1) {
1153                 xasprintf(&up->errmsg,
1154                     "Cannot calculate checksum for \"%s\": %s",
1155                     path, strerror(errno));
1156                 return (UPDATER_ERR_MSG);
1157         }
1158         error = updater_updatefile(up, fup, md5, 0);
1159         return (error);
1160 }
1161
1162 /*
1163  * Edit a file and add delta.
1164  */
1165 static int
1166 updater_diff_batch(struct updater *up, struct file_update *fup)
1167 {
1168         struct stream *rd;
1169         char *cmd, *line, *state, *tok;
1170         int error;
1171
1172         state = NULL;
1173         rd = up->rd;
1174         while ((line = stream_getln(rd, NULL)) != NULL) {
1175                 if (strcmp(line, ".") == 0)
1176                         break;
1177                 cmd = proto_get_ascii(&line);
1178                 if (cmd == NULL || strlen(cmd) != 1) {
1179                         error = UPDATER_ERR_PROTO;
1180                         goto bad;
1181                 }
1182                 switch (cmd[0]) {
1183                 case 'L':
1184                         line = stream_getln(rd, NULL);
1185                         /* XXX - We're just eating the log for now. */
1186                         while (line != NULL && strcmp(line, ".") != 0 &&
1187                             strcmp(line, ".+") != 0)
1188                                 line = stream_getln(rd, NULL);
1189                         if (line == NULL) {
1190                                 error = UPDATER_ERR_READ;
1191                                 goto bad;
1192                         }
1193                         break;
1194                 case 'S':
1195                         tok = proto_get_ascii(&line);
1196                         if (tok == NULL || line != NULL) {
1197                                 error = UPDATER_ERR_PROTO;
1198                                 goto bad;
1199                         }
1200                         if (state != NULL)
1201                                 free(state);
1202                         state = xstrdup(tok);
1203                         break;
1204                 case 'T':
1205                         error = updater_diff_apply(up, fup, state);
1206                         if (error)
1207                                 goto bad;
1208                         break;
1209                 default:
1210                         error = UPDATER_ERR_PROTO;
1211                         goto bad;
1212                 }
1213         }
1214         if (line == NULL) {
1215                 error = UPDATER_ERR_READ;
1216                 goto bad;
1217         }
1218         if (state != NULL)
1219                 free(state);
1220         return (0);
1221 bad:
1222         if (state != NULL)
1223                 free(state);
1224         return (error);
1225 }
1226
1227 int
1228 updater_diff_apply(struct updater *up, struct file_update *fup, char *state)
1229 {
1230         struct diffinfo dibuf, *di;
1231         struct coll *coll;
1232         struct statusrec *sr;
1233         int error;
1234
1235         coll = fup->coll;
1236         sr = &fup->srbuf;
1237         di = &dibuf;
1238
1239         di->di_rcsfile = sr->sr_file;
1240         di->di_cvsroot = coll->co_cvsroot;
1241         di->di_revnum = sr->sr_revnum;
1242         di->di_revdate = sr->sr_revdate;
1243         di->di_author = fup->author;
1244         di->di_tag = sr->sr_tag;
1245         di->di_state = state;
1246         di->di_expand = fup->expand;
1247
1248         error = diff_apply(up->rd, fup->orig, fup->to, coll->co_keyword, di, 1);
1249         if (error) {
1250                 /* XXX Bad error message */
1251                 xasprintf(&up->errmsg, "Bad diff from server");
1252                 return (UPDATER_ERR_MSG);
1253         }
1254         return (0);
1255 }
1256
1257 /* Update or create a node. */
1258 static int
1259 updater_updatenode(struct updater *up, struct coll *coll,
1260     struct file_update *fup, char *name, char *attr)
1261 {
1262         struct fattr *fa, *fileattr;
1263         struct status *st;
1264         struct statusrec *sr;
1265         int error, rv;
1266
1267         sr = &fup->srbuf;
1268         st = fup->st;
1269         fa = fattr_decode(attr);
1270
1271         if (fattr_type(fa) == FT_SYMLINK) {
1272                 lprintf(1, " Symlink %s -> %s\n", name, 
1273                     fattr_getlinktarget(fa));
1274         } else {
1275                 lprintf(1, " Mknod %s\n", name);
1276         }
1277
1278         /* Create directory. */
1279         error = mkdirhier(fup->destpath, coll->co_umask);
1280         if (error)
1281                 return (UPDATER_ERR_PROTO);
1282
1283         /* If it does not exist, create it. */
1284         if (access(fup->destpath, F_OK) != 0)
1285                 fattr_makenode(fa, fup->destpath);
1286
1287         /*
1288          * Coming from attic? I don't think this is a problem since we have
1289          * determined attic before we call this function (Look at UpdateNode in
1290          * cvsup).
1291          */
1292         fattr_umask(fa, coll->co_umask);
1293         rv = fattr_install(fa, fup->destpath, fup->temppath);
1294         if (rv == -1) {
1295                 xasprintf(&up->errmsg, "Cannot update attributes on "
1296             "\"%s\": %s", fup->destpath, strerror(errno));
1297                 return (UPDATER_ERR_MSG);
1298         }
1299         /*
1300          * XXX: Executes not implemented. Have not encountered much use for it
1301          * yet.
1302          */
1303         /*
1304          * We weren't necessarily able to set all the file attributes to the
1305          * desired values, and any executes may have altered the attributes.
1306          * To make sure we record the actual attribute values, we fetch
1307          * them from the file.
1308          *
1309          * However, we preserve the link count as received from the
1310          * server.  This is important for preserving hard links in mirror
1311          * mode.
1312          */
1313         fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
1314         if (fileattr == NULL) {
1315                 xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath,
1316                     strerror(errno));
1317                 return (UPDATER_ERR_MSG);
1318         }
1319         fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT);
1320         fattr_free(sr->sr_clientattr);
1321         sr->sr_clientattr = fileattr;
1322
1323         /*
1324          * To save space, don't write out the device and inode unless
1325          * the link count is greater than 1.  These attributes are used
1326          * only for detecting hard links.  If the link count is 1 then we
1327          * know there aren't any hard links.
1328          */
1329         if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) ||
1330             fattr_getlinkcount(sr->sr_clientattr) <= 1)
1331                 fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE);
1332
1333         /* If it is a symlink, write only out it's path. */
1334         if (fattr_type(fa) == FT_SYMLINK) {
1335                 fattr_maskout(sr->sr_clientattr, ~(FA_FILETYPE |
1336                     FA_LINKTARGET));
1337         }
1338         fattr_maskout(sr->sr_clientattr, FA_FLAGS);
1339         error = status_put(st, sr);
1340         if (error) {
1341                 up->errmsg = status_errmsg(st);
1342                 return (UPDATER_ERR_MSG);
1343         }
1344         fattr_free(fa);
1345
1346         return (0);
1347 }
1348
1349 /*
1350  * Fetches a new file in CVS mode.
1351  */
1352 static int
1353 updater_addfile(struct updater *up, struct file_update *fup, char *attr,
1354     int isfixup)
1355 {
1356         struct coll *coll;
1357         struct stream *to;
1358         struct statusrec *sr;
1359         struct fattr *fa;
1360         char buf[BUFSIZE];
1361         char md5[MD5_DIGEST_SIZE];
1362         ssize_t nread;
1363         off_t fsize, remains;
1364         char *cmd, *line, *path;
1365         int error; 
1366
1367         coll = fup->coll;
1368         path = fup->destpath;
1369         sr = &fup->srbuf;
1370         fa = fattr_decode(attr);
1371         fsize = fattr_filesize(fa);
1372
1373         error = mkdirhier(path, coll->co_umask);
1374         if (error)
1375                 return (UPDATER_ERR_PROTO);
1376         to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, 0755);
1377         if (to == NULL) {
1378                 xasprintf(&up->errmsg, "%s: Cannot create: %s",
1379                     fup->temppath, strerror(errno));
1380                 return (UPDATER_ERR_MSG);
1381         }
1382         stream_filter_start(to, STREAM_FILTER_MD5, md5);
1383         remains = fsize;
1384         do {
1385                 nread = stream_read(up->rd, buf, (BUFSIZE > remains ?
1386                     remains : BUFSIZE));
1387                 if (nread == -1)
1388                         return (UPDATER_ERR_PROTO);
1389                 remains -= nread;
1390                 if (stream_write(to, buf, nread) == -1)
1391                         goto bad;
1392         } while (remains > 0);
1393         stream_close(to);
1394         line = stream_getln(up->rd, NULL);
1395         if (line == NULL)
1396                 return (UPDATER_ERR_PROTO);
1397         /* Check for EOF. */
1398         if (!(*line == '.' || (strncmp(line, ".<", 2) != 0)))
1399                 return (UPDATER_ERR_PROTO);
1400         line = stream_getln(up->rd, NULL);
1401         if (line == NULL)
1402                 return (UPDATER_ERR_PROTO);
1403
1404         cmd = proto_get_ascii(&line);
1405         fup->wantmd5 = proto_get_ascii(&line);
1406         if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0)
1407                 return (UPDATER_ERR_PROTO);
1408
1409         sr->sr_clientattr = fattr_frompath(fup->temppath, FATTR_NOFOLLOW);
1410         if (sr->sr_clientattr == NULL)
1411                 return (UPDATER_ERR_PROTO);
1412         fattr_override(sr->sr_clientattr, sr->sr_serverattr,
1413             FA_MODTIME | FA_MASK);
1414         error = updater_updatefile(up, fup, md5, isfixup);
1415         fup->wantmd5 = NULL;    /* So that it doesn't get freed. */
1416         return (error);
1417 bad:
1418         xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1419             strerror(errno));
1420         return (UPDATER_ERR_MSG);
1421 }
1422
1423 static int
1424 updater_checkout(struct updater *up, struct file_update *fup, int isfixup)
1425 {
1426         char md5[MD5_DIGEST_SIZE];
1427         struct statusrec *sr;
1428         struct coll *coll;
1429         struct stream *to;
1430         ssize_t nbytes;
1431         size_t size;
1432         char *cmd, *path, *line;
1433         int error, first;
1434
1435         coll = fup->coll;
1436         sr = &fup->srbuf;
1437         path = fup->destpath;
1438
1439         if (isfixup)
1440                 lprintf(1, " Fixup %s\n", fup->coname);
1441         else
1442                 lprintf(1, " Checkout %s\n", fup->coname);
1443         error = mkdirhier(path, coll->co_umask);
1444         if (error) {
1445                 xasprintf(&up->errmsg,
1446                     "Cannot create directories leading to \"%s\": %s",
1447                     path, strerror(errno));
1448                 return (UPDATER_ERR_MSG);
1449         }
1450
1451         to = stream_open_file(fup->temppath,
1452             O_WRONLY | O_CREAT | O_TRUNC, 0600);
1453         if (to == NULL) {
1454                 xasprintf(&up->errmsg, "%s: Cannot create: %s",
1455                     fup->temppath, strerror(errno));
1456                 return (UPDATER_ERR_MSG);
1457         }
1458         stream_filter_start(to, STREAM_FILTER_MD5, md5);
1459         line = stream_getln(up->rd, &size);
1460         first = 1;
1461         while (line != NULL) {
1462                 if (line[size - 1] == '\n')
1463                         size--;
1464                 if ((size == 1 && *line == '.') ||
1465                     (size == 2 && memcmp(line, ".+", 2) == 0))
1466                         break;
1467                 if (size >= 2 && memcmp(line, "..", 2) == 0) {
1468                         size--;
1469                         line++;
1470                 }
1471                 if (!first) {
1472                         nbytes = stream_write(to, "\n", 1);
1473                         if (nbytes == -1)
1474                                 goto bad;
1475                 }
1476                 nbytes = stream_write(to, line, size);
1477                 if (nbytes == -1)
1478                         goto bad;
1479                 line = stream_getln(up->rd, &size);
1480                 first = 0;
1481         }
1482         if (line == NULL) {
1483                 stream_close(to);
1484                 return (UPDATER_ERR_READ);
1485         }
1486         if (size == 1 && *line == '.') {
1487                 nbytes = stream_write(to, "\n", 1);
1488                 if (nbytes == -1)
1489                         goto bad;
1490         }
1491         stream_close(to);
1492         /* Get the checksum line. */
1493         line = stream_getln(up->rd, NULL);
1494         if (line == NULL)
1495                 return (UPDATER_ERR_READ);
1496         cmd = proto_get_ascii(&line);
1497         fup->wantmd5 = proto_get_ascii(&line);
1498         if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0)
1499                 return (UPDATER_ERR_PROTO);
1500         error = updater_updatefile(up, fup, md5, isfixup);
1501         fup->wantmd5 = NULL;    /* So that it doesn't get freed. */
1502         if (error)
1503                 return (error);
1504         return (0);
1505 bad:
1506         xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1507             strerror(errno));
1508         return (UPDATER_ERR_MSG);
1509 }
1510
1511 /*
1512  * Remove all empty directories below file.
1513  * This function will trash the path passed to it.
1514  */
1515 static void
1516 updater_prunedirs(char *base, char *file)
1517 {
1518         char *cp;
1519         int error;
1520
1521         while ((cp = strrchr(file, '/')) != NULL) {
1522                 *cp = '\0';
1523                 if (strcmp(base, file) == 0)
1524                         return;
1525                 error = rmdir(file);
1526                 if (error)
1527                         return;
1528         }
1529 }
1530
1531 /*
1532  * Edit an RCS file.
1533  */
1534 static int
1535 updater_rcsedit(struct updater *up, struct file_update *fup, char *name,
1536     char *rcsopt)
1537 {
1538         struct coll *coll;
1539         struct stream *dest;
1540         struct statusrec *sr;
1541         struct status *st;
1542         struct rcsfile *rf;
1543         struct fattr *oldfattr;
1544         char md5[MD5_DIGEST_SIZE];
1545         char *branch, *cmd, *expand, *line, *path, *revnum, *tag, *temppath;
1546         int error;
1547
1548         coll = fup->coll;
1549         sr = &fup->srbuf;
1550         st = fup->st;
1551         temppath = fup->temppath;
1552         path = fup->origpath != NULL ? fup->origpath : fup->destpath;
1553         error = 0;
1554
1555         /* If the path is new, we must create the Attic dir if needed. */
1556         if (fup->origpath != NULL) {
1557                 error = mkdirhier(fup->destpath, coll->co_umask);
1558                 if (error) {
1559                         xasprintf(&up->errmsg, "Unable to create Attic dir for "
1560                             "%s\n", fup->origpath);
1561                         return (UPDATER_ERR_MSG);
1562                 }
1563         }
1564         /*
1565          * XXX: we could avoid parsing overhead if we're reading ahead before we
1566          * parse the file.
1567          */
1568         oldfattr = fattr_frompath(path, FATTR_NOFOLLOW);
1569         if (oldfattr == NULL) {
1570                 xasprintf(&up->errmsg, "%s: Cannot get attributes: %s", path,
1571                     strerror(errno));
1572                 return (UPDATER_ERR_MSG);
1573         }
1574         fattr_merge(sr->sr_serverattr, oldfattr);
1575         rf = NULL;
1576
1577         /* Macro for making touching an RCS file faster. */
1578 #define UPDATER_OPENRCS(rf, up, path, name, cvsroot, tag) do {          \
1579         if ((rf) == NULL) {                                             \
1580                 lprintf(1, " Edit %s", fup->coname);                    \
1581                 if (fup->attic)                                         \
1582                         lprintf(1, " -> Attic");                        \
1583                 lprintf(1, "\n");                                       \
1584                 (rf) = rcsfile_frompath((path), (name), (cvsroot),      \
1585                     (tag), 0);                                          \
1586                 if ((rf) == NULL) {                                     \
1587                         xasprintf(&(up)->errmsg,                        \
1588                             "Error reading rcsfile %s\n", (name));      \
1589                         return (UPDATER_ERR_MSG);                       \
1590                 }                                                       \
1591         }                                                               \
1592 } while (0)
1593
1594         while ((line = stream_getln(up->rd, NULL)) != NULL) {
1595                 if (strcmp(line, ".") == 0)
1596                         break;
1597                 cmd = proto_get_ascii(&line);
1598                 if (cmd == NULL) {
1599                         lprintf(-1, "Error editing %s\n", name);
1600                         return (UPDATER_ERR_PROTO);
1601                 }
1602                 switch(cmd[0]) {
1603                         case 'B':
1604                                 branch = proto_get_ascii(&line);
1605                                 if (branch == NULL || line != NULL)
1606                                         return (UPDATER_ERR_PROTO);
1607                                 UPDATER_OPENRCS(rf, up, path, name,
1608                                     coll->co_cvsroot, coll->co_tag);
1609                                 break;
1610                         case 'b':
1611                                 UPDATER_OPENRCS(rf, up, path, name,
1612                                     coll->co_cvsroot, coll->co_tag);
1613                                 rcsfile_setval(rf, RCSFILE_BRANCH, NULL);
1614                                 break;
1615                         case 'D':
1616                                 UPDATER_OPENRCS(rf, up, path, name,
1617                                     coll->co_cvsroot, coll->co_tag);
1618                                 error = updater_addelta(rf, up->rd, line);
1619                                 if (error)
1620                                         return (error);
1621                                 break;
1622                         case 'd':
1623                                 revnum = proto_get_ascii(&line);
1624                                 if (revnum == NULL || line != NULL)
1625                                         return (UPDATER_ERR_PROTO);
1626                                 UPDATER_OPENRCS(rf, up, path, name,
1627                                     coll->co_cvsroot, coll->co_tag);
1628                                 rcsfile_deleterev(rf, revnum);
1629                                 break;
1630                         case 'E':
1631                                 expand = proto_get_ascii(&line);
1632                                 if (expand == NULL || line != NULL)
1633                                         return (UPDATER_ERR_PROTO);
1634                                 UPDATER_OPENRCS(rf, up, path, name,
1635                                     coll->co_cvsroot, coll->co_tag);
1636                                 rcsfile_setval(rf, RCSFILE_EXPAND, expand);
1637                                 break;
1638                         case 'T':
1639                                 tag = proto_get_ascii(&line);
1640                                 revnum = proto_get_ascii(&line);
1641                                 if (tag == NULL || revnum == NULL ||
1642                                     line != NULL)
1643                                         return (UPDATER_ERR_PROTO);
1644                                 UPDATER_OPENRCS(rf, up, path, name,
1645                                     coll->co_cvsroot, coll->co_tag);
1646                                 rcsfile_addtag(rf, tag, revnum);
1647                                 break;
1648                         case 't':
1649                                 tag = proto_get_ascii(&line);
1650                                 revnum = proto_get_ascii(&line);
1651                                 if (tag == NULL || revnum == NULL ||
1652                                     line != NULL)
1653                                         return (UPDATER_ERR_PROTO);
1654                                 UPDATER_OPENRCS(rf, up, path, name,
1655                                     coll->co_cvsroot, coll->co_tag);
1656                                 rcsfile_deletetag(rf, tag, revnum);
1657                                 break;
1658                         default:
1659                                 return (UPDATER_ERR_PROTO);
1660                 }
1661         }
1662
1663         if (rf == NULL) {
1664                 fattr_maskout(oldfattr, ~FA_MODTIME);
1665                 if (fattr_equal(oldfattr, sr->sr_serverattr))
1666                         lprintf(1, " SetAttrs %s", fup->coname);
1667                 else
1668                         lprintf(1, " Touch %s", fup->coname);
1669                 /* Install new attributes. */
1670                 fattr_umask(sr->sr_serverattr, coll->co_umask);
1671                 fattr_install(sr->sr_serverattr, fup->destpath, NULL);
1672                 if (fup->attic)
1673                         lprintf(1, " -> Attic");
1674                 lprintf(1, "\n");
1675                 fattr_free(oldfattr);
1676                 goto finish;
1677         }
1678
1679         /* Write and rename temp file. */
1680         dest = stream_open_file(fup->temppath,
1681             O_RDWR | O_CREAT | O_TRUNC, 0600);
1682         if (dest == NULL) {
1683                 xasprintf(&up->errmsg, "Error opening file %s for writing: %s\n", 
1684                     fup->temppath, strerror(errno));
1685                 return (UPDATER_ERR_MSG);
1686         }
1687         stream_filter_start(dest, STREAM_FILTER_MD5RCS, md5);
1688         error = rcsfile_write(rf, dest);
1689         stream_close(dest);
1690         rcsfile_free(rf);
1691         if (error) {
1692                 xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1693                     strerror(errno));
1694                 return (UPDATER_ERR_MSG);
1695         }
1696
1697 finish:
1698         sr->sr_clientattr = fattr_frompath(path, FATTR_NOFOLLOW);
1699         if (sr->sr_clientattr == NULL) {
1700                 xasprintf(&up->errmsg, "%s: Cannot get attributes: %s",
1701                     fup->destpath, strerror(errno));
1702                 return (UPDATER_ERR_MSG);
1703         }
1704         fattr_override(sr->sr_clientattr, sr->sr_serverattr,
1705             FA_MODTIME | FA_MASK);
1706         if (rf != NULL) {
1707                 error = updater_updatefile(up, fup, md5, 0);
1708                 fup->wantmd5 = NULL;    /* So that it doesn't get freed. */
1709                 if (error)
1710                         return (error);
1711         } else {
1712                 /* Record its attributes since we touched it. */
1713                 if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) ||
1714                     fattr_getlinkcount(sr->sr_clientattr) <= 1)
1715                 fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE);
1716                 error = status_put(st, sr);
1717                 if (error) {
1718                         up->errmsg = status_errmsg(st);
1719                         return (UPDATER_ERR_MSG);
1720                 }
1721         }
1722
1723         /* In this case, we need to remove the old file afterwards. */
1724         /* XXX: Can we be sure that a file not edited is moved? I don't think
1725          * this is a problem, since if a file is moved, it should be edited to
1726          * show if it's dead or not.
1727          */
1728         if (fup->origpath != NULL)
1729                 updater_deletefile(fup->origpath);
1730         return (0);
1731 }
1732
1733 /*
1734  * Add a delta to a RCS file.
1735  */
1736 int
1737 updater_addelta(struct rcsfile *rf, struct stream *rd, char *cmdline)
1738 {
1739         struct delta *d;
1740         size_t size;
1741         char *author, *cmd, *diffbase, *line, *logline;
1742         char *revdate, *revnum, *state, *textline;
1743
1744         revnum = proto_get_ascii(&cmdline);
1745         diffbase = proto_get_ascii(&cmdline);
1746         revdate = proto_get_ascii(&cmdline);
1747         author = proto_get_ascii(&cmdline);
1748         size = 0;
1749
1750         if (revnum == NULL || revdate == NULL || author == NULL)
1751                 return (UPDATER_ERR_PROTO);
1752
1753         /* First add the delta so we have it. */
1754         d = rcsfile_addelta(rf, revnum, revdate, author, diffbase);
1755         if (d == NULL) {
1756                 lprintf(-1, "Error adding delta %s\n", revnum);
1757                 return (UPDATER_ERR_READ);
1758         }
1759         while ((line = stream_getln(rd, NULL)) != NULL) {
1760                 if (strcmp(line, ".") == 0)
1761                         break;
1762                 cmd = proto_get_ascii(&line);
1763                 switch (cmd[0]) {
1764                         case 'L':
1765                                 /* Do the same as in 'C' command. */
1766                                 logline = stream_getln(rd, &size);
1767                                 while (logline != NULL) {
1768                                         if (size == 2 && *logline == '.')
1769                                                 break;
1770                                         if (size == 3 && 
1771                                             memcmp(logline, ".+", 2) == 0) {
1772                                                 rcsdelta_truncatelog(d, -1);
1773                                                 break;
1774                                         }
1775                                         if (size >= 3 &&
1776                                             memcmp(logline, "..", 2) == 0) {
1777                                                 size--;
1778                                                 logline++;
1779                                         }
1780                                         if (rcsdelta_appendlog(d, logline, size)
1781                                             < 0)
1782                                                 return (-1);
1783                                         logline = stream_getln(rd, &size);
1784                                 }
1785                         break;
1786                         case 'N':
1787                         case 'n':
1788                                 /* XXX: Not supported. */
1789                         break;
1790                         case 'S':
1791                                 state = proto_get_ascii(&line);
1792                                 if (state == NULL)
1793                                         return (UPDATER_ERR_PROTO);
1794                                 rcsdelta_setstate(d, state);
1795                         break;
1796                         case 'T':
1797                                 /* Do the same as in 'C' command. */
1798                                 textline = stream_getln(rd, &size);
1799                                 while (textline != NULL) {
1800                                         if (size == 2 && *textline == '.')
1801                                                 break;
1802                                         if (size == 3 &&
1803                                             memcmp(textline, ".+", 2) == 0) {
1804                                                 /* Truncate newline. */
1805                                                 rcsdelta_truncatetext(d, -1);
1806                                                 break;
1807                                         }
1808                                         if (size >= 3 &&
1809                                             memcmp(textline, "..", 2) == 0) {
1810                                                 size--;
1811                                                 textline++;
1812                                         }
1813                                         if (rcsdelta_appendtext(d, textline,
1814                                             size) < 0)
1815                                                 return (-1);
1816                                         textline = stream_getln(rd, &size);
1817                                 }
1818                         break;
1819                 }
1820         }
1821
1822         return (0);
1823 }
1824
1825 int
1826 updater_append_file(struct updater *up, struct file_update *fup, off_t pos)
1827 {
1828         struct fattr *fa;
1829         struct stream *to;
1830         struct statusrec *sr;
1831         ssize_t nread;
1832         off_t bytes;
1833         char buf[BUFSIZE], md5[MD5_DIGEST_SIZE];
1834         char *line, *cmd;
1835         int error, fd;
1836
1837         sr = &fup->srbuf;
1838         fa = sr->sr_serverattr;
1839         to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC,
1840             0755);
1841         if (to == NULL) {
1842                 xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->temppath,
1843                     strerror(errno));
1844                 return (UPDATER_ERR_MSG);
1845         }
1846         fd = open(fup->destpath, O_RDONLY);
1847         if (fd < 0) {
1848                 xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->destpath,
1849                     strerror(errno));
1850                 return (UPDATER_ERR_MSG);
1851         }
1852
1853         stream_filter_start(to, STREAM_FILTER_MD5, md5);
1854         /* First write the existing content. */
1855         while ((nread = read(fd, buf, BUFSIZE)) > 0) {
1856                 if (stream_write(to, buf, nread) == -1)
1857                         goto bad;
1858         }
1859         if (nread == -1) {
1860                 xasprintf(&up->errmsg, "%s: Error reading: %s", fup->destpath,
1861                     strerror(errno));
1862                 return (UPDATER_ERR_MSG);
1863         }
1864         close(fd);
1865
1866         bytes = fattr_filesize(fa) - pos;
1867         /* Append the new data. */
1868         do {
1869                 nread = stream_read(up->rd, buf,
1870                     (BUFSIZE > bytes) ? bytes : BUFSIZE);
1871                 if (nread == -1)
1872                         return (UPDATER_ERR_PROTO);
1873                 bytes -= nread;
1874                 if (stream_write(to, buf, nread) == -1)
1875                         goto bad;
1876         } while (bytes > 0);
1877         stream_close(to);
1878
1879         line = stream_getln(up->rd, NULL);
1880         if (line == NULL)
1881                 return (UPDATER_ERR_PROTO);
1882         /* Check for EOF. */
1883         if (!(*line == '.' || (strncmp(line, ".<", 2) != 0)))
1884                 return (UPDATER_ERR_PROTO);
1885         line = stream_getln(up->rd, NULL);
1886         if (line == NULL)
1887                 return (UPDATER_ERR_PROTO);
1888
1889         cmd = proto_get_ascii(&line);
1890         fup->wantmd5 = proto_get_ascii(&line);
1891         if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0)
1892                 return (UPDATER_ERR_PROTO);
1893
1894         sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
1895         if (sr->sr_clientattr == NULL)
1896                 return (UPDATER_ERR_PROTO);
1897         fattr_override(sr->sr_clientattr, sr->sr_serverattr,
1898             FA_MODTIME | FA_MASK);
1899         error = updater_updatefile(up, fup, md5, 0);
1900         fup->wantmd5 = NULL;    /* So that it doesn't get freed. */
1901         return (error);
1902 bad:
1903         xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1904             strerror(errno));
1905         return (UPDATER_ERR_MSG);
1906 }
1907
1908 /*
1909  * Read file data from stream of checkout commands, and write it to the
1910  * destination.
1911  */
1912 static int
1913 updater_read_checkout(struct stream *src, struct stream *dest)
1914 {
1915         ssize_t nbytes;
1916         size_t size;
1917         char *line;
1918         int first;
1919
1920         first = 1;
1921         line = stream_getln(src, &size);
1922         while (line != NULL) {
1923                 if (line[size - 1] == '\n')
1924                         size--;
1925                 if ((size == 1 && *line == '.') ||
1926                     (size == 2 && strncmp(line, ".+", 2) == 0))
1927                         break;
1928                 if (size >= 2 && strncmp(line, "..", 2) == 0) {
1929                         size--;
1930                         line++;
1931                 }
1932                 if (!first) {
1933                         nbytes = stream_write(dest, "\n", 1);
1934                         if (nbytes == -1)
1935                                 return (UPDATER_ERR_MSG);
1936                 }
1937                 nbytes = stream_write(dest, line, size);
1938                 if (nbytes == -1)
1939                         return (UPDATER_ERR_MSG);
1940                 line = stream_getln(src, &size);
1941                 first = 0;
1942         }
1943         if (line == NULL)
1944                 return (UPDATER_ERR_READ);
1945         if (size == 1 && *line == '.') {
1946                 nbytes = stream_write(dest, "\n", 1);
1947                 if (nbytes == -1)
1948                         return (UPDATER_ERR_MSG);
1949         }
1950         return (0);
1951 }
1952
1953 /* Update file using the rsync protocol. */
1954 static int
1955 updater_rsync(struct updater *up, struct file_update *fup, size_t blocksize)
1956 {
1957         struct statusrec *sr;
1958         struct stream *to;
1959         char md5[MD5_DIGEST_SIZE];
1960         ssize_t nbytes;
1961         size_t blocknum, blockstart, blockcount;
1962         char *buf, *line;
1963         int error, orig;
1964
1965         sr = &fup->srbuf;
1966
1967         lprintf(1, " Rsync %s\n", fup->coname);
1968         /* First open all files that we are going to work on. */
1969         to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC,
1970             0600);
1971         if (to == NULL) {
1972                 xasprintf(&up->errmsg, "%s: Cannot create: %s",
1973                     fup->temppath, strerror(errno));
1974                 return (UPDATER_ERR_MSG);
1975         }
1976         orig = open(fup->destpath, O_RDONLY);
1977         if (orig < 0) {
1978                 xasprintf(&up->errmsg, "%s: Cannot open: %s",
1979                     fup->destpath, strerror(errno));
1980                 return (UPDATER_ERR_MSG);
1981         }
1982         stream_filter_start(to, STREAM_FILTER_MD5, md5);
1983
1984         error = updater_read_checkout(up->rd, to);
1985         if (error) {
1986                 xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1987                     strerror(errno));
1988                 return (error);
1989         }
1990
1991         /* Buffer must contain blocksize bytes. */
1992         buf = xmalloc(blocksize);
1993         /* Done with the initial text, read and write chunks. */
1994         line = stream_getln(up->rd, NULL);
1995         while (line != NULL) {
1996                 if (strcmp(line, ".") == 0)
1997                         break;
1998                 error = UPDATER_ERR_PROTO;
1999                 if (proto_get_sizet(&line, &blockstart, 10) != 0)
2000                         goto bad;
2001                 if (proto_get_sizet(&line, &blockcount, 10) != 0)
2002                         goto bad;
2003                 /* Read blocks from original file. */
2004                 lseek(orig, (blocksize * blockstart), SEEK_SET);
2005                 error = UPDATER_ERR_MSG;
2006                 for (blocknum = 0; blocknum < blockcount; blocknum++) {
2007                         nbytes = read(orig, buf, blocksize);
2008                         if (nbytes < 0) {
2009                                 xasprintf(&up->errmsg, "%s: Cannot read: %s",
2010                                     fup->destpath, strerror(errno));
2011                                 goto bad;
2012                         }
2013                         nbytes = stream_write(to, buf, nbytes);
2014                         if (nbytes == -1) {
2015                                 xasprintf(&up->errmsg, "%s: Cannot write: %s",
2016                                     fup->temppath, strerror(errno));
2017                                 goto bad;
2018                         }
2019                 }
2020                 /* Get the remaining text from the server. */
2021                 error = updater_read_checkout(up->rd, to);
2022                 if (error) {
2023                         xasprintf(&up->errmsg, "%s: Cannot write: %s",
2024                             fup->temppath, strerror(errno));
2025                         goto bad;
2026                 }
2027                 line = stream_getln(up->rd, NULL);
2028         }
2029         stream_close(to);
2030         close(orig);
2031
2032         sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
2033         if (sr->sr_clientattr == NULL)
2034                 return (UPDATER_ERR_PROTO);
2035         fattr_override(sr->sr_clientattr, sr->sr_serverattr,
2036             FA_MODTIME | FA_MASK);
2037
2038         error = updater_updatefile(up, fup, md5, 0);
2039         fup->wantmd5 = NULL;    /* So that it doesn't get freed. */
2040 bad:
2041         free(buf);
2042         return (error);
2043 }