1 /*-
2  * Copyright (c) 2000-2005 MAEKAWA Masahide <maekawa@cvsync.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  * 3. Neither the name of the author nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 
33 #include <stdlib.h>
34 
35 #include <errno.h>
36 #include <limits.h>
37 #include <pthread.h>
38 #include <string.h>
39 #include <strings.h>
40 
41 #include "compat_stdbool.h"
42 #include "compat_stdint.h"
43 #include "compat_inttypes.h"
44 #include "compat_limits.h"
45 #include "compat_strings.h"
46 #include "basedef.h"
47 
48 #include "attribute.h"
49 #include "collection.h"
50 #include "cvsync.h"
51 #include "cvsync_attr.h"
52 #include "distfile.h"
53 #include "hash.h"
54 #include "logmsg.h"
55 #include "mux.h"
56 
57 #include "filecmp.h"
58 #include "updater.h"
59 
60 bool filecmp_fetch(struct filecmp_args *);
61 bool filecmp_close(struct filecmp_args *);
62 
63 struct filecmp_args *
filecmp_init(struct mux * mx,struct collection * list,struct collection * cls,const char * hostinfo,uint32_t proto,int type)64 filecmp_init(struct mux *mx, struct collection *list, struct collection *cls,
65 	     const char *hostinfo, uint32_t proto, int type)
66 {
67 	struct filecmp_args *fca;
68 
69 	if ((fca = malloc(sizeof(*fca))) == NULL) {
70 		logmsg_err("%s", strerror(errno));
71 		return (NULL);
72 	}
73 	fca->fca_mux = mx;
74 	fca->fca_hostinfo = hostinfo;
75 	fca->fca_collections_list = list;
76 	fca->fca_collections = cls;
77 	fca->fca_proto = proto;
78 	fca->fca_pathmax = sizeof(fca->fca_path);
79 	fca->fca_namemax = CVSYNC_NAME_MAX;
80 	fca->fca_cmdmax = sizeof(fca->fca_cmd);
81 
82 	if (!hash_set(type, &fca->fca_hash_ops)) {
83 		free(fca);
84 		return (NULL);
85 	}
86 
87 	return (fca);
88 }
89 
90 void
filecmp_destroy(struct filecmp_args * fca)91 filecmp_destroy(struct filecmp_args *fca)
92 {
93 	free(fca);
94 }
95 
96 void *
filecmp(void * arg)97 filecmp(void *arg)
98 {
99 	struct filecmp_args *fca = arg;
100 	struct collection *cl;
101 	int type;
102 
103 	for (cl = fca->fca_collections ; cl != NULL ; cl = cl->cl_next) {
104 		if (!filecmp_fetch(fca)) {
105 			logmsg_err("%s FileCmp: Fetch Error",
106 				   fca->fca_hostinfo);
107 			mux_abort(fca->fca_mux);
108 			return (CVSYNC_THREAD_FAILURE);
109 		}
110 
111 		if (fca->fca_tag != FILECMP_START) {
112 			logmsg_err("%s FileCmp: %02x: Invalid Tag",
113 				   fca->fca_hostinfo, fca->fca_tag);
114 			mux_abort(fca->fca_mux);
115 			return (CVSYNC_THREAD_FAILURE);
116 		}
117 
118 		if ((strcasecmp(cl->cl_name, fca->fca_name) != 0) ||
119 		    (strcasecmp(cl->cl_release, fca->fca_release) != 0)) {
120 			mux_abort(fca->fca_mux);
121 			return (CVSYNC_THREAD_FAILURE);
122 		}
123 
124 		type = cvsync_release_pton(cl->cl_release);
125 		if (type == CVSYNC_RELEASE_UNKNOWN) {
126 			logmsg_err("%s FileCmp: %s: Release Error",
127 				   fca->fca_hostinfo, cl->cl_release);
128 			mux_abort(fca->fca_mux);
129 			return (CVSYNC_THREAD_FAILURE);
130 		}
131 
132 		if (!filecmp_start(fca, cl->cl_name, cl->cl_release)) {
133 			logmsg_err("%s FileCmp: Initializer Error",
134 				   fca->fca_hostinfo);
135 			mux_abort(fca->fca_mux);
136 			return (CVSYNC_THREAD_FAILURE);
137 		}
138 
139 		fca->fca_pathlen = cl->cl_prefixlen;
140 		if (fca->fca_pathlen >= fca->fca_pathmax) {
141 			logmsg_err("%s FileCmp: Initializer Error",
142 				   fca->fca_hostinfo);
143 			mux_abort(fca->fca_mux);
144 			return (CVSYNC_THREAD_FAILURE);
145 		}
146 		(void)memcpy(fca->fca_path, cl->cl_prefix, fca->fca_pathlen);
147 		fca->fca_path[fca->fca_pathlen] = '\0';
148 		fca->fca_rpath = fca->fca_path + fca->fca_pathlen;
149 		fca->fca_rpathlen = fca->fca_pathlen;
150 		fca->fca_collection = cl;
151 		fca->fca_umask = cl->cl_umask;
152 
153 		switch (type) {
154 		case CVSYNC_RELEASE_LIST:
155 			if (!filecmp_list(fca)) {
156 				logmsg_err("%s FileCmp: LIST Error",
157 					   fca->fca_hostinfo);
158 				mux_abort(fca->fca_mux);
159 				return (CVSYNC_THREAD_FAILURE);
160 			}
161 			break;
162 		case CVSYNC_RELEASE_RCS:
163 			if (!filecmp_rcs(fca)) {
164 				logmsg_err("%s FileCmp: RCS Error",
165 					   fca->fca_hostinfo);
166 				mux_abort(fca->fca_mux);
167 				return (CVSYNC_THREAD_FAILURE);
168 			}
169 			break;
170 		default:
171 			logmsg_err("%s FileCmp: Release Error",
172 				   fca->fca_hostinfo);
173 			mux_abort(fca->fca_mux);
174 			return (CVSYNC_THREAD_FAILURE);
175 		}
176 
177 		if (!filecmp_end(fca)) {
178 			logmsg_err("%s FileCmp: Collection Finalizer Error",
179 				   fca->fca_hostinfo);
180 			mux_abort(fca->fca_mux);
181 			return (CVSYNC_THREAD_FAILURE);
182 		}
183 
184 		if (cl->cl_distfile != NULL) {
185 			distfile_close(cl->cl_distfile);
186 			cl->cl_distfile = NULL;
187 		}
188 
189 		logmsg("%s Collection %s/%s (time=%ds)", fca->fca_hostinfo,
190 		       cl->cl_name, cl->cl_release, time(NULL) - cl->cl_tick);
191 	}
192 
193 	if (!filecmp_fetch(fca)) {
194 		logmsg_err("%s FileCmp: Fetch Error", fca->fca_hostinfo);
195 		mux_abort(fca->fca_mux);
196 		return (CVSYNC_THREAD_FAILURE);
197 	}
198 
199 	if (fca->fca_tag != FILECMP_END) {
200 		logmsg_err("%s FileCmp: %02x: Invalid Tag", fca->fca_hostinfo,
201 			   fca->fca_tag);
202 		mux_abort(fca->fca_mux);
203 		return (CVSYNC_THREAD_FAILURE);
204 	}
205 
206 	if (!filecmp_end(fca)) {
207 		logmsg_err("%s FileCmp: Finalizer Error", fca->fca_hostinfo);
208 		mux_abort(fca->fca_mux);
209 		return (CVSYNC_THREAD_FAILURE);
210 	}
211 
212 	if (!filecmp_close(fca)) {
213 		logmsg_err("%s FileCmp: Finalizer Error", fca->fca_hostinfo);
214 		mux_abort(fca->fca_mux);
215 		return (CVSYNC_THREAD_FAILURE);
216 	}
217 
218 	return (CVSYNC_THREAD_SUCCESS);
219 }
220 
221 bool
filecmp_fetch(struct filecmp_args * fca)222 filecmp_fetch(struct filecmp_args *fca)
223 {
224 	uint8_t *cmd = fca->fca_cmd;
225 	size_t len, namelen, relnamelen;
226 
227 	if (!mux_recv(fca->fca_mux, MUX_FILECMP_IN, cmd, 3))
228 		return (false);
229 	len = GetWord(cmd);
230 	if ((len == 0) || (len > fca->fca_cmdmax - 2))
231 		return (false);
232 	fca->fca_tag = cmd[2];
233 
234 	switch (fca->fca_tag) {
235 	case FILECMP_START:
236 		if (len < 3)
237 			return (false);
238 		if (!mux_recv(fca->fca_mux, MUX_FILECMP_IN, cmd, 2))
239 			return (false);
240 		if ((namelen = cmd[0]) > fca->fca_namemax)
241 			return (false);
242 		if ((relnamelen = cmd[1]) > fca->fca_namemax)
243 			return (false);
244 		if (len != namelen + relnamelen + 3)
245 			return (false);
246 
247 		if (!mux_recv(fca->fca_mux, MUX_FILECMP_IN, fca->fca_name,
248 			      namelen)) {
249 			return (false);
250 		}
251 		fca->fca_name[namelen] = '\0';
252 
253 		if (!mux_recv(fca->fca_mux, MUX_FILECMP_IN, fca->fca_release,
254 			      relnamelen)) {
255 			return (false);
256 		}
257 		fca->fca_release[relnamelen] = '\0';
258 
259 		break;
260 	case FILECMP_END:
261 		if (len != 1)
262 			return (false);
263 		break;
264 	default:
265 		return (false);
266 	}
267 
268 	return (true);
269 }
270 
271 bool
filecmp_start(struct filecmp_args * fca,const char * name,const char * relname)272 filecmp_start(struct filecmp_args *fca, const char *name, const char *relname)
273 {
274 	uint8_t *cmd = fca->fca_cmd;
275 	size_t len, namelen, relnamelen;
276 
277 	if (((namelen = strlen(name)) > fca->fca_namemax) ||
278 	    ((relnamelen = strlen(relname)) > fca->fca_namemax)) {
279 		return (false);
280 	}
281 
282 	if ((len = namelen + relnamelen + 5) > fca->fca_cmdmax)
283 		return (false);
284 
285 	SetWord(cmd, len - 2);
286 	cmd[2] = UPDATER_START;
287 	cmd[3] = namelen;
288 	cmd[4] = relnamelen;
289 	if (!mux_send(fca->fca_mux, MUX_UPDATER, cmd, 5))
290 		return (false);
291 	if (!mux_send(fca->fca_mux, MUX_UPDATER, name, namelen))
292 		return (false);
293 	if (!mux_send(fca->fca_mux, MUX_UPDATER, relname, relnamelen))
294 		return (false);
295 
296 	if (!mux_flush(fca->fca_mux, MUX_UPDATER))
297 		return (false);
298 
299 	return (true);
300 }
301 
302 bool
filecmp_end(struct filecmp_args * fca)303 filecmp_end(struct filecmp_args *fca)
304 {
305 	uint8_t *cmd = fca->fca_cmd;
306 
307 	SetWord(cmd, 1);
308 	cmd[2] = UPDATER_END;
309 
310 	return (mux_send(fca->fca_mux, MUX_UPDATER, cmd, 3));
311 }
312 
313 bool
filecmp_close(struct filecmp_args * fca)314 filecmp_close(struct filecmp_args *fca)
315 {
316 	if (!mux_close_out(fca->fca_mux, MUX_UPDATER))
317 		return (false);
318 	return (mux_close_in(fca->fca_mux, MUX_FILECMP_IN));
319 }
320 
321 int
filecmp_access(struct filecmp_args * fca,struct cvsync_attr * cap)322 filecmp_access(struct filecmp_args *fca, struct cvsync_attr *cap)
323 {
324 	struct distfile_args *da = fca->fca_collection->cl_distfile;
325 	struct stat st;
326 	size_t len;
327 	int err;
328 
329 	if ((da == NULL) || (da->da_size == 0))
330 		return (DISTFILE_ALLOW);
331 
332 	if ((len = fca->fca_pathlen + cap->ca_namelen) >= fca->fca_pathmax)
333 		return (DISTFILE_DENY);
334 	(void)memcpy(&fca->fca_path[fca->fca_pathlen], cap->ca_name,
335 		     cap->ca_namelen);
336 	fca->fca_path[len] = '\0';
337 
338 	if (fca->fca_collection->cl_symfollow)
339 		err = stat(fca->fca_path, &st);
340 	else
341 		err = lstat(fca->fca_path, &st);
342 	if (err == -1) {
343 		if (errno != ENOENT) {
344 			logmsg_err("%s FileCmp: access error: %s",
345 				   fca->fca_hostinfo, strerror(errno));
346 		}
347 		return (DISTFILE_DENY);
348 	}
349 	if (S_ISDIR(st.st_mode)) {
350 		if (distfile_access(da, fca->fca_rpath) == DISTFILE_DENY)
351 			return (DISTFILE_DENY);
352 		fca->fca_path[len] = '/';
353 		if (++len >= fca->fca_pathmax)
354 			return (DISTFILE_DENY);
355 		fca->fca_path[len] = '\0';
356 	}
357 
358 	return (distfile_access(da, fca->fca_rpath));
359 }
360