]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/csup/updater.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / contrib / 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 lister 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 = 0;
774                         error = updater_rcsedit(up, fup, name, rcsopt);
775                         if (error)
776                                 return (error);
777                         break;
778                 case 'X':
779                 case 'x':
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);
786                         if (error)
787                                 return (UPDATER_ERR_PROTO);
788
789                         fup->temppath = tempname(fup->destpath);
790                         sr = &fup->srbuf;
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);
798                         if (error)
799                                 return (error);
800                         break;
801                 case 'Z':
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 ||
806                             line != NULL)
807                                 return (UPDATER_ERR_PROTO);
808                         error = fup_prepare(fup, name, 0);
809                         fup->temppath = tempname(fup->destpath);
810                         sr = &fup->srbuf;
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);
819                         if (error)
820                                 return (error);
821                         break;
822                 case '!':
823                         /* Warning from server. */
824                         msg = proto_get_rest(&line);
825                         if (msg == NULL)
826                                 return (UPDATER_ERR_PROTO);
827                         lprintf(-1, "Server warning: %s\n", msg);
828                         break;
829                 default:
830                         return (UPDATER_ERR_PROTO);
831                 }
832                 fup_cleanup(fup);
833         }
834         if (line == NULL)
835                 return (UPDATER_ERR_READ);
836         return (0);
837 }
838
839 /* Delete file. */
840 static int
841 updater_delete(struct updater *up, struct file_update *fup)
842 {
843         struct config *config;
844         struct coll *coll;
845
846         config = up->config;
847         coll = fup->coll;
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);
853                 up->deletecount++;
854                 updater_deletefile(fup->destpath);
855                 if (coll->co_options & CO_CHECKOUTMODE)
856                         updater_prunedirs(coll->co_prefix, fup->destpath);
857         } else {
858                 lprintf(1," NoDelete %s\n", fup->coname);
859         }
860         return (0);
861 }
862
863 static void
864 updater_deletefile(const char *path)
865 {
866         int error;
867
868         error = fattr_delete(path);
869         if (error && errno != ENOENT) {
870                 lprintf(-1, "Cannot delete \"%s\": %s\n",
871                     path, strerror(errno));
872         }
873 }
874
875 static int
876 updater_setattrs(struct updater *up, struct file_update *fup, char *name,
877     char *tag, char *date, char *revnum, char *revdate, struct fattr *rcsattr)
878 {
879         struct statusrec sr;
880         struct status *st;
881         struct coll *coll;
882         struct fattr *fileattr, *fa;
883         char *path;
884         int error, rv;
885
886         coll = fup->coll;
887         st = fup->st;
888         path = fup->destpath;
889
890         fileattr = fattr_frompath(path, FATTR_NOFOLLOW);
891         if (fileattr == NULL) {
892                 /* The file has vanished. */
893                 error = status_delete(st, name, 0);
894                 if (error) {
895                         up->errmsg = status_errmsg(st);
896                         return (UPDATER_ERR_MSG);
897                 }
898                 return (0);
899         }
900         fa = fattr_forcheckout(rcsattr, coll->co_umask);
901         fattr_override(fileattr, fa, FA_MASK);
902         fattr_free(fa);
903
904         rv = fattr_install(fileattr, path, NULL);
905         if (rv == -1) {
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);
911         }
912         if (rv == 1) {
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);
919                         if (error) {
920                                 up->errmsg = status_errmsg(st);
921                                 return (UPDATER_ERR_MSG);
922                         }
923                         return (0);
924                 }
925         }
926
927         fattr_maskout(fileattr, FA_COIGNORE);
928
929         sr.sr_type = SR_CHECKOUTLIVE;
930         sr.sr_file = name;
931         sr.sr_tag = tag;
932         sr.sr_date = date;
933         sr.sr_revnum = revnum;
934         sr.sr_revdate = revdate;
935         sr.sr_clientattr = fileattr;
936         sr.sr_serverattr = rcsattr;
937
938         error = status_put(st, &sr);
939         fattr_free(fileattr);
940         if (error) {
941                 up->errmsg = status_errmsg(st);
942                 return (UPDATER_ERR_MSG);
943         }
944         return (0);
945 }
946
947 static int
948 updater_updatefile(struct updater *up, struct file_update *fup,
949     const char *md5, int isfixup)
950 {
951         struct coll *coll;
952         struct status *st;
953         struct statusrec *sr;
954         struct fattr *fileattr;
955         int error, rv;
956
957         coll = fup->coll;
958         sr = &fup->srbuf;
959         st = fup->st;
960
961         if (strcmp(fup->wantmd5, md5) != 0) {
962                 if (isfixup) {
963                         lprintf(-1, "%s: Checksum mismatch -- "
964                             "file not updated\n", fup->destpath);
965                 } else {
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);
969                 }
970                 if (coll->co_options & CO_KEEPBADFILES)
971                         lprintf(-1, "Bad version saved in %s\n", fup->temppath);
972                 else
973                         updater_deletefile(fup->temppath);
974                 return (0);
975         }
976
977         fattr_umask(sr->sr_clientattr, coll->co_umask);
978         rv = fattr_install(sr->sr_clientattr, fup->destpath, fup->temppath);
979         if (rv == -1) {
980                 xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s",
981                     fup->temppath, fup->destpath, strerror(errno));
982                 return (UPDATER_ERR_MSG);
983         }
984
985         /* XXX Executes */
986         /*
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.
991          *
992          * However, we preserve the link count as received from the
993          * server.  This is important for preserving hard links in mirror
994          * mode.
995          */
996         fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
997         if (fileattr == NULL) {
998                 xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath,
999                     strerror(errno));
1000                 return (UPDATER_ERR_MSG);
1001         }
1002         fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT);
1003         fattr_free(sr->sr_clientattr);
1004         sr->sr_clientattr = fileattr;
1005
1006         /*
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.
1011          */
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);
1015
1016         if (coll->co_options & CO_CHECKOUTMODE)
1017                 fattr_maskout(sr->sr_clientattr, FA_COIGNORE);
1018
1019         error = status_put(st, sr);
1020         if (error) {
1021                 up->errmsg = status_errmsg(st);
1022                 return (UPDATER_ERR_MSG);
1023         }
1024         return (0);
1025 }
1026
1027 /*
1028  * Update attributes of a directory.
1029  */
1030 static int
1031 updater_setdirattrs(struct updater *up, struct coll *coll,
1032     struct file_update *fup, char *name, char *attr)
1033 {
1034         struct statusrec *sr;
1035         struct fattr *fa;
1036         int error, rv;
1037
1038         sr = &fup->srbuf;
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);
1049         if (rv == -1) {
1050                 xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s",
1051                     fup->temppath, fup->destpath, strerror(errno));
1052                 return (UPDATER_ERR_MSG);
1053         }
1054         /*
1055          * Now, make sure they were set and record what was set in the status
1056          * file.
1057          */
1058         fa = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
1059         if (fa == NULL) {
1060                 xasprintf(&up->errmsg, "Cannot open \%s\": %s", fup->destpath,
1061                     strerror(errno));
1062                 return (UPDATER_ERR_MSG);
1063         }
1064         fattr_free(sr->sr_clientattr);
1065         fattr_maskout(fa, FA_FLAGS);
1066         sr->sr_clientattr = fa;
1067         error = status_put(fup->st, sr);
1068         if (error) {
1069                 up->errmsg = status_errmsg(fup->st);
1070                 return (UPDATER_ERR_MSG);
1071         }
1072
1073         return (0);
1074 }
1075
1076 static int
1077 updater_diff(struct updater *up, struct file_update *fup)
1078 {
1079         char md5[MD5_DIGEST_SIZE];
1080         struct coll *coll;
1081         struct statusrec *sr;
1082         struct fattr *fa, *tmp;
1083         char *author, *path, *revnum, *revdate;
1084         char *line, *cmd;
1085         int error;
1086
1087         coll = fup->coll;
1088         sr = &fup->srbuf;
1089         path = fup->destpath;
1090
1091         lprintf(1, " Edit %s\n", fup->coname);
1092         while ((line = stream_getln(up->rd, NULL)) != NULL) {
1093                 if (strcmp(line, ".") == 0)
1094                         break;
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)
1109                         free(fup->author);
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);
1120                         }
1121                 } else {
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);
1129                 }
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);
1136                 }
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);
1140                 if (error)
1141                         return (error);
1142         }
1143         if (line == NULL)
1144                 return (UPDATER_ERR_READ);
1145
1146         fa = fattr_frompath(path, FATTR_FOLLOW);
1147         tmp = fattr_forcheckout(sr->sr_serverattr, coll->co_umask);
1148         fattr_override(fa, tmp, FA_MASK);
1149         fattr_free(tmp);
1150         fattr_maskout(fa, FA_MODTIME);
1151         sr->sr_clientattr = fa;
1152
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);
1158         }
1159         error = updater_updatefile(up, fup, md5, 0);
1160         return (error);
1161 }
1162
1163 /*
1164  * Edit a file and add delta.
1165  */
1166 static int
1167 updater_diff_batch(struct updater *up, struct file_update *fup)
1168 {
1169         struct stream *rd;
1170         char *cmd, *line, *state, *tok;
1171         int error;
1172
1173         state = NULL;
1174         rd = up->rd;
1175         while ((line = stream_getln(rd, NULL)) != NULL) {
1176                 if (strcmp(line, ".") == 0)
1177                         break;
1178                 cmd = proto_get_ascii(&line);
1179                 if (cmd == NULL || strlen(cmd) != 1) {
1180                         error = UPDATER_ERR_PROTO;
1181                         goto bad;
1182                 }
1183                 switch (cmd[0]) {
1184                 case 'L':
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);
1190                         if (line == NULL) {
1191                                 error = UPDATER_ERR_READ;
1192                                 goto bad;
1193                         }
1194                         break;
1195                 case 'S':
1196                         tok = proto_get_ascii(&line);
1197                         if (tok == NULL || line != NULL) {
1198                                 error = UPDATER_ERR_PROTO;
1199                                 goto bad;
1200                         }
1201                         if (state != NULL)
1202                                 free(state);
1203                         state = xstrdup(tok);
1204                         break;
1205                 case 'T':
1206                         error = updater_diff_apply(up, fup, state);
1207                         if (error)
1208                                 goto bad;
1209                         break;
1210                 default:
1211                         error = UPDATER_ERR_PROTO;
1212                         goto bad;
1213                 }
1214         }
1215         if (line == NULL) {
1216                 error = UPDATER_ERR_READ;
1217                 goto bad;
1218         }
1219         if (state != NULL)
1220                 free(state);
1221         return (0);
1222 bad:
1223         if (state != NULL)
1224                 free(state);
1225         return (error);
1226 }
1227
1228 int
1229 updater_diff_apply(struct updater *up, struct file_update *fup, char *state)
1230 {
1231         struct diffinfo dibuf, *di;
1232         struct coll *coll;
1233         struct statusrec *sr;
1234         int error;
1235
1236         coll = fup->coll;
1237         sr = &fup->srbuf;
1238         di = &dibuf;
1239
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;
1248
1249         error = diff_apply(up->rd, fup->orig, fup->to, coll->co_keyword, di, 1);
1250         if (error) {
1251                 /* XXX Bad error message */
1252                 xasprintf(&up->errmsg, "Bad diff from server");
1253                 return (UPDATER_ERR_MSG);
1254         }
1255         return (0);
1256 }
1257
1258 /* Update or create a node. */
1259 static int
1260 updater_updatenode(struct updater *up, struct coll *coll,
1261     struct file_update *fup, char *name, char *attr)
1262 {
1263         struct fattr *fa, *fileattr;
1264         struct status *st;
1265         struct statusrec *sr;
1266         int error, rv;
1267
1268         sr = &fup->srbuf;
1269         st = fup->st;
1270         fa = fattr_decode(attr);
1271
1272         if (fattr_type(fa) == FT_SYMLINK) {
1273                 lprintf(1, " Symlink %s -> %s\n", name, 
1274                     fattr_getlinktarget(fa));
1275         } else {
1276                 lprintf(1, " Mknod %s\n", name);
1277         }
1278
1279         /* Create directory. */
1280         error = mkdirhier(fup->destpath, coll->co_umask);
1281         if (error)
1282                 return (UPDATER_ERR_PROTO);
1283
1284         /* If it does not exist, create it. */
1285         if (access(fup->destpath, F_OK) != 0)
1286                 fattr_makenode(fa, fup->destpath);
1287
1288         /*
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
1291          * cvsup).
1292          */
1293         fattr_umask(fa, coll->co_umask);
1294         rv = fattr_install(fa, fup->destpath, fup->temppath);
1295         if (rv == -1) {
1296                 xasprintf(&up->errmsg, "Cannot update attributes on "
1297             "\"%s\": %s", fup->destpath, strerror(errno));
1298                 return (UPDATER_ERR_MSG);
1299         }
1300         /*
1301          * XXX: Executes not implemented. Have not encountered much use for it
1302          * yet.
1303          */
1304         /*
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.
1309          *
1310          * However, we preserve the link count as received from the
1311          * server.  This is important for preserving hard links in mirror
1312          * mode.
1313          */
1314         fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
1315         if (fileattr == NULL) {
1316                 xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath,
1317                     strerror(errno));
1318                 return (UPDATER_ERR_MSG);
1319         }
1320         fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT);
1321         fattr_free(sr->sr_clientattr);
1322         sr->sr_clientattr = fileattr;
1323
1324         /*
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.
1329          */
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);
1333
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 |
1337                     FA_LINKTARGET));
1338         }
1339         fattr_maskout(sr->sr_clientattr, FA_FLAGS);
1340         error = status_put(st, sr);
1341         if (error) {
1342                 up->errmsg = status_errmsg(st);
1343                 return (UPDATER_ERR_MSG);
1344         }
1345         fattr_free(fa);
1346
1347         return (0);
1348 }
1349
1350 /*
1351  * Fetches a new file in CVS mode.
1352  */
1353 static int
1354 updater_addfile(struct updater *up, struct file_update *fup, char *attr,
1355     int isfixup)
1356 {
1357         struct coll *coll;
1358         struct stream *to;
1359         struct statusrec *sr;
1360         struct fattr *fa;
1361         char buf[BUFSIZE];
1362         char md5[MD5_DIGEST_SIZE];
1363         ssize_t nread;
1364         off_t fsize, remains;
1365         char *cmd, *line, *path;
1366         int error; 
1367
1368         coll = fup->coll;
1369         path = fup->destpath;
1370         sr = &fup->srbuf;
1371         fa = fattr_decode(attr);
1372         fsize = fattr_filesize(fa);
1373
1374         error = mkdirhier(path, coll->co_umask);
1375         if (error)
1376                 return (UPDATER_ERR_PROTO);
1377         to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, 0755);
1378         if (to == NULL) {
1379                 xasprintf(&up->errmsg, "%s: Cannot create: %s",
1380                     fup->temppath, strerror(errno));
1381                 return (UPDATER_ERR_MSG);
1382         }
1383         stream_filter_start(to, STREAM_FILTER_MD5, md5);
1384         remains = fsize;
1385         do {
1386                 nread = stream_read(up->rd, buf, (BUFSIZE > remains ?
1387                     remains : BUFSIZE));
1388                 if (nread == -1)
1389                         return (UPDATER_ERR_PROTO);
1390                 remains -= nread;
1391                 if (stream_write(to, buf, nread) == -1)
1392                         goto bad;
1393         } while (remains > 0);
1394         stream_close(to);
1395         line = stream_getln(up->rd, NULL);
1396         if (line == 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);
1402         if (line == NULL)
1403                 return (UPDATER_ERR_PROTO);
1404
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);
1409
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. */
1417         return (error);
1418 bad:
1419         xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1420             strerror(errno));
1421         return (UPDATER_ERR_MSG);
1422 }
1423
1424 static int
1425 updater_checkout(struct updater *up, struct file_update *fup, int isfixup)
1426 {
1427         char md5[MD5_DIGEST_SIZE];
1428         struct statusrec *sr;
1429         struct coll *coll;
1430         struct stream *to;
1431         ssize_t nbytes;
1432         size_t size;
1433         char *cmd, *path, *line;
1434         int error, first;
1435
1436         coll = fup->coll;
1437         sr = &fup->srbuf;
1438         path = fup->destpath;
1439
1440         if (isfixup)
1441                 lprintf(1, " Fixup %s\n", fup->coname);
1442         else
1443                 lprintf(1, " Checkout %s\n", fup->coname);
1444         error = mkdirhier(path, coll->co_umask);
1445         if (error) {
1446                 xasprintf(&up->errmsg,
1447                     "Cannot create directories leading to \"%s\": %s",
1448                     path, strerror(errno));
1449                 return (UPDATER_ERR_MSG);
1450         }
1451
1452         to = stream_open_file(fup->temppath,
1453             O_WRONLY | O_CREAT | O_TRUNC, 0600);
1454         if (to == NULL) {
1455                 xasprintf(&up->errmsg, "%s: Cannot create: %s",
1456                     fup->temppath, strerror(errno));
1457                 return (UPDATER_ERR_MSG);
1458         }
1459         stream_filter_start(to, STREAM_FILTER_MD5, md5);
1460         line = stream_getln(up->rd, &size);
1461         first = 1;
1462         while (line != NULL) {
1463                 if (line[size - 1] == '\n')
1464                         size--;
1465                 if ((size == 1 && *line == '.') ||
1466                     (size == 2 && memcmp(line, ".+", 2) == 0))
1467                         break;
1468                 if (size >= 2 && memcmp(line, "..", 2) == 0) {
1469                         size--;
1470                         line++;
1471                 }
1472                 if (!first) {
1473                         nbytes = stream_write(to, "\n", 1);
1474                         if (nbytes == -1)
1475                                 goto bad;
1476                 }
1477                 nbytes = stream_write(to, line, size);
1478                 if (nbytes == -1)
1479                         goto bad;
1480                 line = stream_getln(up->rd, &size);
1481                 first = 0;
1482         }
1483         if (line == NULL) {
1484                 stream_close(to);
1485                 return (UPDATER_ERR_READ);
1486         }
1487         if (size == 1 && *line == '.') {
1488                 nbytes = stream_write(to, "\n", 1);
1489                 if (nbytes == -1)
1490                         goto bad;
1491         }
1492         stream_close(to);
1493         /* Get the checksum line. */
1494         line = stream_getln(up->rd, NULL);
1495         if (line == 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. */
1503         if (error)
1504                 return (error);
1505         return (0);
1506 bad:
1507         xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1508             strerror(errno));
1509         return (UPDATER_ERR_MSG);
1510 }
1511
1512 /*
1513  * Remove all empty directories below file.
1514  * This function will trash the path passed to it.
1515  */
1516 static void
1517 updater_prunedirs(char *base, char *file)
1518 {
1519         char *cp;
1520         int error;
1521
1522         while ((cp = strrchr(file, '/')) != NULL) {
1523                 *cp = '\0';
1524                 if (strcmp(base, file) == 0)
1525                         return;
1526                 error = rmdir(file);
1527                 if (error)
1528                         return;
1529         }
1530 }
1531
1532 /*
1533  * Edit an RCS file.
1534  */
1535 static int
1536 updater_rcsedit(struct updater *up, struct file_update *fup, char *name,
1537     char *rcsopt)
1538 {
1539         struct coll *coll;
1540         struct stream *dest;
1541         struct statusrec *sr;
1542         struct status *st;
1543         struct rcsfile *rf;
1544         struct fattr *oldfattr;
1545         char md5[MD5_DIGEST_SIZE];
1546         char *branch, *cmd, *expand, *line, *path, *revnum, *tag, *temppath;
1547         int error;
1548
1549         coll = fup->coll;
1550         sr = &fup->srbuf;
1551         st = fup->st;
1552         temppath = fup->temppath;
1553         path = fup->origpath != NULL ? fup->origpath : fup->destpath;
1554         error = 0;
1555
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);
1559                 if (error) {
1560                         xasprintf(&up->errmsg, "Unable to create Attic dir for "
1561                             "%s\n", fup->origpath);
1562                         return (UPDATER_ERR_MSG);
1563                 }
1564         }
1565         /*
1566          * XXX: we could avoid parsing overhead if we're reading ahead before we
1567          * parse the file.
1568          */
1569         oldfattr = fattr_frompath(path, FATTR_NOFOLLOW);
1570         if (oldfattr == NULL) {
1571                 xasprintf(&up->errmsg, "%s: Cannot get attributes: %s", path,
1572                     strerror(errno));
1573                 return (UPDATER_ERR_MSG);
1574         }
1575         fattr_merge(sr->sr_serverattr, oldfattr);
1576         rf = NULL;
1577
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);                    \
1582                 if (fup->attic)                                         \
1583                         lprintf(1, " -> Attic");                        \
1584                 lprintf(1, "\n");                                       \
1585                 (rf) = rcsfile_frompath((path), (name), (cvsroot),      \
1586                     (tag), 0);                                          \
1587                 if ((rf) == NULL) {                                     \
1588                         xasprintf(&(up)->errmsg,                        \
1589                             "Error reading rcsfile %s\n", (name));      \
1590                         return (UPDATER_ERR_MSG);                       \
1591                 }                                                       \
1592         }                                                               \
1593 } while (0)
1594
1595         while ((line = stream_getln(up->rd, NULL)) != NULL) {
1596                 if (strcmp(line, ".") == 0)
1597                         break;
1598                 cmd = proto_get_ascii(&line);
1599                 if (cmd == NULL) {
1600                         lprintf(-1, "Error editing %s\n", name);
1601                         return (UPDATER_ERR_PROTO);
1602                 }
1603                 switch(cmd[0]) {
1604                         case 'B':
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);
1610                                 break;
1611                         case 'b':
1612                                 UPDATER_OPENRCS(rf, up, path, name,
1613                                     coll->co_cvsroot, coll->co_tag);
1614                                 rcsfile_setval(rf, RCSFILE_BRANCH, NULL);
1615                                 break;
1616                         case 'D':
1617                                 UPDATER_OPENRCS(rf, up, path, name,
1618                                     coll->co_cvsroot, coll->co_tag);
1619                                 error = updater_addelta(rf, up->rd, line);
1620                                 if (error)
1621                                         return (error);
1622                                 break;
1623                         case 'd':
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);
1630                                 break;
1631                         case 'E':
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);
1638                                 break;
1639                         case 'T':
1640                                 tag = proto_get_ascii(&line);
1641                                 revnum = proto_get_ascii(&line);
1642                                 if (tag == NULL || revnum == NULL ||
1643                                     line != 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);
1648                                 break;
1649                         case 't':
1650                                 tag = proto_get_ascii(&line);
1651                                 revnum = proto_get_ascii(&line);
1652                                 if (tag == NULL || revnum == NULL ||
1653                                     line != 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);
1658                                 break;
1659                         default:
1660                                 return (UPDATER_ERR_PROTO);
1661                 }
1662         }
1663
1664         if (rf == NULL) {
1665                 fattr_maskout(oldfattr, ~FA_MODTIME);
1666                 if (fattr_equal(oldfattr, sr->sr_serverattr))
1667                         lprintf(1, " SetAttrs %s", fup->coname);
1668                 else
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);
1673                 if (fup->attic)
1674                         lprintf(1, " -> Attic");
1675                 lprintf(1, "\n");
1676                 fattr_free(oldfattr);
1677                 goto finish;
1678         }
1679
1680         /* Write and rename temp file. */
1681         dest = stream_open_file(fup->temppath,
1682             O_RDWR | O_CREAT | O_TRUNC, 0600);
1683         if (dest == NULL) {
1684                 xasprintf(&up->errmsg, "Error opening file %s for writing: %s\n", 
1685                     fup->temppath, strerror(errno));
1686                 return (UPDATER_ERR_MSG);
1687         }
1688         stream_filter_start(dest, STREAM_FILTER_MD5RCS, md5);
1689         error = rcsfile_write(rf, dest);
1690         stream_close(dest);
1691         rcsfile_free(rf);
1692         if (error) {
1693                 xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1694                     strerror(errno));
1695                 return (UPDATER_ERR_MSG);
1696         }
1697
1698 finish:
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);
1704         }
1705         fattr_override(sr->sr_clientattr, sr->sr_serverattr,
1706             FA_MODTIME | FA_MASK);
1707         if (rf != NULL) {
1708                 error = updater_updatefile(up, fup, md5, 0);
1709                 fup->wantmd5 = NULL;    /* So that it doesn't get freed. */
1710                 if (error)
1711                         return (error);
1712         } else {
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);
1718                 if (error) {
1719                         up->errmsg = status_errmsg(st);
1720                         return (UPDATER_ERR_MSG);
1721                 }
1722         }
1723
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.
1728          */
1729         if (fup->origpath != NULL)
1730                 updater_deletefile(fup->origpath);
1731         return (0);
1732 }
1733
1734 /*
1735  * Add a delta to a RCS file.
1736  */
1737 int
1738 updater_addelta(struct rcsfile *rf, struct stream *rd, char *cmdline)
1739 {
1740         struct delta *d;
1741         size_t size;
1742         char *author, *cmd, *diffbase, *line, *logline;
1743         char *revdate, *revnum, *state, *textline;
1744
1745         revnum = proto_get_ascii(&cmdline);
1746         diffbase = proto_get_ascii(&cmdline);
1747         revdate = proto_get_ascii(&cmdline);
1748         author = proto_get_ascii(&cmdline);
1749         size = 0;
1750
1751         if (revnum == NULL || revdate == NULL || author == NULL)
1752                 return (UPDATER_ERR_PROTO);
1753
1754         /* First add the delta so we have it. */
1755         d = rcsfile_addelta(rf, revnum, revdate, author, diffbase);
1756         if (d == NULL) {
1757                 lprintf(-1, "Error adding delta %s\n", revnum);
1758                 return (UPDATER_ERR_READ);
1759         }
1760         while ((line = stream_getln(rd, NULL)) != NULL) {
1761                 if (strcmp(line, ".") == 0)
1762                         break;
1763                 cmd = proto_get_ascii(&line);
1764                 switch (cmd[0]) {
1765                         case 'L':
1766                                 /* Do the same as in 'C' command. */
1767                                 logline = stream_getln(rd, &size);
1768                                 while (logline != NULL) {
1769                                         if (size == 2 && *logline == '.')
1770                                                 break;
1771                                         if (size == 3 && 
1772                                             memcmp(logline, ".+", 2) == 0) {
1773                                                 rcsdelta_truncatelog(d, -1);
1774                                                 break;
1775                                         }
1776                                         if (size >= 3 &&
1777                                             memcmp(logline, "..", 2) == 0) {
1778                                                 size--;
1779                                                 logline++;
1780                                         }
1781                                         if (rcsdelta_appendlog(d, logline, size)
1782                                             < 0)
1783                                                 return (-1);
1784                                         logline = stream_getln(rd, &size);
1785                                 }
1786                         break;
1787                         case 'N':
1788                         case 'n':
1789                                 /* XXX: Not supported. */
1790                         break;
1791                         case 'S':
1792                                 state = proto_get_ascii(&line);
1793                                 if (state == NULL)
1794                                         return (UPDATER_ERR_PROTO);
1795                                 rcsdelta_setstate(d, state);
1796                         break;
1797                         case 'T':
1798                                 /* Do the same as in 'C' command. */
1799                                 textline = stream_getln(rd, &size);
1800                                 while (textline != NULL) {
1801                                         if (size == 2 && *textline == '.')
1802                                                 break;
1803                                         if (size == 3 &&
1804                                             memcmp(textline, ".+", 2) == 0) {
1805                                                 /* Truncate newline. */
1806                                                 rcsdelta_truncatetext(d, -1);
1807                                                 break;
1808                                         }
1809                                         if (size >= 3 &&
1810                                             memcmp(textline, "..", 2) == 0) {
1811                                                 size--;
1812                                                 textline++;
1813                                         }
1814                                         if (rcsdelta_appendtext(d, textline,
1815                                             size) < 0)
1816                                                 return (-1);
1817                                         textline = stream_getln(rd, &size);
1818                                 }
1819                         break;
1820                 }
1821         }
1822
1823         return (0);
1824 }
1825
1826 int
1827 updater_append_file(struct updater *up, struct file_update *fup, off_t pos)
1828 {
1829         struct fattr *fa;
1830         struct stream *to;
1831         struct statusrec *sr;
1832         ssize_t nread;
1833         off_t bytes;
1834         char buf[BUFSIZE], md5[MD5_DIGEST_SIZE];
1835         char *line, *cmd;
1836         int error, fd;
1837
1838         sr = &fup->srbuf;
1839         fa = sr->sr_serverattr;
1840         to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC,
1841             0755);
1842         if (to == NULL) {
1843                 xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->temppath,
1844                     strerror(errno));
1845                 return (UPDATER_ERR_MSG);
1846         }
1847         fd = open(fup->destpath, O_RDONLY);
1848         if (fd < 0) {
1849                 xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->destpath,
1850                     strerror(errno));
1851                 return (UPDATER_ERR_MSG);
1852         }
1853
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)
1858                         goto bad;
1859         }
1860         if (nread == -1) {
1861                 xasprintf(&up->errmsg, "%s: Error reading: %s", fup->destpath,
1862                     strerror(errno));
1863                 return (UPDATER_ERR_MSG);
1864         }
1865         close(fd);
1866
1867         bytes = fattr_filesize(fa) - pos;
1868         /* Append the new data. */
1869         do {
1870                 nread = stream_read(up->rd, buf,
1871                     (BUFSIZE > bytes) ? bytes : BUFSIZE);
1872                 if (nread == -1)
1873                         return (UPDATER_ERR_PROTO);
1874                 bytes -= nread;
1875                 if (stream_write(to, buf, nread) == -1)
1876                         goto bad;
1877         } while (bytes > 0);
1878         stream_close(to);
1879
1880         line = stream_getln(up->rd, NULL);
1881         if (line == 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);
1887         if (line == NULL)
1888                 return (UPDATER_ERR_PROTO);
1889
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);
1894
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. */
1902         return (error);
1903 bad:
1904         xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1905             strerror(errno));
1906         return (UPDATER_ERR_MSG);
1907 }
1908
1909 /*
1910  * Read file data from stream of checkout commands, and write it to the
1911  * destination.
1912  */
1913 static int
1914 updater_read_checkout(struct stream *src, struct stream *dest)
1915 {
1916         ssize_t nbytes;
1917         size_t size;
1918         char *line;
1919         int first;
1920
1921         first = 1;
1922         line = stream_getln(src, &size);
1923         while (line != NULL) {
1924                 if (line[size - 1] == '\n')
1925                         size--;
1926                 if ((size == 1 && *line == '.') ||
1927                     (size == 2 && strncmp(line, ".+", 2) == 0))
1928                         break;
1929                 if (size >= 2 && strncmp(line, "..", 2) == 0) {
1930                         size--;
1931                         line++;
1932                 }
1933                 if (!first) {
1934                         nbytes = stream_write(dest, "\n", 1);
1935                         if (nbytes == -1)
1936                                 return (UPDATER_ERR_MSG);
1937                 }
1938                 nbytes = stream_write(dest, line, size);
1939                 if (nbytes == -1)
1940                         return (UPDATER_ERR_MSG);
1941                 line = stream_getln(src, &size);
1942                 first = 0;
1943         }
1944         if (line == NULL)
1945                 return (UPDATER_ERR_READ);
1946         if (size == 1 && *line == '.') {
1947                 nbytes = stream_write(dest, "\n", 1);
1948                 if (nbytes == -1)
1949                         return (UPDATER_ERR_MSG);
1950         }
1951         return (0);
1952 }
1953
1954 /* Update file using the rsync protocol. */
1955 static int
1956 updater_rsync(struct updater *up, struct file_update *fup, size_t blocksize)
1957 {
1958         struct statusrec *sr;
1959         struct stream *to;
1960         char md5[MD5_DIGEST_SIZE];
1961         ssize_t nbytes;
1962         size_t blocknum, blockstart, blockcount;
1963         char *buf, *line;
1964         int error, orig;
1965
1966         sr = &fup->srbuf;
1967
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,
1971             0600);
1972         if (to == NULL) {
1973                 xasprintf(&up->errmsg, "%s: Cannot create: %s",
1974                     fup->temppath, strerror(errno));
1975                 return (UPDATER_ERR_MSG);
1976         }
1977         orig = open(fup->destpath, O_RDONLY);
1978         if (orig < 0) {
1979                 xasprintf(&up->errmsg, "%s: Cannot open: %s",
1980                     fup->destpath, strerror(errno));
1981                 return (UPDATER_ERR_MSG);
1982         }
1983         stream_filter_start(to, STREAM_FILTER_MD5, md5);
1984
1985         error = updater_read_checkout(up->rd, to);
1986         if (error) {
1987                 xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
1988                     strerror(errno));
1989                 return (error);
1990         }
1991
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)
1998                         break;
1999                 error = UPDATER_ERR_PROTO;
2000                 if (proto_get_sizet(&line, &blockstart, 10) != 0)
2001                         goto bad;
2002                 if (proto_get_sizet(&line, &blockcount, 10) != 0)
2003                         goto bad;
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);
2009                         if (nbytes < 0) {
2010                                 xasprintf(&up->errmsg, "%s: Cannot read: %s",
2011                                     fup->destpath, strerror(errno));
2012                                 goto bad;
2013                         }
2014                         nbytes = stream_write(to, buf, nbytes);
2015                         if (nbytes == -1) {
2016                                 xasprintf(&up->errmsg, "%s: Cannot write: %s",
2017                                     fup->temppath, strerror(errno));
2018                                 goto bad;
2019                         }
2020                 }
2021                 /* Get the remaining text from the server. */
2022                 error = updater_read_checkout(up->rd, to);
2023                 if (error) {
2024                         xasprintf(&up->errmsg, "%s: Cannot write: %s",
2025                             fup->temppath, strerror(errno));
2026                         goto bad;
2027                 }
2028                 line = stream_getln(up->rd, NULL);
2029         }
2030         stream_close(to);
2031         close(orig);
2032
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);
2038
2039         error = updater_updatefile(up, fup, md5, 0);
2040         fup->wantmd5 = NULL;    /* So that it doesn't get freed. */
2041 bad:
2042         free(buf);
2043         return (error);
2044 }