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 <limits.h>
36 #include <pthread.h>
37 #include <string.h>
38 #include <unistd.h>
39 
40 #include "compat_stdbool.h"
41 #include "compat_stdint.h"
42 #include "compat_inttypes.h"
43 #include "compat_limits.h"
44 #include "compat_unistd.h"
45 #include "basedef.h"
46 
47 #include "attribute.h"
48 #include "collection.h"
49 #include "cvsync.h"
50 #include "cvsync_attr.h"
51 #include "filetypes.h"
52 #include "list.h"
53 #include "mdirent.h"
54 #include "mux.h"
55 
56 #include "dircmp.h"
57 #include "filescan.h"
58 
59 bool dircmp_rcs_fetch(struct dircmp_args *);
60 
61 bool dircmp_rcs_add(struct dircmp_args *, struct mdirent_rcs *);
62 bool dircmp_rcs_add_dir(struct dircmp_args *, struct mdirent_rcs *);
63 bool dircmp_rcs_add_file(struct dircmp_args *, struct mdirent_rcs *);
64 bool dircmp_rcs_add_symlink(struct dircmp_args *, struct mdirent_rcs *);
65 bool dircmp_rcs_remove(struct dircmp_args *);
66 bool dircmp_rcs_remove_dir(struct dircmp_args *);
67 bool dircmp_rcs_remove_file(struct dircmp_args *);
68 bool dircmp_rcs_replace(struct dircmp_args *, struct mdirent_rcs *);
69 bool dircmp_rcs_update(struct dircmp_args *, struct mdirent_rcs *);
70 
71 struct mDIR *dircmp_rcs_opendir(struct dircmp_args *, size_t);
72 
73 bool
dircmp_rcs(struct dircmp_args * dca)74 dircmp_rcs(struct dircmp_args *dca)
75 {
76 	struct mDIR *mdirp;
77 	struct mdirent_rcs *mdp, *entries;
78 	struct cvsync_attr *cap = &dca->dca_attr;
79 	struct list *lp;
80 	int rv;
81 	bool fetched;
82 
83 	if (dca->dca_collection->cl_scanfile != NULL)
84 		return (dircmp_rcs_scanfile(dca));
85 
86 	if ((lp = list_init()) == NULL)
87 		return (false);
88 	list_set_destructor(lp, mclosedir);
89 
90 	if ((mdirp = dircmp_rcs_opendir(dca, dca->dca_pathlen)) == NULL) {
91 		list_destroy(lp);
92 		return (false);
93 	}
94 	mdirp->m_parent_pathlen = dca->dca_pathlen;
95 
96 	fetched = false;
97 
98 	for (;;) {
99 		if (!fetched) {
100 			if (!dircmp_rcs_fetch(dca)) {
101 				mclosedir(mdirp);
102 				list_destroy(lp);
103 				return (false);
104 			}
105 			fetched = true;
106 		}
107 
108 		if (cap->ca_tag == DIRCMP_END)
109 			break;
110 
111 		if (cap->ca_tag == DIRCMP_UP) {
112 			while (mdirp->m_offset < mdirp->m_nentries) {
113 				entries = mdirp->m_entries;
114 				mdp = &entries[mdirp->m_offset++];
115 				if (!dircmp_access(dca, mdp))
116 					continue;
117 				if (!dircmp_rcs_add(dca, mdp)) {
118 					mclosedir(mdirp);
119 					list_destroy(lp);
120 					return (false);
121 				}
122 			}
123 			dca->dca_pathlen = mdirp->m_parent_pathlen;
124 			dca->dca_path[dca->dca_pathlen] = '\0';
125 			mclosedir(mdirp);
126 
127 			if ((mdirp = list_remove_tail(lp)) == NULL) {
128 				list_destroy(lp);
129 				return (false);
130 			}
131 			mdirp->m_offset++;
132 
133 			fetched = false;
134 			continue;
135 		}
136 
137 		if (mdirp->m_offset == mdirp->m_nentries) {
138 			if (!dircmp_rcs_remove(dca)) {
139 				mclosedir(mdirp);
140 				list_destroy(lp);
141 				return (false);
142 			}
143 
144 			fetched = false;
145 			continue;
146 		}
147 
148 		entries = mdirp->m_entries;
149 		mdp = &entries[mdirp->m_offset];
150 		if (!dircmp_access(dca, mdp)) {
151 			mdirp->m_offset++;
152 			continue;
153 		}
154 
155 		if (mdp->md_namelen == cap->ca_namelen) {
156 			rv = memcmp(mdp->md_name, cap->ca_name,
157 				    cap->ca_namelen);
158 		} else {
159 			if (mdp->md_namelen < cap->ca_namelen) {
160 				rv = memcmp(mdp->md_name, cap->ca_name,
161 					    mdp->md_namelen);
162 			} else {
163 				rv = memcmp(mdp->md_name, cap->ca_name,
164 					    cap->ca_namelen);
165 			}
166 			if (rv == 0) {
167 				if (mdp->md_namelen < cap->ca_namelen)
168 					rv = -1;
169 				else
170 					rv = 1;
171 			}
172 		}
173 		if (rv == 0) {
174 			if (!dircmp_rcs_update(dca, mdp)) {
175 				mclosedir(mdirp);
176 				list_destroy(lp);
177 				return (false);
178 			}
179 			if (cap->ca_tag != DIRCMP_DOWN) {
180 				mdirp->m_offset++;
181 			} else {
182 				size_t len;
183 
184 				if (!list_insert_tail(lp, mdirp)) {
185 					mclosedir(mdirp);
186 					list_destroy(lp);
187 					return (false);
188 				}
189 
190 				len = dca->dca_pathlen + mdp->md_namelen + 1;
191 				if (len >= dca->dca_pathmax) {
192 					list_destroy(lp);
193 					return (false);
194 				}
195 				(void)memcpy(&dca->dca_path[dca->dca_pathlen],
196 					     mdp->md_name, mdp->md_namelen);
197 				dca->dca_path[len - 1] = '/';
198 				dca->dca_path[len] = '\0';
199 
200 				mdirp = dircmp_rcs_opendir(dca, len);
201 				if (mdirp == NULL) {
202 					list_destroy(lp);
203 					return (false);
204 				}
205 				mdirp->m_parent_pathlen = dca->dca_pathlen;
206 				dca->dca_pathlen = len;
207 			}
208 			fetched = false;
209 		} else if (rv < 0) {
210 			if (!dircmp_rcs_add(dca, mdp)) {
211 				mclosedir(mdirp);
212 				list_destroy(lp);
213 				return (false);
214 			}
215 			mdirp->m_offset++;
216 		} else { /* rv > 0 */
217 			if (!dircmp_rcs_remove(dca)) {
218 				mclosedir(mdirp);
219 				list_destroy(lp);
220 				return (false);
221 			}
222 			fetched = false;
223 		}
224 	}
225 
226 	for (;;) {
227 		while (mdirp->m_offset < mdirp->m_nentries) {
228 			entries = mdirp->m_entries;
229 			mdp = &entries[mdirp->m_offset++];
230 			if (!dircmp_access(dca, mdp))
231 				continue;
232 
233 			if (!dircmp_rcs_add(dca, mdp)) {
234 				mclosedir(mdirp);
235 				list_destroy(lp);
236 				return (false);
237 			}
238 		}
239 		mclosedir(mdirp);
240 
241 		if (list_isempty(lp))
242 			break;
243 
244 		if ((mdirp = list_remove_tail(lp)) == NULL) {
245 			list_destroy(lp);
246 			return (false);
247 		}
248 	}
249 
250 	list_destroy(lp);
251 
252 	return (true);
253 }
254 
255 bool
dircmp_rcs_fetch(struct dircmp_args * dca)256 dircmp_rcs_fetch(struct dircmp_args *dca)
257 {
258 	struct cvsync_attr *cap = &dca->dca_attr;
259 	uint8_t *cmd = dca->dca_cmd;
260 	size_t len;
261 
262 	if (!mux_recv(dca->dca_mux, MUX_DIRCMP_IN, cmd, 3))
263 		return (false);
264 	len = GetWord(cmd);
265 	if ((len == 0) || (len > dca->dca_cmdmax - 2))
266 		return (false);
267 	if ((cap->ca_tag = cmd[2]) == DIRCMP_END)
268 		return (len == 1);
269 	if (cap->ca_tag == DIRCMP_UP) {
270 		cap->ca_type = FILETYPE_DIR;
271 		return (len == 1);
272 	}
273 	if (len < 2)
274 		return (false);
275 
276 	if (!mux_recv(dca->dca_mux, MUX_DIRCMP_IN, cmd, 1))
277 		return (false);
278 	cap->ca_namelen = cmd[0];
279 
280 	switch (cap->ca_tag) {
281 	case DIRCMP_DOWN:
282 		cap->ca_type = FILETYPE_DIR;
283 		if ((cap->ca_namelen == 0) ||
284 		    (cap->ca_namelen > sizeof(cap->ca_name)) ||
285 		    (cap->ca_namelen != len - RCS_ATTRLEN_DIR - 2)) {
286 			return (false);
287 		}
288 		if (!mux_recv(dca->dca_mux, MUX_DIRCMP_IN, cap->ca_name,
289 			      cap->ca_namelen)) {
290 			return (false);
291 		}
292 		if (!cvsync_rcs_filename((char *)cap->ca_name,
293 					 cap->ca_namelen)) {
294 			return (false);
295 		}
296 		if (!mux_recv(dca->dca_mux, MUX_DIRCMP_IN, cmd,
297 			      RCS_ATTRLEN_DIR)) {
298 			return (false);
299 		}
300 		if (!attr_rcs_decode_dir(cmd, RCS_ATTRLEN_DIR, cap))
301 			return (false);
302 		break;
303 	case DIRCMP_FILE:
304 		cap->ca_type = FILETYPE_FILE;
305 		if ((cap->ca_namelen == 0) ||
306 		    (cap->ca_namelen > sizeof(cap->ca_name)) ||
307 		    (cap->ca_namelen != len - RCS_ATTRLEN_FILE - 2)) {
308 			return (false);
309 		}
310 		if (!mux_recv(dca->dca_mux, MUX_DIRCMP_IN, cap->ca_name,
311 			      cap->ca_namelen)) {
312 			return (false);
313 		}
314 		if (!cvsync_rcs_filename((char *)cap->ca_name,
315 					 cap->ca_namelen)) {
316 			return (false);
317 		}
318 		if (!mux_recv(dca->dca_mux, MUX_DIRCMP_IN, cmd,
319 			      RCS_ATTRLEN_FILE)) {
320 			return (false);
321 		}
322 		if (!attr_rcs_decode_file(cmd, RCS_ATTRLEN_FILE, cap))
323 			return (false);
324 		break;
325 	case DIRCMP_RCS:
326 	case DIRCMP_RCS_ATTIC:
327 		if (cap->ca_tag == DIRCMP_RCS)
328 			cap->ca_type = FILETYPE_RCS;
329 		else
330 			cap->ca_type = FILETYPE_RCS_ATTIC;
331 		if ((cap->ca_namelen == 0) ||
332 		    (cap->ca_namelen > sizeof(cap->ca_name)) ||
333 		    (cap->ca_namelen != len - RCS_ATTRLEN_RCS - 2)) {
334 			return (false);
335 		}
336 		if (!mux_recv(dca->dca_mux, MUX_DIRCMP_IN, cap->ca_name,
337 			      cap->ca_namelen)) {
338 			return (false);
339 		}
340 		if (!cvsync_rcs_filename((char *)cap->ca_name,
341 					 cap->ca_namelen)) {
342 			return (false);
343 		}
344 		if (!mux_recv(dca->dca_mux, MUX_DIRCMP_IN, cmd,
345 			      RCS_ATTRLEN_RCS)) {
346 			return (false);
347 		}
348 		if (!attr_rcs_decode_rcs(cmd, RCS_ATTRLEN_RCS, cap))
349 			return (false);
350 		break;
351 	case DIRCMP_SYMLINK:
352 		cap->ca_type = FILETYPE_SYMLINK;
353 		if ((cap->ca_namelen == 0) ||
354 		    (cap->ca_namelen > sizeof(cap->ca_name))) {
355 			return (false);
356 		}
357 		if (len <= cap->ca_namelen + 2)
358 			return (false);
359 		if (!mux_recv(dca->dca_mux, MUX_DIRCMP_IN, cap->ca_name,
360 			      cap->ca_namelen)) {
361 			return (false);
362 		}
363 		if (!cvsync_rcs_filename((char *)cap->ca_name,
364 					 cap->ca_namelen)) {
365 			return (false);
366 		}
367 		cap->ca_auxlen = len - cap->ca_namelen - 2;
368 		if (cap->ca_auxlen > sizeof(cap->ca_aux))
369 			return (false);
370 		if (!mux_recv(dca->dca_mux, MUX_DIRCMP_IN, cap->ca_aux,
371 			      cap->ca_auxlen)) {
372 			return (false);
373 		}
374 		break;
375 	default:
376 		return (false);
377 	}
378 
379 	return (true);
380 }
381 
382 bool
dircmp_rcs_add(struct dircmp_args * dca,struct mdirent_rcs * mdp)383 dircmp_rcs_add(struct dircmp_args *dca, struct mdirent_rcs *mdp)
384 {
385 	struct mDIR *mdirp;
386 	struct mdirent_rcs *entries;
387 	struct list *lp;
388 	struct stat *st = &mdp->md_stat;
389 	size_t len;
390 
391 	if (S_ISREG(st->st_mode))
392 		return (dircmp_rcs_add_file(dca, mdp));
393 	if (S_ISLNK(st->st_mode))
394 		return (dircmp_rcs_add_symlink(dca, mdp));
395 
396 	if (!S_ISDIR(st->st_mode))
397 		return (false);
398 	if (!dircmp_rcs_add_dir(dca, mdp))
399 		return (false);
400 
401 	if ((lp = list_init()) == NULL)
402 		return (false);
403 	list_set_destructor(lp, mclosedir);
404 
405 	len = dca->dca_pathlen + mdp->md_namelen + 1;
406 	if (len >= dca->dca_pathmax) {
407 		list_destroy(lp);
408 		return (false);
409 	}
410 
411 	(void)memcpy(&dca->dca_path[dca->dca_pathlen], mdp->md_name,
412 		     mdp->md_namelen);
413 	dca->dca_path[len - 1] = '/';
414 	dca->dca_path[len] = '\0';
415 
416 	if ((mdirp = dircmp_rcs_opendir(dca, len)) == NULL) {
417 		list_destroy(lp);
418 		return (false);
419 	}
420 	mdirp->m_parent_pathlen = dca->dca_pathlen;
421 	dca->dca_pathlen = len;
422 
423 	if (!list_insert_tail(lp, mdirp)) {
424 		mclosedir(mdirp);
425 		list_destroy(lp);
426 		return (false);
427 	}
428 
429 	do {
430 		if ((mdirp = list_remove_tail(lp)) == NULL) {
431 			list_destroy(lp);
432 			return (false);
433 		}
434 
435 		while (mdirp->m_offset < mdirp->m_nentries) {
436 			entries = mdirp->m_entries;
437 			mdp = &entries[mdirp->m_offset++];
438 			if (!dircmp_access(dca, mdp))
439 				continue;
440 
441 			switch (mdp->md_stat.st_mode & S_IFMT) {
442 			case S_IFDIR:
443 				if (!dircmp_rcs_add_dir(dca, mdp)) {
444 					mclosedir(mdirp);
445 					list_destroy(lp);
446 					return (false);
447 				}
448 
449 				len = dca->dca_pathlen + mdp->md_namelen + 1;
450 				if (len >= dca->dca_pathmax) {
451 					mclosedir(mdirp);
452 					list_destroy(lp);
453 					return (false);
454 				}
455 				(void)memcpy(&dca->dca_path[dca->dca_pathlen],
456 					     mdp->md_name, mdp->md_namelen);
457 				dca->dca_path[len - 1] = '/';
458 				dca->dca_path[len] = '\0';
459 
460 				if (!list_insert_tail(lp, mdirp)) {
461 					mclosedir(mdirp);
462 					list_destroy(lp);
463 					return (false);
464 				}
465 
466 				mdirp = dircmp_rcs_opendir(dca, len);
467 				if (mdirp == NULL) {
468 					list_destroy(lp);
469 					return (false);
470 				}
471 				mdirp->m_parent_pathlen = dca->dca_pathlen;
472 				dca->dca_pathlen = len;
473 
474 				break;
475 			case S_IFREG:
476 				if (!dircmp_rcs_add_file(dca, mdp)) {
477 					mclosedir(mdirp);
478 					list_destroy(lp);
479 					return (false);
480 				}
481 				break;
482 			case S_IFLNK:
483 				if (!dircmp_rcs_add_symlink(dca, mdp)) {
484 					mclosedir(mdirp);
485 					list_destroy(lp);
486 					return (false);
487 				}
488 				break;
489 			default:
490 				mclosedir(mdirp);
491 				list_destroy(lp);
492 				return (false);
493 			}
494 		}
495 
496 		dca->dca_pathlen = mdirp->m_parent_pathlen;
497 		dca->dca_path[dca->dca_pathlen] = '\0';
498 
499 		mclosedir(mdirp);
500 	} while (!list_isempty(lp));
501 
502 	list_destroy(lp);
503 
504 	return (true);
505 }
506 
507 bool
dircmp_rcs_add_dir(struct dircmp_args * dca,struct mdirent_rcs * mdp)508 dircmp_rcs_add_dir(struct dircmp_args *dca, struct mdirent_rcs *mdp)
509 {
510 	uint8_t *cmd = dca->dca_cmd;
511 	size_t len, rlen;
512 
513 	if ((len = dca->dca_pathlen + mdp->md_namelen) >= dca->dca_pathmax)
514 		return (false);
515 	rlen = dca->dca_pathlen - dca->dca_rpathlen;
516 	if ((len = rlen + mdp->md_namelen + 6) > dca->dca_cmdmax)
517 		return (false);
518 
519 	SetWord(cmd, len - 2);
520 	cmd[2] = FILESCAN_ADD;
521 	cmd[3] = FILETYPE_DIR;
522 	SetWord(&cmd[4], rlen + mdp->md_namelen);
523 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, cmd, 6))
524 		return (false);
525 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, dca->dca_rpath, rlen))
526 		return (false);
527 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, mdp->md_name,
528 		      mdp->md_namelen)) {
529 		return (false);
530 	}
531 
532 	return (true);
533 }
534 
535 bool
dircmp_rcs_add_file(struct dircmp_args * dca,struct mdirent_rcs * mdp)536 dircmp_rcs_add_file(struct dircmp_args *dca, struct mdirent_rcs *mdp)
537 {
538 	uint8_t *cmd = dca->dca_cmd;
539 	size_t len, rlen;
540 
541 	if ((len = dca->dca_pathlen + mdp->md_namelen) >= dca->dca_pathmax)
542 		return (false);
543 	rlen = dca->dca_pathlen - dca->dca_rpathlen;
544 	if ((len = rlen + mdp->md_namelen + 6) > dca->dca_cmdmax)
545 		return (false);
546 
547 	if (IS_FILE_RCS(mdp->md_name, mdp->md_namelen)) {
548 		if (mdp->md_attic)
549 			cmd[3] = FILETYPE_RCS_ATTIC;
550 		else
551 			cmd[3] = FILETYPE_RCS;
552 	} else {
553 		cmd[3] = FILETYPE_FILE;
554 	}
555 
556 	SetWord(cmd, len - 2);
557 	cmd[2] = FILESCAN_ADD;
558 	SetWord(&cmd[4], rlen + mdp->md_namelen);
559 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, cmd, 6))
560 		return (false);
561 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, dca->dca_rpath, rlen))
562 		return (false);
563 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, mdp->md_name,
564 		      mdp->md_namelen)) {
565 		return (false);
566 	}
567 
568 	return (true);
569 }
570 
571 bool
dircmp_rcs_add_symlink(struct dircmp_args * dca,struct mdirent_rcs * mdp)572 dircmp_rcs_add_symlink(struct dircmp_args *dca, struct mdirent_rcs *mdp)
573 {
574 	uint8_t *cmd = dca->dca_cmd;
575 	size_t len, rlen;
576 
577 	if ((len = dca->dca_pathlen + mdp->md_namelen) >= dca->dca_pathmax)
578 		return (false);
579 	rlen = dca->dca_pathlen - dca->dca_rpathlen;
580 	if ((len = rlen + mdp->md_namelen + 6) > dca->dca_cmdmax)
581 		return (false);
582 
583 	SetWord(cmd, len - 2);
584 	cmd[2] = FILESCAN_ADD;
585 	cmd[3] = FILETYPE_SYMLINK;
586 	SetWord(&cmd[4], rlen + mdp->md_namelen);
587 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, cmd, 6))
588 		return (false);
589 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, dca->dca_rpath, rlen))
590 		return (false);
591 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, mdp->md_name,
592 		      mdp->md_namelen)) {
593 		return (false);
594 	}
595 
596 	return (true);
597 }
598 
599 bool
dircmp_rcs_remove(struct dircmp_args * dca)600 dircmp_rcs_remove(struct dircmp_args *dca)
601 {
602 	struct cvsync_attr *cap = &dca->dca_attr;
603 	size_t len, *plen_stack, c, n;
604 
605 	if (cap->ca_type != FILETYPE_DIR)
606 		return (dircmp_rcs_remove_file(dca));
607 
608 	n = 4;
609 	if ((plen_stack = malloc(n * sizeof(*plen_stack))) == NULL)
610 		return (false);
611 	plen_stack[0] = dca->dca_pathlen;
612 	c = 1;
613 
614 	len = dca->dca_pathlen + cap->ca_namelen + 1;
615 	if (len >= dca->dca_pathmax) {
616 		free(plen_stack);
617 		return (false);
618 	}
619 
620 	(void)memcpy(&dca->dca_path[dca->dca_pathlen], cap->ca_name,
621 		     cap->ca_namelen);
622 	dca->dca_path[len - 1] = '/';
623 	dca->dca_path[len] = '\0';
624 	dca->dca_pathlen = len;
625 
626 	do {
627 		if (!dircmp_rcs_fetch(dca)) {
628 			free(plen_stack);
629 			return (false);
630 		}
631 
632 		switch (cap->ca_tag) {
633 		case DIRCMP_END:
634 			free(plen_stack);
635 			return (false);
636 		case DIRCMP_DOWN:
637 			if (c == n) {
638 				size_t *newp, old = n, new = old * 2;
639 
640 				newp = malloc(new * sizeof(*newp));
641 				if (newp == NULL) {
642 					free(plen_stack);
643 					return (false);
644 				}
645 				(void)memcpy(newp, plen_stack,
646 					     old * sizeof(*newp));
647 				(void)memset(&newp[old], 0,
648 					     (new - old) * sizeof(*newp));
649 
650 				free(plen_stack);
651 				plen_stack = newp;
652 				n = new;
653 			}
654 			plen_stack[c++] = dca->dca_pathlen;
655 
656 			len = dca->dca_pathlen + cap->ca_namelen + 1;
657 			if (len >= dca->dca_pathmax) {
658 				free(plen_stack);
659 				return (false);
660 			}
661 
662 			(void)memcpy(&dca->dca_path[dca->dca_pathlen],
663 				     cap->ca_name, cap->ca_namelen);
664 			dca->dca_path[len - 1] = '/';
665 			dca->dca_path[len] = '\0';
666 			dca->dca_pathlen = len;
667 
668 			break;
669 		case DIRCMP_UP:
670 			dca->dca_path[dca->dca_pathlen - 1] = '\0';
671 
672 			if (!dircmp_rcs_remove_dir(dca)) {
673 				free(plen_stack);
674 				return (false);
675 			}
676 
677 			dca->dca_pathlen = plen_stack[--c];
678 			dca->dca_path[dca->dca_pathlen] = '\0';
679 
680 			break;
681 		case DIRCMP_FILE:
682 		case DIRCMP_RCS:
683 		case DIRCMP_RCS_ATTIC:
684 		case DIRCMP_SYMLINK:
685 			if (!dircmp_rcs_remove_file(dca)) {
686 				free(plen_stack);
687 				return (false);
688 			}
689 			break;
690 		default:
691 			free(plen_stack);
692 			return (false);
693 		}
694 	} while (c > 0);
695 
696 	free(plen_stack);
697 
698 	return (true);
699 }
700 
701 bool
dircmp_rcs_remove_dir(struct dircmp_args * dca)702 dircmp_rcs_remove_dir(struct dircmp_args *dca)
703 {
704 	uint8_t *cmd = dca->dca_cmd;
705 	size_t len, rlen;
706 
707 	rlen = dca->dca_pathlen - dca->dca_rpathlen - 1;
708 	if ((len = rlen + 6) > dca->dca_cmdmax)
709 		return (false);
710 
711 	SetWord(cmd, len - 2);
712 	cmd[2] = FILESCAN_REMOVE;
713 	cmd[3] = FILETYPE_DIR;
714 	SetWord(&cmd[4], rlen);
715 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, cmd, 6))
716 		return (false);
717 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, dca->dca_rpath, rlen))
718 		return (false);
719 
720 	return (true);
721 }
722 
723 bool
dircmp_rcs_remove_file(struct dircmp_args * dca)724 dircmp_rcs_remove_file(struct dircmp_args *dca)
725 {
726 	struct cvsync_attr *cap = &dca->dca_attr;
727 	uint8_t *cmd = dca->dca_cmd;
728 	size_t len, rlen;
729 
730 	if ((len = dca->dca_pathlen + cap->ca_namelen) >= dca->dca_pathmax)
731 		return (false);
732 	rlen = dca->dca_pathlen - dca->dca_rpathlen;
733 	if ((len = rlen + cap->ca_namelen + 6) > dca->dca_cmdmax)
734 		return (false);
735 
736 	SetWord(cmd, len - 2);
737 	cmd[2] = FILESCAN_REMOVE;
738 	cmd[3] = cap->ca_type;
739 	SetWord(&cmd[4], rlen + cap->ca_namelen);
740 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, cmd, 6))
741 		return (false);
742 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, dca->dca_rpath, rlen))
743 		return (false);
744 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, cap->ca_name,
745 		      cap->ca_namelen)) {
746 		return (false);
747 	}
748 
749 	return (true);
750 }
751 
752 bool
dircmp_rcs_replace(struct dircmp_args * dca,struct mdirent_rcs * mdp)753 dircmp_rcs_replace(struct dircmp_args *dca, struct mdirent_rcs *mdp)
754 {
755 	switch (mdp->md_stat.st_mode & S_IFMT) {
756 	case S_IFDIR:
757 	case S_IFREG:
758 	case S_IFLNK:
759 		if (!dircmp_rcs_remove(dca))
760 			return (false);
761 		if (!dircmp_rcs_add(dca, mdp))
762 			return (false);
763 		break;
764 	default:
765 		return (false);
766 	}
767 
768 	return (true);
769 }
770 
771 bool
dircmp_rcs_update(struct dircmp_args * dca,struct mdirent_rcs * mdp)772 dircmp_rcs_update(struct dircmp_args *dca, struct mdirent_rcs *mdp)
773 {
774 	struct cvsync_attr *cap = &dca->dca_attr;
775 	struct stat *st = &mdp->md_stat;
776 	uint16_t mode = RCS_MODE(st->st_mode, dca->dca_collection->cl_umask);
777 	uint8_t *cmd = dca->dca_cmd;
778 	size_t base, len, rlen;
779 	int wn;
780 
781 	if ((len = dca->dca_pathlen + mdp->md_namelen) >= dca->dca_pathmax)
782 		return (false);
783 	rlen = dca->dca_pathlen - dca->dca_rpathlen;
784 	if ((base = rlen + mdp->md_namelen + 6) > dca->dca_cmdmax)
785 		return (false);
786 
787 	len = dca->dca_cmdmax - base;
788 
789 	switch (st->st_mode & S_IFMT) {
790 	case S_IFDIR:
791 		if (cap->ca_type != FILETYPE_DIR)
792 			return (dircmp_rcs_replace(dca, mdp));
793 		if (mode == cap->ca_mode)
794 			return (true);
795 		cmd[2] = FILESCAN_SETATTR;
796 		cmd[3] = FILETYPE_DIR;
797 		if ((len = attr_rcs_encode_dir(&cmd[6], len, mode)) == 0)
798 			return (false);
799 		break;
800 	case S_IFREG:
801 		if (IS_FILE_RCS(mdp->md_name, mdp->md_namelen)) {
802 			if ((cap->ca_type != FILETYPE_RCS) &&
803 			    (cap->ca_type != FILETYPE_RCS_ATTIC)) {
804 				return (dircmp_rcs_replace(dca, mdp));
805 			}
806 			if (mdp->md_attic)
807 				cmd[3] = FILETYPE_RCS_ATTIC;
808 			else
809 				cmd[3] = FILETYPE_RCS;
810 			if ((cmd[3] == cap->ca_type) &&
811 			    ((int64_t)st->st_mtime == cap->ca_mtime) &&
812 			    (mode == cap->ca_mode)) {
813 				return (true);
814 			}
815 			if (cap->ca_type != cmd[3]) {
816 				cmd[2] = FILESCAN_RCS_ATTIC;
817 			} else {
818 				if ((int64_t)st->st_mtime != cap->ca_mtime)
819 					cmd[2] = FILESCAN_UPDATE;
820 				else
821 					cmd[2] = FILESCAN_SETATTR;
822 			}
823 			if ((len = attr_rcs_encode_rcs(&cmd[6], len,
824 						       st->st_mtime,
825 						       mode)) == 0) {
826 				return (false);
827 			}
828 		} else {
829 			if (cap->ca_type != FILETYPE_FILE)
830 				return (dircmp_rcs_replace(dca, mdp));
831 			if (((int64_t)st->st_mtime == cap->ca_mtime) &&
832 			    ((uint64_t)st->st_size == cap->ca_size) &&
833 			    (mode == cap->ca_mode)) {
834 				return (true);
835 			}
836 			if (((int64_t)st->st_mtime != cap->ca_mtime) ||
837 			    ((uint64_t)st->st_size != cap->ca_size)) {
838 				if (st->st_size == 0)
839 					return (dircmp_rcs_replace(dca, mdp));
840 
841 				cmd[2] = FILESCAN_UPDATE;
842 			} else {
843 				cmd[2] = FILESCAN_SETATTR;
844 			}
845 			cmd[3] = FILETYPE_FILE;
846 			if ((len = attr_rcs_encode_file(&cmd[6], len,
847 							st->st_mtime,
848 							st->st_size,
849 							mode)) == 0) {
850 				return (false);
851 			}
852 		}
853 		break;
854 	case S_IFLNK:
855 		if (cap->ca_type != FILETYPE_SYMLINK)
856 			return (dircmp_rcs_replace(dca, mdp));
857 		(void)memcpy(&dca->dca_path[dca->dca_pathlen], mdp->md_name,
858 			     mdp->md_namelen);
859 		dca->dca_path[dca->dca_pathlen + mdp->md_namelen] = '\0';
860 		if ((wn = readlink(dca->dca_path, dca->dca_symlink,
861 				   dca->dca_pathmax)) == -1) {
862 			return (false);
863 		}
864 		len = (size_t)wn;
865 		if ((len == cap->ca_auxlen) &&
866 		    (memcmp(cap->ca_aux, dca->dca_symlink, len) == 0)) {
867 			return (true);
868 		}
869 		cmd[2] = FILESCAN_UPDATE;
870 		cmd[3] = FILETYPE_SYMLINK;
871 		len = 0;
872 		break;
873 	default:
874 		return (false);
875 	}
876 
877 	SetWord(cmd, len + base - 2);
878 	SetWord(&cmd[4], rlen + mdp->md_namelen);
879 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, cmd, 6))
880 		return (false);
881 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, dca->dca_rpath, rlen))
882 		return (false);
883 	if (!mux_send(dca->dca_mux, MUX_FILESCAN, mdp->md_name,
884 		      mdp->md_namelen)) {
885 		return (false);
886 	}
887 	if (len > 0) {
888 		if (!mux_send(dca->dca_mux, MUX_FILESCAN, &cmd[6], len))
889 			return (false);
890 	}
891 
892 	return (true);
893 }
894 
895 struct mDIR *
dircmp_rcs_opendir(struct dircmp_args * dca,size_t pathlen)896 dircmp_rcs_opendir(struct dircmp_args *dca, size_t pathlen)
897 {
898 	struct mDIR *mdirp;
899 	struct mdirent_rcs *mdp;
900 	struct mdirent_args mda;
901 	struct collection *cl = dca->dca_collection;
902 	size_t rpathlen = pathlen - (dca->dca_rpath - dca->dca_path), len;
903 	int rv;
904 
905 	mda.mda_errormode = cl->cl_errormode;
906 	mda.mda_symfollow = cl->cl_symfollow;
907 	mda.mda_remove = false;
908 
909 	if (rpathlen >= cl->cl_rprefixlen) {
910 		if ((mdirp = mopendir_rcs(dca->dca_path, pathlen,
911 					  dca->dca_pathmax, &mda)) == NULL) {
912 			return (NULL);
913 		}
914 
915 		return (mdirp);
916 	}
917 
918 	if ((mdp = malloc(sizeof(*mdp))) == NULL)
919 		return (NULL);
920 
921 	for (len = rpathlen ; len < cl->cl_rprefixlen ; len++) {
922 		if (cl->cl_rprefix[len] == '/')
923 			break;
924 	}
925 	if ((len -= rpathlen) >= sizeof(mdp->md_name)) {
926 		free(mdp);
927 		return (NULL);
928 	}
929 	(void)memcpy(mdp->md_name, &cl->cl_rprefix[rpathlen], len);
930 	mdp->md_namelen = len;
931 	mdp->md_attic = false;
932 	mdp->md_dead = false;
933 
934 	len = cl->cl_prefixlen + rpathlen + mdp->md_namelen;
935 	if (len >= dca->dca_pathmax) {
936 		free(mdp);
937 		return (NULL);
938 	}
939 	(void)memcpy(&dca->dca_path[cl->cl_prefixlen], cl->cl_rprefix,
940 		     rpathlen + mdp->md_namelen);
941 	dca->dca_path[len] = '\0';
942 
943 	if (cl->cl_symfollow)
944 		rv = stat(dca->dca_path, &mdp->md_stat);
945 	else
946 		rv = lstat(dca->dca_path, &mdp->md_stat);
947 	if (rv == -1) {
948 		free(mdp);
949 		return (NULL);
950 	}
951 	if (!S_ISDIR(mdp->md_stat.st_mode)) {
952 		free(mdp);
953 		return (NULL);
954 	}
955 
956 	if ((mdirp = malloc(sizeof(*mdirp))) == NULL) {
957 		free(mdp);
958 		return (NULL);
959 	}
960 	mdirp->m_entries = mdp;
961 	mdirp->m_nentries = 1;
962 	mdirp->m_offset = 0;
963 	mdirp->m_parent = NULL;
964 	mdirp->m_parent_pathlen = 0;
965 
966 	return (mdirp);
967 }
968