]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/csup/updater.c
This commit was generated by cvs2svn to compensate for changes in r172665,
[FreeBSD/FreeBSD.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 <errno.h>
34 #include <fcntl.h>
35 #include <stddef.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #include "config.h"
42 #include "diff.h"
43 #include "fattr.h"
44 #include "fixups.h"
45 #include "keyword.h"
46 #include "updater.h"
47 #include "misc.h"
48 #include "mux.h"
49 #include "proto.h"
50 #include "status.h"
51 #include "stream.h"
52
53 /* Internal error codes. */
54 #define UPDATER_ERR_PROTO       (-1)    /* Protocol error. */
55 #define UPDATER_ERR_MSG         (-2)    /* Error is in updater->errmsg. */
56 #define UPDATER_ERR_READ        (-3)    /* Error reading from server. */
57 #define UPDATER_ERR_DELETELIM   (-4)    /* File deletion limit exceeded. */
58
59 /* Everything needed to update a file. */
60 struct file_update {
61         struct statusrec srbuf;
62         char *destpath;
63         char *temppath;
64         char *coname;           /* Points somewhere in destpath. */
65         char *wantmd5;
66         struct coll *coll;
67         struct status *st;
68         /* Those are only used for diff updating. */
69         char *author;
70         struct stream *orig;
71         struct stream *to;
72         int expand;
73 };
74
75 struct updater {
76         struct config *config;
77         struct stream *rd;
78         char *errmsg;
79         int deletecount;
80 };
81
82 static struct file_update       *fup_new(struct coll *, struct status *);
83 static int       fup_prepare(struct file_update *, char *);
84 static void      fup_cleanup(struct file_update *);
85 static void      fup_free(struct file_update *);
86
87 static void      updater_prunedirs(char *, char *);
88 static int       updater_batch(struct updater *, int);
89 static int       updater_docoll(struct updater *, struct file_update *, int);
90 static int       updater_delete(struct updater *, struct file_update *);
91 static void      updater_deletefile(const char *);
92 static int       updater_checkout(struct updater *, struct file_update *, int);
93 static int       updater_setattrs(struct updater *, struct file_update *,
94                      char *, char *, char *, char *, char *, struct fattr *);
95 static int       updater_updatefile(struct updater *, struct file_update *fup,
96                      const char *, int);
97 static int       updater_diff(struct updater *, struct file_update *);
98 static int       updater_diff_batch(struct updater *, struct file_update *);
99 static int       updater_diff_apply(struct updater *, struct file_update *,
100                      char *);
101
102 static struct file_update *
103 fup_new(struct coll *coll, struct status *st)
104 {
105         struct file_update *fup;
106
107         fup = xmalloc(sizeof(struct file_update));
108         memset(fup, 0, sizeof(*fup));
109         fup->coll = coll;
110         fup->st = st;
111         return (fup);
112 }
113
114 static int
115 fup_prepare(struct file_update *fup, char *name)
116 {
117         struct coll *coll;
118
119         coll = fup->coll;
120         fup->destpath = checkoutpath(coll->co_prefix, name);
121         if (fup->destpath == NULL)
122                 return (-1);
123         fup->coname = fup->destpath + coll->co_prefixlen + 1;
124         return (0);
125 }
126
127 /* Called after each file update to reinit the structure. */
128 static void
129 fup_cleanup(struct file_update *fup)
130 {
131         struct statusrec *sr;
132
133         sr = &fup->srbuf;
134
135         if (fup->destpath != NULL) {
136                 free(fup->destpath);
137                 fup->destpath = NULL;
138         }
139         if (fup->temppath != NULL) {
140                 free(fup->temppath);
141                 fup->temppath = NULL;
142         }
143         fup->coname = NULL;
144         if (fup->author != NULL) {
145                 free(fup->author);
146                 fup->author = NULL;
147         }
148         fup->expand = 0;
149         if (fup->wantmd5 != NULL) {
150                 free(fup->wantmd5);
151                 fup->wantmd5 = NULL;
152         }
153         if (fup->orig != NULL) {
154                 stream_close(fup->orig);
155                 fup->orig = NULL;
156         }
157         if (fup->to != NULL) {
158                 stream_close(fup->to);
159                 fup->to = NULL;
160         }
161         if (sr->sr_file != NULL)
162                 free(sr->sr_file);
163         if (sr->sr_tag != NULL)
164                 free(sr->sr_tag);
165         if (sr->sr_date != NULL)
166                 free(sr->sr_date);
167         if (sr->sr_revnum != NULL)
168                 free(sr->sr_revnum);
169         if (sr->sr_revdate != NULL)
170                 free(sr->sr_revdate);
171         fattr_free(sr->sr_clientattr);
172         fattr_free(sr->sr_serverattr);
173         memset(sr, 0, sizeof(*sr));
174 }
175
176 static void
177 fup_free(struct file_update *fup)
178 {
179
180         fup_cleanup(fup);
181         free(fup);
182 }
183
184 void *
185 updater(void *arg)
186 {
187         struct thread_args *args;
188         struct updater upbuf, *up;
189         int error;
190
191         args = arg;
192
193         up = &upbuf;
194         up->config = args->config;
195         up->rd = args->rd;
196         up->errmsg = NULL;
197         up->deletecount = 0;
198
199         error = updater_batch(up, 0);
200
201         /*
202          * Make sure to close the fixups even in case of an error,
203          * so that the lister thread doesn't block indefinitely.
204          */
205         fixups_close(up->config->fixups);
206         if (!error)
207                 error = updater_batch(up, 1);
208         switch (error) {
209         case UPDATER_ERR_PROTO:
210                 xasprintf(&args->errmsg, "Updater failed: Protocol error");
211                 args->status = STATUS_FAILURE;
212                 break;
213         case UPDATER_ERR_MSG:
214                 xasprintf(&args->errmsg, "Updater failed: %s", up->errmsg);
215                 free(up->errmsg);
216                 args->status = STATUS_FAILURE;
217                 break;
218         case UPDATER_ERR_READ:
219                 if (stream_eof(up->rd)) {
220                         xasprintf(&args->errmsg, "Updater failed: "
221                             "Premature EOF from server");
222                 } else {
223                         xasprintf(&args->errmsg, "Updater failed: "
224                             "Network read failure: %s", strerror(errno));
225                 }
226                 args->status = STATUS_TRANSIENTFAILURE;
227                 break;
228         case UPDATER_ERR_DELETELIM:
229                 xasprintf(&args->errmsg, "Updater failed: "
230                     "File deletion limit exceeded");
231                 args->status = STATUS_FAILURE;
232                 break;
233         default:
234                 assert(error == 0);
235                 args->status = STATUS_SUCCESS;
236         };
237         return (NULL);
238 }
239
240 static int
241 updater_batch(struct updater *up, int isfixups)
242 {
243         struct stream *rd;
244         struct coll *coll;
245         struct status *st;
246         struct file_update *fup;
247         char *line, *cmd, *errmsg, *collname, *release;
248         int error;
249
250         rd = up->rd;
251         STAILQ_FOREACH(coll, &up->config->colls, co_next) {
252                 if (coll->co_options & CO_SKIP)
253                         continue;
254                 umask(coll->co_umask);
255                 line = stream_getln(rd, NULL);
256                 if (line == NULL)
257                         return (UPDATER_ERR_READ);
258                 cmd = proto_get_ascii(&line);
259                 collname = proto_get_ascii(&line);
260                 release = proto_get_ascii(&line);
261                 if (release == NULL || line != NULL)
262                         return (UPDATER_ERR_PROTO);
263                 if (strcmp(cmd, "COLL") != 0 ||
264                     strcmp(collname, coll->co_name) != 0 ||
265                     strcmp(release, coll->co_release) != 0)
266                         return (UPDATER_ERR_PROTO);
267
268                 if (!isfixups)
269                         lprintf(1, "Updating collection %s/%s\n", coll->co_name,
270                             coll->co_release);
271
272                 if (coll->co_options & CO_COMPRESS)
273                         stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL);
274
275                 st = status_open(coll, coll->co_scantime, &errmsg);
276                 if (st == NULL) {
277                         up->errmsg = errmsg;
278                         return (UPDATER_ERR_MSG);
279                 }
280                 fup = fup_new(coll, st);
281                 error = updater_docoll(up, fup, isfixups);
282                 status_close(st, &errmsg);
283                 fup_free(fup);
284                 if (errmsg != NULL) {
285                         /* Discard previous error. */
286                         if (up->errmsg != NULL)
287                                 free(up->errmsg);
288                         up->errmsg = errmsg;
289                         return (UPDATER_ERR_MSG);
290                 }
291                 if (error)
292                         return (error);
293
294                 if (coll->co_options & CO_COMPRESS)
295                         stream_filter_stop(rd);
296         }
297         line = stream_getln(rd, NULL);
298         if (line == NULL)
299                 return (UPDATER_ERR_READ);
300         if (strcmp(line, ".") != 0)
301                 return (UPDATER_ERR_PROTO);
302         return (0);
303 }
304
305 static int
306 updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
307 {
308         struct stream *rd;
309         struct coll *coll;
310         struct statusrec srbuf, *sr;
311         struct fattr *rcsattr, *tmp;
312         char *cmd, *line, *msg, *attr;
313         char *name, *tag, *date, *revdate;
314         char *expand, *wantmd5, *revnum;
315         time_t t;
316         int error, needfixupmsg;
317
318         error = 0;
319         rd = up->rd;
320         coll = fup->coll;
321         needfixupmsg = isfixups;
322         while ((line = stream_getln(rd, NULL)) != NULL) {
323                 if (strcmp(line, ".") == 0)
324                         break;
325                 memset(&srbuf, 0, sizeof(srbuf));
326                 if (needfixupmsg) {
327                         lprintf(1, "Applying fixups for collection %s/%s\n",
328                             coll->co_name, coll->co_release);
329                         needfixupmsg = 0;
330                 }
331                 cmd = proto_get_ascii(&line);
332                 if (cmd == NULL || strlen(cmd) != 1)
333                         return (UPDATER_ERR_PROTO);
334                 switch (cmd[0]) {
335                 case 'T':
336                         /* Update recorded information for checked-out file. */
337                         name = proto_get_ascii(&line);
338                         tag = proto_get_ascii(&line);
339                         date = proto_get_ascii(&line);
340                         revnum = proto_get_ascii(&line);
341                         revdate = proto_get_ascii(&line);
342                         attr = proto_get_ascii(&line);
343                         if (attr == NULL || line != NULL)
344                                 return (UPDATER_ERR_PROTO);
345
346                         rcsattr = fattr_decode(attr);
347                         if (rcsattr == NULL)
348                                 return (UPDATER_ERR_PROTO);
349
350                         error = fup_prepare(fup, name);
351                         if (error)
352                                 return (UPDATER_ERR_PROTO);
353                         error = updater_setattrs(up, fup, name, tag, date,
354                             revnum, revdate, rcsattr);
355                         fattr_free(rcsattr);
356                         if (error)
357                                 return (error);
358                         break;
359                 case 'c':
360                         /* Checkout dead file. */
361                         name = proto_get_ascii(&line);
362                         tag = proto_get_ascii(&line);
363                         date = proto_get_ascii(&line);
364                         attr = proto_get_ascii(&line);
365                         if (attr == NULL || line != NULL)
366                                 return (UPDATER_ERR_PROTO);
367
368                         error = fup_prepare(fup, name);
369                         if (error)
370                                 return (UPDATER_ERR_PROTO);
371                         /* Theoritically, the file does not exist on the client.
372                            Just to make sure, we'll delete it here, if it
373                            exists. */
374                         if (access(fup->destpath, F_OK) == 0) {
375                                 error = updater_delete(up, fup);
376                                 if (error)
377                                         return (error);
378                         }
379
380                         sr = &srbuf;
381                         sr->sr_type = SR_CHECKOUTDEAD;
382                         sr->sr_file = name;
383                         sr->sr_tag = tag;
384                         sr->sr_date = date;
385                         sr->sr_serverattr = fattr_decode(attr);
386                         if (sr->sr_serverattr == NULL)
387                                 return (UPDATER_ERR_PROTO);
388
389                         error = status_put(fup->st, sr);
390                         fattr_free(sr->sr_serverattr);
391                         if (error) {
392                                 up->errmsg = status_errmsg(fup->st);
393                                 return (UPDATER_ERR_MSG);
394                         }
395                         break;
396                 case 'U':
397                         /* Update live checked-out file. */
398                         name = proto_get_ascii(&line);
399                         tag = proto_get_ascii(&line);
400                         date = proto_get_ascii(&line);
401                         proto_get_ascii(&line); /* XXX - oldRevNum */
402                         proto_get_ascii(&line); /* XXX - fromAttic */
403                         proto_get_ascii(&line); /* XXX - logLines */
404                         expand = proto_get_ascii(&line);
405                         attr = proto_get_ascii(&line);
406                         wantmd5 = proto_get_ascii(&line);
407                         if (wantmd5 == NULL || line != NULL)
408                                 return (UPDATER_ERR_PROTO);
409
410                         sr = &fup->srbuf;
411                         sr->sr_type = SR_CHECKOUTLIVE;
412                         sr->sr_file = xstrdup(name);
413                         sr->sr_date = xstrdup(date);
414                         sr->sr_tag = xstrdup(tag);
415                         sr->sr_serverattr = fattr_decode(attr);
416                         if (sr->sr_serverattr == NULL)
417                                 return (UPDATER_ERR_PROTO);
418
419                         fup->expand = keyword_decode_expand(expand);
420                         if (fup->expand == -1)
421                                 return (UPDATER_ERR_PROTO);
422                         error = fup_prepare(fup, name);
423                         if (error)
424                                 return (UPDATER_ERR_PROTO);
425
426                         fup->wantmd5 = xstrdup(wantmd5);
427                         fup->temppath = tempname(fup->destpath);
428                         error = updater_diff(up, fup);
429                         if (error)
430                                 return (error);
431                         break;
432                 case 'u':
433                         /* Update dead checked-out file. */
434                         name = proto_get_ascii(&line);
435                         tag = proto_get_ascii(&line);
436                         date = proto_get_ascii(&line);
437                         attr = proto_get_ascii(&line);
438                         if (attr == NULL || line != NULL)
439                                 return (UPDATER_ERR_PROTO);
440
441                         error = fup_prepare(fup, name);
442                         if (error)
443                                 return (UPDATER_ERR_PROTO);
444                         error = updater_delete(up, fup);
445                         if (error)
446                                 return (error);
447                         sr = &srbuf;
448                         sr->sr_type = SR_CHECKOUTDEAD;
449                         sr->sr_file = name;
450                         sr->sr_tag = tag;
451                         sr->sr_date = date;
452                         sr->sr_serverattr = fattr_decode(attr);
453                         if (sr->sr_serverattr == NULL)
454                                 return (UPDATER_ERR_PROTO);
455                         error = status_put(fup->st, sr);
456                         fattr_free(sr->sr_serverattr);
457                         if (error) {
458                                 up->errmsg = status_errmsg(fup->st);
459                                 return (UPDATER_ERR_MSG);
460                         }
461                         break;
462                 case 'C':
463                 case 'Y':
464                         /* Checkout file. */
465                         name = proto_get_ascii(&line);
466                         tag = proto_get_ascii(&line);
467                         date = proto_get_ascii(&line);
468                         revnum = proto_get_ascii(&line);
469                         revdate = proto_get_ascii(&line);
470                         attr = proto_get_ascii(&line);
471                         if (attr == NULL || line != NULL)
472                                 return (UPDATER_ERR_PROTO);
473
474                         sr = &fup->srbuf;
475                         sr->sr_type = SR_CHECKOUTLIVE;
476                         sr->sr_file = xstrdup(name);
477                         sr->sr_tag = xstrdup(tag);
478                         sr->sr_date = xstrdup(date);
479                         sr->sr_revnum = xstrdup(revnum);
480                         sr->sr_revdate = xstrdup(revdate);
481                         sr->sr_serverattr = fattr_decode(attr);
482                         if (sr->sr_serverattr == NULL)
483                                 return (UPDATER_ERR_PROTO);
484
485                         t = rcsdatetotime(revdate);
486                         if (t == -1)
487                                 return (UPDATER_ERR_PROTO);
488
489                         sr->sr_clientattr = fattr_new(FT_FILE, t);
490                         tmp = fattr_forcheckout(sr->sr_serverattr,
491                             coll->co_umask);
492                         fattr_override(sr->sr_clientattr, tmp, FA_MASK);
493                         fattr_free(tmp);
494                         fattr_mergedefault(sr->sr_clientattr);
495                         error = fup_prepare(fup, name);
496                         if (error)
497                                 return (UPDATER_ERR_PROTO);
498                         fup->temppath = tempname(fup->destpath);
499                         if (*cmd == 'Y')
500                                 error = updater_checkout(up, fup, 1);
501                         else
502                                 error = updater_checkout(up, fup, 0);
503                         if (error)
504                                 return (error);
505                         break;
506                 case 'D':
507                         /* Delete file. */
508                         name = proto_get_ascii(&line);
509                         if (name == NULL || line != NULL)
510                                 return (UPDATER_ERR_PROTO);
511                         error = fup_prepare(fup, name);
512                         if (error)
513                                 return (UPDATER_ERR_PROTO);
514                         error = updater_delete(up, fup);
515                         if (error)
516                                 return (error);
517                         error = status_delete(fup->st, name, 0);
518                         if (error) {
519                                 up->errmsg = status_errmsg(fup->st);
520                                 return (UPDATER_ERR_MSG);
521                         }
522                         break;
523                 case '!':
524                         /* Warning from server. */
525                         msg = proto_get_rest(&line);
526                         if (msg == NULL)
527                                 return (UPDATER_ERR_PROTO);
528                         lprintf(-1, "Server warning: %s\n", msg);
529                         break;
530                 default:
531                         return (UPDATER_ERR_PROTO);
532                 }
533                 fup_cleanup(fup);
534         }
535         if (line == NULL)
536                 return (UPDATER_ERR_READ);
537         return (0);
538 }
539
540 /* Delete file. */
541 static int
542 updater_delete(struct updater *up, struct file_update *fup)
543 {
544         struct config *config;
545         struct coll *coll;
546
547         config = up->config;
548         coll = fup->coll;
549         if (coll->co_options & CO_DELETE) {
550                 lprintf(1, " Delete %s\n", fup->coname);
551                 if (config->deletelim >= 0 &&
552                     up->deletecount >= config->deletelim)
553                         return (UPDATER_ERR_DELETELIM);
554                 up->deletecount++;
555                 updater_deletefile(fup->destpath);
556                 if (coll->co_options & CO_CHECKOUTMODE)
557                         updater_prunedirs(coll->co_prefix, fup->destpath);
558         } else {
559                 lprintf(1," NoDelete %s\n", fup->coname);
560         }
561         return (0);
562 }
563
564 static void
565 updater_deletefile(const char *path)
566 {
567         int error;
568
569         error = fattr_delete(path);
570         if (error && errno != ENOENT) {
571                 lprintf(-1, "Cannot delete \"%s\": %s\n",
572                     path, strerror(errno));
573         }
574 }
575
576 static int
577 updater_setattrs(struct updater *up, struct file_update *fup, char *name,
578     char *tag, char *date, char *revnum, char *revdate, struct fattr *rcsattr)
579 {
580         struct statusrec sr;
581         struct status *st;
582         struct coll *coll;
583         struct fattr *fileattr, *fa;
584         char *path;
585         int error, rv;
586
587         coll = fup->coll;
588         st = fup->st;
589         path = fup->destpath;
590
591         fileattr = fattr_frompath(path, FATTR_NOFOLLOW);
592         if (fileattr == NULL) {
593                 /* The file has vanished. */
594                 error = status_delete(st, name, 0);
595                 if (error) {
596                         up->errmsg = status_errmsg(st);
597                         return (UPDATER_ERR_MSG);
598                 }
599                 return (0);
600         }
601         fa = fattr_forcheckout(rcsattr, coll->co_umask);
602         fattr_override(fileattr, fa, FA_MASK);
603         fattr_free(fa);
604
605         rv = fattr_install(fileattr, path, NULL);
606         if (rv == -1) {
607                 lprintf(1, " SetAttrs %s\n", fup->coname);
608                 fattr_free(fileattr);
609                 xasprintf(&up->errmsg, "Cannot set attributes for \"%s\": %s",
610                     path, strerror(errno));
611                 return (UPDATER_ERR_MSG);
612         }
613         if (rv == 1) {
614                 lprintf(1, " SetAttrs %s\n", fup->coname);
615                 fattr_free(fileattr);
616                 fileattr = fattr_frompath(path, FATTR_NOFOLLOW);
617                 if (fileattr == NULL) {
618                         /* We're being very unlucky. */
619                         error = status_delete(st, name, 0);
620                         if (error) {
621                                 up->errmsg = status_errmsg(st);
622                                 return (UPDATER_ERR_MSG);
623                         }
624                         return (0);
625                 }
626         }
627
628         fattr_maskout(fileattr, FA_COIGNORE);
629
630         sr.sr_type = SR_CHECKOUTLIVE;
631         sr.sr_file = name;
632         sr.sr_tag = tag;
633         sr.sr_date = date;
634         sr.sr_revnum = revnum;
635         sr.sr_revdate = revdate;
636         sr.sr_clientattr = fileattr;
637         sr.sr_serverattr = rcsattr;
638
639         error = status_put(st, &sr);
640         fattr_free(fileattr);
641         if (error) {
642                 up->errmsg = status_errmsg(st);
643                 return (UPDATER_ERR_MSG);
644         }
645         return (0);
646 }
647
648 static int
649 updater_updatefile(struct updater *up, struct file_update *fup,
650     const char *md5, int isfixup)
651 {
652         struct coll *coll;
653         struct status *st;
654         struct statusrec *sr;
655         struct fattr *fileattr;
656         int error, rv;
657
658         coll = fup->coll;
659         sr = &fup->srbuf;
660         st = fup->st;
661
662         if (strcmp(fup->wantmd5, md5) != 0) {
663                 if (isfixup) {
664                         lprintf(-1, "%s: Checksum mismatch -- "
665                             "file not updated\n", fup->destpath);
666                 } else {
667                         lprintf(-1, "%s: Checksum mismatch -- "
668                             "will transfer entire file\n", fup->destpath);
669                         fixups_put(up->config->fixups, fup->coll, sr->sr_file);
670                 }
671                 if (coll->co_options & CO_KEEPBADFILES)
672                         lprintf(-1, "Bad version saved in %s\n", fup->temppath);
673                 else
674                         updater_deletefile(fup->temppath);
675                 return (0);
676         }
677
678         fattr_umask(sr->sr_clientattr, coll->co_umask);
679         rv = fattr_install(sr->sr_clientattr, fup->destpath, fup->temppath);
680         if (rv == -1) {
681                 xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s",
682                     fup->temppath, fup->destpath, strerror(errno));
683                 return (UPDATER_ERR_MSG);
684         }
685
686         /* XXX Executes */
687         /*
688          * We weren't necessarily able to set all the file attributes to the
689          * desired values, and any executes may have altered the attributes.
690          * To make sure we record the actual attribute values, we fetch
691          * them from the file.
692          *
693          * However, we preserve the link count as received from the
694          * server.  This is important for preserving hard links in mirror
695          * mode.
696          */
697         fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
698         if (fileattr == NULL) {
699                 xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath,
700                     strerror(errno));
701                 return (UPDATER_ERR_MSG);
702         }
703         fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT);
704         fattr_free(sr->sr_clientattr);
705         sr->sr_clientattr = fileattr;
706
707         /*
708          * To save space, don't write out the device and inode unless
709          * the link count is greater than 1.  These attributes are used
710          * only for detecting hard links.  If the link count is 1 then we
711          * know there aren't any hard links.
712          */
713         if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) ||
714             fattr_getlinkcount(sr->sr_clientattr) <= 1)
715                 fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE);
716
717         if (coll->co_options & CO_CHECKOUTMODE)
718                 fattr_maskout(sr->sr_clientattr, FA_COIGNORE);
719
720         error = status_put(st, sr);
721         if (error) {
722                 up->errmsg = status_errmsg(st);
723                 return (UPDATER_ERR_MSG);
724         }
725         return (0);
726 }
727
728 static int
729 updater_diff(struct updater *up, struct file_update *fup)
730 {
731         char md5[MD5_DIGEST_SIZE];
732         struct coll *coll;
733         struct statusrec *sr;
734         struct fattr *fa, *tmp;
735         char *author, *path, *revnum, *revdate;
736         char *line, *cmd;
737         int error;
738
739         coll = fup->coll;
740         sr = &fup->srbuf;
741         path = fup->destpath;
742
743         lprintf(1, " Edit %s\n", fup->coname);
744         while ((line = stream_getln(up->rd, NULL)) != NULL) {
745                 if (strcmp(line, ".") == 0)
746                         break;
747                 cmd = proto_get_ascii(&line);
748                 if (cmd == NULL || strcmp(cmd, "D") != 0)
749                         return (UPDATER_ERR_PROTO);
750                 revnum = proto_get_ascii(&line);
751                 proto_get_ascii(&line); /* XXX - diffbase */
752                 revdate = proto_get_ascii(&line);
753                 author = proto_get_ascii(&line);
754                 if (author == NULL || line != NULL)
755                         return (UPDATER_ERR_PROTO);
756                 if (sr->sr_revnum != NULL)
757                         free(sr->sr_revnum);
758                 if (sr->sr_revdate != NULL)
759                         free(sr->sr_revdate);
760                 if (fup->author != NULL)
761                         free(fup->author);
762                 sr->sr_revnum = xstrdup(revnum);
763                 sr->sr_revdate = xstrdup(revdate);
764                 fup->author = xstrdup(author);
765                 if (fup->orig == NULL) {
766                         /* First patch, the "origin" file is the one we have. */
767                         fup->orig = stream_open_file(path, O_RDONLY);
768                         if (fup->orig == NULL) {
769                                 xasprintf(&up->errmsg, "%s: Cannot open: %s",
770                                     path, strerror(errno));
771                                 return (UPDATER_ERR_MSG);
772                         }
773                 } else {
774                         /* Subsequent patches. */
775                         stream_close(fup->orig);
776                         fup->orig = fup->to;
777                         stream_rewind(fup->orig);
778                         unlink(fup->temppath);
779                         free(fup->temppath);
780                         fup->temppath = tempname(path);
781                 }
782                 fup->to = stream_open_file(fup->temppath,
783                     O_RDWR | O_CREAT | O_TRUNC, 0600);
784                 if (fup->to == NULL) {
785                         xasprintf(&up->errmsg, "%s: Cannot open: %s",
786                             fup->temppath, strerror(errno));
787                         return (UPDATER_ERR_MSG);
788                 }
789                 lprintf(2, "  Add delta %s %s %s\n", sr->sr_revnum,
790                     sr->sr_revdate, fup->author);
791                 error = updater_diff_batch(up, fup);
792                 if (error)
793                         return (error);
794         }
795         if (line == NULL)
796                 return (UPDATER_ERR_READ);
797
798         fa = fattr_frompath(path, FATTR_FOLLOW);
799         tmp = fattr_forcheckout(sr->sr_serverattr, coll->co_umask);
800         fattr_override(fa, tmp, FA_MASK);
801         fattr_free(tmp);
802         fattr_maskout(fa, FA_MODTIME);
803         sr->sr_clientattr = fa;
804
805         if (MD5_File(fup->temppath, md5) == -1) {
806                 xasprintf(&up->errmsg,
807                     "Cannot calculate checksum for \"%s\": %s",
808                     path, strerror(errno));
809                 return (UPDATER_ERR_MSG);
810         }
811         error = updater_updatefile(up, fup, md5, 0);
812         return (error);
813 }
814
815 static int
816 updater_diff_batch(struct updater *up, struct file_update *fup)
817 {
818         struct stream *rd;
819         char *cmd, *line, *state, *tok;
820         int error;
821
822         state = NULL;
823         rd = up->rd;
824         while ((line = stream_getln(rd, NULL)) != NULL) {
825                 if (strcmp(line, ".") == 0)
826                         break;
827                 cmd = proto_get_ascii(&line);
828                 if (cmd == NULL || strlen(cmd) != 1) {
829                         error = UPDATER_ERR_PROTO;
830                         goto bad;
831                 }
832                 switch (cmd[0]) {
833                 case 'L':
834                         line = stream_getln(rd, NULL);
835                         /* XXX - We're just eating the log for now. */
836                         while (line != NULL && strcmp(line, ".") != 0 &&
837                             strcmp(line, ".+") != 0)
838                                 line = stream_getln(rd, NULL);
839                         if (line == NULL) {
840                                 error = UPDATER_ERR_READ;
841                                 goto bad;
842                         }
843                         break;
844                 case 'S':
845                         tok = proto_get_ascii(&line);
846                         if (tok == NULL || line != NULL) {
847                                 error = UPDATER_ERR_PROTO;
848                                 goto bad;
849                         }
850                         if (state != NULL)
851                                 free(state);
852                         state = xstrdup(tok);
853                         break;
854                 case 'T':
855                         error = updater_diff_apply(up, fup, state);
856                         if (error)
857                                 goto bad;
858                         break;
859                 default:
860                         error = UPDATER_ERR_PROTO;
861                         goto bad;
862                 }
863         }
864         if (line == NULL) {
865                 error = UPDATER_ERR_READ;
866                 goto bad;
867         }
868         if (state != NULL)
869                 free(state);
870         return (0);
871 bad:
872         if (state != NULL)
873                 free(state);
874         return (error);
875 }
876
877 int
878 updater_diff_apply(struct updater *up, struct file_update *fup, char *state)
879 {
880         struct diffinfo dibuf, *di;
881         struct coll *coll;
882         struct statusrec *sr;
883         int error;
884
885         coll = fup->coll;
886         sr = &fup->srbuf;
887         di = &dibuf;
888
889         di->di_rcsfile = sr->sr_file;
890         di->di_cvsroot = coll->co_cvsroot;
891         di->di_revnum = sr->sr_revnum;
892         di->di_revdate = sr->sr_revdate;
893         di->di_author = fup->author;
894         di->di_tag = sr->sr_tag;
895         di->di_state = state;
896         di->di_expand = fup->expand;
897
898         error = diff_apply(up->rd, fup->orig, fup->to, coll->co_keyword, di);
899         if (error) {
900                 /* XXX Bad error message */
901                 xasprintf(&up->errmsg, "Bad diff from server");
902                 return (UPDATER_ERR_MSG);
903         }
904         return (0);
905 }
906
907 static int
908 updater_checkout(struct updater *up, struct file_update *fup, int isfixup)
909 {
910         char md5[MD5_DIGEST_SIZE];
911         struct statusrec *sr;
912         struct coll *coll;
913         struct stream *to;
914         char *cmd, *path, *line;
915         size_t size;
916         ssize_t nbytes;
917         int error, first;
918
919         coll = fup->coll;
920         sr = &fup->srbuf;
921         path = fup->destpath;
922
923         if (isfixup)
924                 lprintf(1, " Fixup %s\n", fup->coname);
925         else
926                 lprintf(1, " Checkout %s\n", fup->coname);
927         error = mkdirhier(path, coll->co_umask);
928         if (error) {
929                 xasprintf(&up->errmsg,
930                     "Cannot create directories leading to \"%s\": %s",
931                     path, strerror(errno));
932                 return (UPDATER_ERR_MSG);
933         }
934
935         to = stream_open_file(fup->temppath,
936             O_WRONLY | O_CREAT | O_TRUNC, 0600);
937         if (to == NULL) {
938                 xasprintf(&up->errmsg, "%s: Cannot create: %s",
939                     fup->temppath, strerror(errno));
940                 return (UPDATER_ERR_MSG);
941         }
942         stream_filter_start(to, STREAM_FILTER_MD5, md5);
943         line = stream_getln(up->rd, &size);
944         first = 1;
945         while (line != NULL) {
946                 if (line[size - 1] == '\n')
947                         size--;
948                 if ((size == 1 && *line == '.') ||
949                     (size == 2 && memcmp(line, ".+", 2) == 0))
950                         break;
951                 if (size >= 2 && memcmp(line, "..", 2) == 0) {
952                         size--;
953                         line++;
954                 }
955                 if (!first) {
956                         nbytes = stream_write(to, "\n", 1);
957                         if (nbytes == -1)
958                                 goto bad;
959                 }
960                 stream_write(to, line, size);
961                 line = stream_getln(up->rd, &size);
962                 first = 0;
963         }
964         if (line == NULL) {
965                 stream_close(to);
966                 return (UPDATER_ERR_READ);
967         }
968         if (size == 1 && *line == '.') {
969                 nbytes = stream_write(to, "\n", 1);
970                 if (nbytes == -1)
971                         goto bad;
972         }
973         stream_close(to);
974         /* Get the checksum line. */
975         line = stream_getln(up->rd, NULL);
976         if (line == NULL)
977                 return (UPDATER_ERR_READ);
978         cmd = proto_get_ascii(&line);
979         fup->wantmd5 = proto_get_ascii(&line);
980         if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0)
981                 return (UPDATER_ERR_PROTO);
982         error = updater_updatefile(up, fup, md5, isfixup);
983         fup->wantmd5 = NULL;    /* So that it doesn't get freed. */
984         if (error)
985                 return (error);
986         return (0);
987 bad:
988         xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath,
989             strerror(errno));
990         return (UPDATER_ERR_MSG);
991 }
992
993 /*
994  * Remove all empty directories below file.
995  * This function will trash the path passed to it.
996  */
997 static void
998 updater_prunedirs(char *base, char *file)
999 {
1000         char *cp;
1001         int error;
1002
1003         while ((cp = strrchr(file, '/')) != NULL) {
1004                 *cp = '\0';
1005                 if (strcmp(base, file) == 0)
1006                         return;
1007                 error = rmdir(file);
1008                 if (error)
1009                         return;
1010         }
1011 }