1 /*-
2  * Copyright (c) 2003-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/socket.h>
32 #include <sys/stat.h>
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <limits.h>
40 #include <pthread.h>
41 #include <string.h>
42 #include <strings.h>
43 #include <unistd.h>
44 
45 #include "compat_stdbool.h"
46 #include "compat_stdint.h"
47 #include "compat_stdio.h"
48 #include "compat_stdlib.h"
49 #include "compat_inttypes.h"
50 #include "compat_limits.h"
51 #include "compat_strings.h"
52 
53 #include "attribute.h"
54 #include "collection.h"
55 #include "cvsync.h"
56 #include "hash.h"
57 #include "logmsg.h"
58 #include "network.h"
59 #include "token.h"
60 
61 #include "config_common.h"
62 #include "defs.h"
63 
64 /* errormode */
65 static const struct token_keyword errormode_keywords[] = {
66 	{ "abort",	5,	CVSYNC_ERRORMODE_ABORT },
67 	{ "fixup",	5,	CVSYNC_ERRORMODE_FIXUP },
68 	{ "ignore",	6,	CVSYNC_ERRORMODE_IGNORE },
69 	{ NULL,		0,	CVSYNC_ERRORMODE_UNSPEC },
70 };
71 
72 struct config_args *
config_open(const char * cfname)73 config_open(const char *cfname)
74 {
75 	struct config_args *ca;
76 	struct stat st;
77 	int fd;
78 
79 	if ((ca = malloc(sizeof(*ca))) == NULL) {
80 		logmsg_err("%s: %s", cfname, strerror(errno));
81 		return (NULL);
82 	}
83 
84 	if ((fd = open(cfname, O_RDONLY, 0)) == -1) {
85 		logmsg_err("%s: %s", cfname, strerror(errno));
86 		free(ca);
87 		return (NULL);
88 	}
89 	if (fstat(fd, &st) == -1) {
90 		logmsg_err("%s: %s", cfname, strerror(errno));
91 		(void)close(fd);
92 		free(ca);
93 		return (NULL);
94 	}
95 	ca->ca_mtime = st.st_mtime;
96 	if ((ca->ca_fp = fdopen(fd, "r")) == NULL) {
97 		logmsg_err("%s: %s", cfname, strerror(errno));
98 		(void)close(fd);
99 		free(ca);
100 		return (NULL);
101 	}
102 
103 	return (ca);
104 }
105 
106 bool
config_close(struct config_args * ca)107 config_close(struct config_args *ca)
108 {
109 	FILE *fp = ca->ca_fp;
110 
111 	free(ca);
112 
113 	if (fclose(fp) == EOF) {
114 		logmsg_err("%s", strerror(errno));
115 		return (false);
116 	}
117 
118 	return (true);
119 }
120 
121 bool
config_insert_collection(struct config * cf,struct collection * cl)122 config_insert_collection(struct config *cf, struct collection *cl)
123 {
124 	struct collection *prev = cf->cf_collections;
125 
126 	if (prev == NULL) {
127 		cf->cf_collections = cl;
128 		return (true);
129 	}
130 
131 	for (;;) {
132 		if ((strcasecmp(prev->cl_name, cl->cl_name) == 0) &&
133 		    (strcasecmp(prev->cl_release, cl->cl_release) == 0)) {
134 			logmsg_err("collection %s/%s: duplicate", cl->cl_name,
135 				   cl->cl_release);
136 			return (false);
137 		}
138 		if (prev->cl_next == NULL) {
139 			prev->cl_next = cl;
140 			break;
141 		}
142 		prev = prev->cl_next;
143 	}
144 
145 	return (true);
146 }
147 
148 bool
config_parse_base(struct config_args * ca,struct config * cf)149 config_parse_base(struct config_args *ca, struct config *cf)
150 {
151 	struct stat st;
152 
153 	ca->ca_buffer = cf->cf_base;
154 	ca->ca_bufsize = sizeof(cf->cf_base);
155 
156 	if (!config_set_string(ca))
157 		return (false);
158 
159 	if (cf->cf_base[0] != '/') {
160 		logmsg_err("line %u: base %s: must be the absolute path",
161 			   lineno, cf->cf_base);
162 		return (false);
163 	}
164 
165 	if (stat(cf->cf_base, &st) == -1) {
166 		logmsg_err("line %u: base %s: %s", lineno, cf->cf_base,
167 			   strerror(errno));
168 		return (false);
169 	}
170 	if (!S_ISDIR(st.st_mode)) {
171 		logmsg_err("line %u: base %s: %s", lineno, cf->cf_base,
172 			   strerror(ENOTDIR));
173 		return (false);
174 	}
175 
176 	return (true);
177 }
178 
179 bool
config_parse_base_prefix(struct config_args * ca,struct config * cf)180 config_parse_base_prefix(struct config_args *ca, struct config *cf)
181 {
182 	struct stat st;
183 
184 	ca->ca_buffer = cf->cf_base_prefix;
185 	ca->ca_bufsize = sizeof(cf->cf_base_prefix);
186 
187 	if (!config_set_string(ca))
188 		return (false);
189 
190 	if (cf->cf_base_prefix[0] != '/') {
191 		logmsg_err("line %u: base-prefix %s: must be the absolute "
192 			   "path", lineno, cf->cf_base_prefix);
193 		return (false);
194 	}
195 
196 	if (stat(cf->cf_base_prefix, &st) == -1) {
197 		logmsg_err("line %u: base-prefix %s: %s", lineno,
198 			   cf->cf_base_prefix, strerror(errno));
199 		return (false);
200 	}
201 	if (!S_ISDIR(st.st_mode)) {
202 		logmsg_err("line %u: base-prefix %s: %s", lineno,
203 			   cf->cf_base_prefix, strerror(ENOTDIR));
204 		return (false);
205 	}
206 
207 	return (true);
208 }
209 
210 bool
config_parse_errormode(struct config_args * ca,struct collection * cl)211 config_parse_errormode(struct config_args *ca, struct collection *cl)
212 {
213 	const struct token_keyword *key;
214 
215 	if (cl->cl_errormode != CVSYNC_ERRORMODE_UNSPEC) {
216 		logmsg_err("line %u: found duplication of the 'errormode'",
217 			   lineno);
218 		return (false);
219 	}
220 
221 	if ((key = token_get_keyword(ca->ca_fp, errormode_keywords)) == NULL)
222 		return (false);
223 
224 	cl->cl_errormode = key->type;
225 
226 	return (true);
227 }
228 
229 bool
config_parse_hash(struct config_args * ca,struct config * cf)230 config_parse_hash(struct config_args *ca, struct config *cf)
231 {
232 	struct token *tk = &ca->ca_token;
233 
234 	if (cf->cf_hash != HASH_UNSPEC) {
235 		logmsg_err("line %u: found duplication of the 'hash'", lineno);
236 		return (false);
237 	}
238 
239 	if (!token_get_string(ca->ca_fp, tk))
240 		return (false);
241 
242 	if ((cf->cf_hash = hash_pton(tk->token, tk->length)) == HASH_UNSPEC) {
243 		logmsg_err("line %u: %s: unsupported hash type", lineno,
244 			   tk->token);
245 		return (false);
246 	}
247 
248 	return (true);
249 }
250 
251 bool
config_parse_release(struct config_args * ca,struct collection * cl)252 config_parse_release(struct config_args *ca, struct collection *cl)
253 {
254 	ca->ca_buffer = cl->cl_release;
255 	ca->ca_bufsize = sizeof(cl->cl_release);
256 
257 	if (!config_set_string(ca))
258 		return (false);
259 
260 	if (cvsync_release_pton(cl->cl_release) == CVSYNC_RELEASE_UNKNOWN) {
261 		logmsg_err("line %u: %s: unsupported release type", lineno,
262 			   cl->cl_release);
263 		return (false);
264 	}
265 
266 	return (true);
267 }
268 
269 bool
config_parse_umask(struct config_args * ca,struct collection * cl)270 config_parse_umask(struct config_args *ca, struct collection *cl)
271 {
272 	unsigned long ul;
273 
274 	if (cl->cl_umask != CVSYNC_UMASK_UNSPEC) {
275 		logmsg_err("line %u: found duplication of the 'umask'",
276 			   lineno);
277 		return (false);
278 	}
279 
280 	if (!token_get_number(ca->ca_fp, &ul))
281 		return (false);
282 
283 	if (ul & ~CVSYNC_ALLPERMS) {
284 		logmsg_err("line %u: umask %lu: %s", lineno, ul,
285 			   strerror(ERANGE));
286 		return (false);
287 	}
288 
289 	cl->cl_umask = (uint16_t)ul;
290 
291 	return (true);
292 }
293 
294 bool
config_resolv_prefix(struct config * cf,struct collection * cl,bool rdonly)295 config_resolv_prefix(struct config *cf, struct collection *cl, bool rdonly)
296 {
297 	struct stat st;
298 	char path[PATH_MAX];
299 	int fd, wn;
300 
301 	if (cl->cl_prefix[0] != '/') {
302 		if (strlen(cf->cf_base_prefix) == 0) {
303 			if (strlen(cl->cl_prefix) == 0) {
304 				logmsg_err("collection %s/%s: no prefix",
305 					   cl->cl_name, cl->cl_release);
306 			} else {
307 				logmsg_err("collection %s/%s: prefix %s: "
308 					   "must be the absolute path",
309 					   cl->cl_name, cl->cl_release,
310 					   cl->cl_prefix);
311 			}
312 			return (false);
313 		}
314 		wn = snprintf(path, sizeof(path), "%s/%s", cf->cf_base_prefix,
315 			      cl->cl_prefix);
316 	} else {
317 		wn = snprintf(path, sizeof(path), "%s", cl->cl_prefix);
318 	}
319 	if ((wn <= 0) || ((size_t)wn >= sizeof(path))) {
320 		logmsg_err("collection %s/%s: prefix %s: %s", cl->cl_name,
321 			   cl->cl_release, cl->cl_prefix, strerror(EINVAL));
322 		return (false);
323 	}
324 
325 	if (stat(path, &st) == -1) {
326 		logmsg_err("collection %s/%s: prefix %s: %s", cl->cl_name,
327 			   cl->cl_release, cl->cl_prefix, strerror(errno));
328 		return (false);
329 	}
330 	if (!S_ISDIR(st.st_mode)) {
331 		logmsg_err("collection %s/%s: prefix %s: %s", cl->cl_name,
332 			   cl->cl_release, cl->cl_prefix, strerror(ENOTDIR));
333 		return (false);
334 	}
335 	if (realpath(path, cl->cl_prefix) == NULL) {
336 		logmsg_err("collection %s/%s: prefix %s: %s", cl->cl_name,
337 			   cl->cl_release, cl->cl_prefix, strerror(errno));
338 		return (false);
339 	}
340 	cl->cl_prefixlen = strlen(cl->cl_prefix);
341 	if (cl->cl_prefixlen + 1 >= sizeof(cl->cl_prefix)) {
342 		logmsg_err("collection %s/%s: prefix %s: %s", cl->cl_name,
343 			   cl->cl_release, cl->cl_prefix,
344 			   strerror(ENAMETOOLONG));
345 		return (false);
346 	}
347 	cl->cl_prefix[cl->cl_prefixlen++] = '/';
348 	cl->cl_prefix[cl->cl_prefixlen] = '\0';
349 
350 	if (!rdonly) {
351 		(void)memcpy(&cl->cl_prefix[cl->cl_prefixlen], CVSYNC_TMPFILE,
352 			     CVSYNC_TMPFILE_LEN);
353 		cl->cl_prefix[cl->cl_prefixlen + CVSYNC_TMPFILE_LEN] = '\0';
354 
355 		if ((fd = mkstemp(cl->cl_prefix)) == -1) {
356 			cl->cl_prefix[cl->cl_prefixlen] = '\0';
357 			logmsg_err("collection %s/%s: prefix %s: %s",
358 				   cl->cl_name, cl->cl_release, cl->cl_prefix,
359 				   strerror(errno));
360 			return (false);
361 		}
362 		if (unlink(cl->cl_prefix) == -1) {
363 			cl->cl_prefix[cl->cl_prefixlen] = '\0';
364 			logmsg_err("collection %s/%s: prefix %s: %s",
365 				   cl->cl_name, cl->cl_release, cl->cl_prefix,
366 				   strerror(errno));
367 			(void)close(fd);
368 			return (false);
369 		}
370 		if (close(fd) == -1) {
371 			cl->cl_prefix[cl->cl_prefixlen] = '\0';
372 			logmsg_err("collection %s/%s: prefix %s: %s",
373 				   cl->cl_name, cl->cl_release, cl->cl_prefix,
374 				   strerror(errno));
375 			return (false);
376 		}
377 
378 		cl->cl_prefix[cl->cl_prefixlen] = '\0';
379 	}
380 
381 	return (true);
382 }
383 
384 bool
config_resolv_scanfile(struct config * cf,struct collection * cl)385 config_resolv_scanfile(struct config *cf, struct collection *cl)
386 {
387 	char path[PATH_MAX + CVSYNC_NAME_MAX + 1];
388 	int wn;
389 
390 	if (strlen(cl->cl_scan_name) == 0)
391 		return (true);
392 
393 	if (cl->cl_scan_name[0] != '/') {
394 		if (strlen(cf->cf_base) == 0) {
395 			logmsg_err("collection %s/%s: scanfile %s: must be an "
396 				   "absolute path", cl->cl_name,
397 				   cl->cl_release, cl->cl_scan_name);
398 			return (false);
399 		}
400 		wn = snprintf(path, sizeof(path), "%s/%s", cf->cf_base,
401 			      cl->cl_scan_name);
402 	} else {
403 		wn = snprintf(path, sizeof(path), "%s", cl->cl_scan_name);
404 	}
405 	if ((wn <= 0) || ((size_t)wn >= sizeof(path))) {
406 		logmsg_err("collection %s/%s: scanfile %s: %s", cl->cl_name,
407 			   cl->cl_release, cl->cl_scan_name, strerror(EINVAL));
408 		return (false);
409 	}
410 	wn = snprintf(cl->cl_scan_name, sizeof(cl->cl_scan_name), "%s", path);
411 	if ((wn <= 0) || ((size_t)wn >= sizeof(cl->cl_scan_name))) {
412 		logmsg_err("collection %s/%s: scanfile %s: %s", cl->cl_name,
413 			   cl->cl_release, path, strerror(EINVAL));
414 		return (false);
415 	}
416 
417 	return (true);
418 }
419 
420 bool
config_set_string(struct config_args * ca)421 config_set_string(struct config_args *ca)
422 {
423 	struct token *tk = &ca->ca_token;
424 
425 	if (!token_get_string(ca->ca_fp, tk))
426 		return (false);
427 
428 	if (strlen(ca->ca_buffer) != 0) {
429 		logmsg_err("line %u: found duplication of the '%s'",
430 			   lineno, ca->ca_key->name);
431 		return (false);
432 	}
433 	if (tk->length >= ca->ca_bufsize) {
434 		logmsg_err("line %u: %s: %s", lineno, ca->ca_key->name,
435 			   strerror(ENAMETOOLONG));
436 		return (false);
437 	}
438 	(void)memcpy(ca->ca_buffer, tk->token, tk->length);
439 	ca->ca_buffer[tk->length] = '\0';
440 
441 	return (true);
442 }
443