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 <assert.h>
30#include <errno.h>
31#include <stdlib.h>
32#include <string.h>
33#include <stdio.h>
34
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <unistd.h>
38
39#include "config.h"
40#include "detailer.h"
41#include "fixups.h"
42#include "globtree.h"
43#include "misc.h"
44#include "mux.h"
45#include "proto.h"
46#include "rcsfile.h"
47#include "rsyncfile.h"
48#include "status.h"
49#include "stream.h"
50
51/* Internal error codes. */
52#define	DETAILER_ERR_PROTO	(-1)	/* Protocol error. */
53#define	DETAILER_ERR_MSG	(-2)	/* Error is in detailer->errmsg. */
54#define	DETAILER_ERR_READ	(-3)	/* Error reading from server. */
55#define	DETAILER_ERR_WRITE	(-4)	/* Error writing to server. */
56
57struct detailer {
58	struct config *config;
59	struct stream *rd;
60	struct stream *wr;
61	char *errmsg;
62};
63
64static int	detailer_batch(struct detailer *);
65static int	detailer_coll(struct detailer *, struct coll *,
66		    struct status *);
67static int	detailer_dofile_co(struct detailer *, struct coll *,
68		    struct status *, char *);
69static int	detailer_dofile_rcs(struct detailer *, struct coll *,
70		    char *, char *);
71static int	detailer_dofile_regular(struct detailer *, char *, char *);
72static int	detailer_dofile_rsync(struct detailer *, char *, char *);
73static int	detailer_checkrcsattr(struct detailer *, struct coll *, char *,
74		    struct fattr *, int);
75int		detailer_send_details(struct detailer *, struct coll *, char *,
76		    char *, struct fattr *);
77
78void *
79detailer(void *arg)
80{
81	struct thread_args *args;
82	struct detailer dbuf, *d;
83	int error;
84
85	args = arg;
86
87	d = &dbuf;
88	d->config = args->config;
89	d->rd = args->rd;
90	d->wr = args->wr;
91	d->errmsg = NULL;
92
93	error = detailer_batch(d);
94	switch (error) {
95	case DETAILER_ERR_PROTO:
96		xasprintf(&args->errmsg, "Detailer failed: Protocol error");
97		args->status = STATUS_FAILURE;
98		break;
99	case DETAILER_ERR_MSG:
100		xasprintf(&args->errmsg, "Detailer failed: %s", d->errmsg);
101		free(d->errmsg);
102		args->status = STATUS_FAILURE;
103		break;
104	case DETAILER_ERR_READ:
105		if (stream_eof(d->rd)) {
106			xasprintf(&args->errmsg, "Detailer failed: "
107			    "Premature EOF from server");
108		} else {
109			xasprintf(&args->errmsg, "Detailer failed: "
110			    "Network read failure: %s", strerror(errno));
111		}
112		args->status = STATUS_TRANSIENTFAILURE;
113		break;
114	case DETAILER_ERR_WRITE:
115		xasprintf(&args->errmsg, "Detailer failed: "
116		    "Network write failure: %s", strerror(errno));
117		args->status = STATUS_TRANSIENTFAILURE;
118		break;
119	default:
120		assert(error == 0);
121		args->status = STATUS_SUCCESS;
122	}
123	return (NULL);
124}
125
126static int
127detailer_batch(struct detailer *d)
128{
129	struct config *config;
130	struct stream *rd, *wr;
131	struct coll *coll;
132	struct status *st;
133	struct fixup *fixup;
134	char *cmd, *collname, *line, *release;
135	int error, fixupseof;
136
137	config = d->config;
138	rd = d->rd;
139	wr = d->wr;
140	STAILQ_FOREACH(coll, &config->colls, co_next) {
141		if (coll->co_options & CO_SKIP)
142			continue;
143		line = stream_getln(rd, NULL);
144		cmd = proto_get_ascii(&line);
145		collname = proto_get_ascii(&line);
146		release = proto_get_ascii(&line);
147		error = proto_get_time(&line, &coll->co_scantime);
148		if (error || line != NULL || strcmp(cmd, "COLL") != 0 ||
149		    strcmp(collname, coll->co_name) != 0 ||
150		    strcmp(release, coll->co_release) != 0)
151			return (DETAILER_ERR_PROTO);
152		error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
153		    coll->co_release);
154		if (error)
155			return (DETAILER_ERR_WRITE);
156		stream_flush(wr);
157		if (coll->co_options & CO_COMPRESS) {
158			stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL);
159			stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL);
160		}
161		st = status_open(coll, -1, &d->errmsg);
162		if (st == NULL)
163			return (DETAILER_ERR_MSG);
164		error = detailer_coll(d, coll, st);
165		status_close(st, NULL);
166		if (error)
167			return (error);
168		if (coll->co_options & CO_COMPRESS) {
169			stream_filter_stop(rd);
170			stream_filter_stop(wr);
171		}
172		stream_flush(wr);
173	}
174	line = stream_getln(rd, NULL);
175	if (line == NULL)
176		return (DETAILER_ERR_READ);
177	if (strcmp(line, ".") != 0)
178		return (DETAILER_ERR_PROTO);
179	error = proto_printf(wr, ".\n");
180	if (error)
181		return (DETAILER_ERR_WRITE);
182	stream_flush(wr);
183
184	/* Now send fixups if needed. */
185	fixup = NULL;
186	fixupseof = 0;
187	STAILQ_FOREACH(coll, &config->colls, co_next) {
188		if (coll->co_options & CO_SKIP)
189			continue;
190		error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
191		    coll->co_release);
192		if (error)
193			return (DETAILER_ERR_WRITE);
194		if (coll->co_options & CO_COMPRESS)
195			stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL);
196		while (!fixupseof) {
197			if (fixup == NULL)
198				fixup = fixups_get(config->fixups);
199			if (fixup == NULL) {
200				fixupseof = 1;
201				break;
202			}
203			if (fixup->f_coll != coll)
204				break;
205			if (coll->co_options & CO_CHECKOUTMODE)
206				error = proto_printf(wr, "Y %s %s %s\n",
207				    fixup->f_name, coll->co_tag, coll->co_date);
208			else {
209				error = proto_printf(wr, "A %s\n",
210				    fixup->f_name);
211			}
212			if (error)
213				return (DETAILER_ERR_WRITE);
214			fixup = NULL;
215		}
216		error = proto_printf(wr, ".\n");
217		if (error)
218			return (DETAILER_ERR_WRITE);
219		if (coll->co_options & CO_COMPRESS)
220			stream_filter_stop(wr);
221		stream_flush(wr);
222	}
223	error = proto_printf(wr, ".\n");
224	if (error)
225		return (DETAILER_ERR_WRITE);
226	return (0);
227}
228
229static int
230detailer_coll(struct detailer *d, struct coll *coll, struct status *st)
231{
232	struct fattr *rcsattr;
233	struct stream *rd, *wr;
234	char *attr, *cmd, *file, *line, *msg, *path, *target;
235	int error, attic;
236
237	rd = d->rd;
238	wr = d->wr;
239	attic = 0;
240	line = stream_getln(rd, NULL);
241	if (line == NULL)
242		return (DETAILER_ERR_READ);
243	while (strcmp(line, ".") != 0) {
244		cmd = proto_get_ascii(&line);
245		if (cmd == NULL || strlen(cmd) != 1)
246			return (DETAILER_ERR_PROTO);
247		switch (cmd[0]) {
248		case 'D':
249			/* Delete file. */
250			file = proto_get_ascii(&line);
251			if (file == NULL || line != NULL)
252				return (DETAILER_ERR_PROTO);
253			error = proto_printf(wr, "D %s\n", file);
254			if (error)
255				return (DETAILER_ERR_WRITE);
256			break;
257		case 'I':
258		case 'i':
259		case 'j':
260			/* Directory operations. */
261			file = proto_get_ascii(&line);
262			if (file == NULL || line != NULL)
263				return (DETAILER_ERR_PROTO);
264			error = proto_printf(wr, "%s %s\n", cmd, file);
265			if (error)
266				return (DETAILER_ERR_WRITE);
267			break;
268		case 'J':
269			/* Set directory attributes. */
270			file = proto_get_ascii(&line);
271			attr = proto_get_ascii(&line);
272			if (file == NULL || line != NULL || attr == NULL)
273				return (DETAILER_ERR_PROTO);
274			error = proto_printf(wr, "%s %s %s\n", cmd, file, attr);
275			if (error)
276				return (DETAILER_ERR_WRITE);
277			break;
278		case 'H':
279		case 'h':
280			/* Create a hard link. */
281			file = proto_get_ascii(&line);
282			target = proto_get_ascii(&line);
283			if (file == NULL || target == NULL)
284				return (DETAILER_ERR_PROTO);
285			error = proto_printf(wr, "%s %s %s\n", cmd, file,
286			    target);
287			break;
288		case 't':
289			file = proto_get_ascii(&line);
290			attr = proto_get_ascii(&line);
291			if (file == NULL || attr == NULL || line != NULL) {
292				return (DETAILER_ERR_PROTO);
293			}
294			rcsattr = fattr_decode(attr);
295			if (rcsattr == NULL) {
296				return (DETAILER_ERR_PROTO);
297			}
298			error = detailer_checkrcsattr(d, coll, file, rcsattr,
299			    1);
300			break;
301
302		case 'T':
303			file = proto_get_ascii(&line);
304			attr = proto_get_ascii(&line);
305			if (file == NULL || attr == NULL || line != NULL)
306				return (DETAILER_ERR_PROTO);
307			rcsattr = fattr_decode(attr);
308			if (rcsattr == NULL)
309				return (DETAILER_ERR_PROTO);
310			error = detailer_checkrcsattr(d, coll, file, rcsattr,
311			    0);
312			break;
313
314		case 'U':
315			/* Add or update file. */
316			file = proto_get_ascii(&line);
317			if (file == NULL || line != NULL)
318				return (DETAILER_ERR_PROTO);
319			if (coll->co_options & CO_CHECKOUTMODE) {
320				error = detailer_dofile_co(d, coll, st, file);
321			} else {
322				path = cvspath(coll->co_prefix, file, 0);
323				rcsattr = fattr_frompath(path, FATTR_NOFOLLOW);
324				error = detailer_send_details(d, coll, file,
325				    path, rcsattr);
326				if (rcsattr != NULL)
327					fattr_free(rcsattr);
328				free(path);
329			}
330			if (error)
331				return (error);
332			break;
333		case '!':
334			/* Warning from server. */
335			msg = proto_get_rest(&line);
336			if (msg == NULL)
337				return (DETAILER_ERR_PROTO);
338			lprintf(-1, "Server warning: %s\n", msg);
339			break;
340		default:
341			return (DETAILER_ERR_PROTO);
342		}
343		stream_flush(wr);
344		line = stream_getln(rd, NULL);
345		if (line == NULL)
346			return (DETAILER_ERR_READ);
347	}
348	error = proto_printf(wr, ".\n");
349	if (error)
350		return (DETAILER_ERR_WRITE);
351	return (0);
352}
353
354/*
355 * Tell the server to update a regular file.
356 */
357static int
358detailer_dofile_regular(struct detailer *d, char *name, char *path)
359{
360	struct stream *wr;
361	struct stat st;
362	char md5[MD5_DIGEST_SIZE];
363	int error;
364
365	wr = d->wr;
366	error = stat(path, &st);
367	/* If we don't have it or it's unaccessible, we want it again. */
368	if (error) {
369		proto_printf(wr, "A %s\n", name);
370		return (0);
371	}
372
373	/* If not, we want the file to be updated. */
374	error = MD5_File(path, md5);
375	if (error) {
376		lprintf(-1, "Error reading \"%s\"\n", name);
377		return (error);
378	}
379	error = proto_printf(wr, "R %s %O %s\n", name, st.st_size, md5);
380	if (error)
381		return (DETAILER_ERR_WRITE);
382	return (0);
383}
384
385/*
386 * Tell the server to update a file with the rsync algorithm.
387 */
388static int
389detailer_dofile_rsync(struct detailer *d, char *name, char *path)
390{
391	struct stream *wr;
392	struct rsyncfile *rf;
393
394	wr = d->wr;
395	rf = rsync_open(path, 0, 1);
396	if (rf == NULL) {
397		/* Fallback if we fail in opening it. */
398		proto_printf(wr, "A %s\n", name);
399		return (0);
400	}
401	proto_printf(wr, "r %s %z %z\n", name, rsync_filesize(rf),
402	    rsync_blocksize(rf));
403	/* Detail the blocks. */
404	while (rsync_nextblock(rf) != 0)
405		proto_printf(wr, "%s %s\n", rsync_rsum(rf), rsync_blockmd5(rf));
406	proto_printf(wr, ".\n");
407	rsync_close(rf);
408	return (0);
409}
410
411/*
412 * Tell the server to update an RCS file that we have, or send it if we don't.
413 */
414static int
415detailer_dofile_rcs(struct detailer *d, struct coll *coll, char *name,
416    char *path)
417{
418	struct stream *wr;
419	struct fattr *fa;
420	struct rcsfile *rf;
421	int error;
422
423	wr = d->wr;
424	path = atticpath(coll->co_prefix, name);
425	fa = fattr_frompath(path, FATTR_NOFOLLOW);
426	if (fa == NULL) {
427		/* We don't have it, so send request to get it. */
428		error = proto_printf(wr, "A %s\n", name);
429		if (error)
430			return (DETAILER_ERR_WRITE);
431		free(path);
432		return (0);
433	}
434
435	rf = rcsfile_frompath(path, name, coll->co_cvsroot, coll->co_tag, 1);
436	free(path);
437	if (rf == NULL) {
438		error = proto_printf(wr, "A %s\n", name);
439		if (error)
440			return (DETAILER_ERR_WRITE);
441		return (0);
442	}
443	/* Tell to update the RCS file. The client version details follow. */
444	rcsfile_send_details(rf, wr);
445	rcsfile_free(rf);
446	fattr_free(fa);
447	return (0);
448}
449
450static int
451detailer_dofile_co(struct detailer *d, struct coll *coll, struct status *st,
452    char *file)
453{
454	struct stream *wr;
455	struct fattr *fa;
456	struct statusrec *sr;
457	char md5[MD5_DIGEST_SIZE];
458	char *path;
459	int error, ret;
460
461	wr = d->wr;
462	path = checkoutpath(coll->co_prefix, file);
463	if (path == NULL)
464		return (DETAILER_ERR_PROTO);
465	fa = fattr_frompath(path, FATTR_NOFOLLOW);
466	if (fa == NULL) {
467		/* We don't have the file, so the only option at this
468		   point is to tell the server to send it.  The server
469		   may figure out that the file is dead, in which case
470		   it will tell us. */
471		error = proto_printf(wr, "C %s %s %s\n",
472		    file, coll->co_tag, coll->co_date);
473		free(path);
474		if (error)
475			return (DETAILER_ERR_WRITE);
476		return (0);
477	}
478	ret = status_get(st, file, 0, 0, &sr);
479	if (ret == -1) {
480		d->errmsg = status_errmsg(st);
481		free(path);
482		return (DETAILER_ERR_MSG);
483	}
484	if (ret == 0)
485		sr = NULL;
486
487	/* If our recorded information doesn't match the file that the
488	   client has, then ignore the recorded information. */
489	if (sr != NULL && (sr->sr_type != SR_CHECKOUTLIVE ||
490	    !fattr_equal(sr->sr_clientattr, fa)))
491		sr = NULL;
492	fattr_free(fa);
493	if (sr != NULL && strcmp(sr->sr_revdate, ".") != 0) {
494		error = proto_printf(wr, "U %s %s %s %s %s\n", file,
495		    coll->co_tag, coll->co_date, sr->sr_revnum, sr->sr_revdate);
496		free(path);
497		if (error)
498			return (DETAILER_ERR_WRITE);
499		return (0);
500	}
501
502	/*
503	 * We don't have complete and/or accurate recorded information
504	 * about what version of the file we have.  Compute the file's
505	 * checksum as an aid toward identifying which version it is.
506	 */
507	error = MD5_File(path, md5);
508	if (error) {
509		xasprintf(&d->errmsg,
510		    "Cannot calculate checksum for \"%s\": %s", path,
511		    strerror(errno));
512		return (DETAILER_ERR_MSG);
513	}
514	free(path);
515	if (sr == NULL) {
516		error = proto_printf(wr, "S %s %s %s %s\n", file,
517		    coll->co_tag, coll->co_date, md5);
518	} else {
519		error = proto_printf(wr, "s %s %s %s %s %s\n", file,
520		    coll->co_tag, coll->co_date, sr->sr_revnum, md5);
521	}
522	if (error)
523		return (DETAILER_ERR_WRITE);
524	return (0);
525}
526
527int
528detailer_checkrcsattr(struct detailer *d, struct coll *coll, char *name,
529    struct fattr *server_attr, int attic)
530{
531	struct fattr *client_attr;
532	char *attr, *path;
533	int error;
534
535	/*
536	 * I don't think we can use the status file, since it only records file
537	 * attributes in cvsmode.
538	 */
539	client_attr = NULL;
540	path = cvspath(coll->co_prefix, name, attic);
541	if (path == NULL) {
542		return (DETAILER_ERR_PROTO);
543	}
544
545	if (access(path, F_OK) == 0 &&
546	    ((client_attr = fattr_frompath(path, FATTR_NOFOLLOW)) != NULL) &&
547	    fattr_equal(client_attr, server_attr)) {
548		attr = fattr_encode(client_attr, NULL, 0);
549		if (attic) {
550			error = proto_printf(d->wr, "l %s %s\n", name, attr);
551		} else {
552			error = proto_printf(d->wr, "L %s %s\n", name, attr);
553		}
554		free(attr);
555		free(path);
556		fattr_free(client_attr);
557		if (error)
558			return (DETAILER_ERR_WRITE);
559		return (0);
560	}
561	/* We don't have it, so tell the server to send it. */
562	error = detailer_send_details(d, coll, name, path, client_attr);
563	fattr_free(client_attr);
564	free(path);
565	return (error);
566}
567
568int
569detailer_send_details(struct detailer *d, struct coll *coll, char *name,
570    char *path, struct fattr *fa)
571{
572	int error;
573	size_t len;
574
575       /*
576        * Try to check if the file exists either live or dead to see if we can
577        * edit it and put it live or dead, rather than receiving the entire
578        * file.
579	*/
580	if (fa == NULL) {
581		path = atticpath(coll->co_prefix, name);
582		fa = fattr_frompath(path, FATTR_NOFOLLOW);
583	}
584	if (fa == NULL) {
585		error = proto_printf(d->wr, "A %s\n", name);
586		if (error)
587			return (DETAILER_ERR_WRITE);
588	} else if (fattr_type(fa) == FT_FILE) {
589		if (isrcs(name, &len) && !(coll->co_options & CO_NORCS)) {
590			detailer_dofile_rcs(d, coll, name, path);
591		} else if (!(coll->co_options & CO_NORSYNC) &&
592		    !globtree_test(coll->co_norsync, name)) {
593			detailer_dofile_rsync(d, name, path);
594		} else {
595			detailer_dofile_regular(d, name, path);
596		}
597	} else {
598		error = proto_printf(d->wr, "N %s\n", name);
599		if (error)
600			return (DETAILER_ERR_WRITE);
601	}
602	return (0);
603}
604