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