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 "dirscan.h"
57 #include "dircmp.h"
58 
59 bool dirscan_rcs_down(struct dirscan_args *, struct mdirent_rcs *);
60 bool dirscan_rcs_up(struct dirscan_args *);
61 bool dirscan_rcs_file(struct dirscan_args *, struct mdirent_rcs *);
62 bool dirscan_rcs_symlink(struct dirscan_args *, struct mdirent_rcs *);
63 
64 struct mDIR *dirscan_rcs_opendir(struct dirscan_args *, size_t);
65 
66 bool
dirscan_rcs(struct dirscan_args * dsa)67 dirscan_rcs(struct dirscan_args *dsa)
68 {
69 	struct mDIR *mdirp;
70 	struct mdirent_rcs *mdp = NULL, *entries;
71 	struct collection *cl = dsa->dsa_collection;
72 	struct list *lp;
73 	size_t len;
74 
75 	if (cl->cl_scanfile != NULL)
76 		return (dirscan_rcs_scanfile(dsa));
77 
78 	if ((lp = list_init()) == NULL)
79 		return (false);
80 	list_set_destructor(lp, mclosedir);
81 
82 	if ((mdirp = dirscan_rcs_opendir(dsa, dsa->dsa_pathlen)) == NULL) {
83 		list_destroy(lp);
84 		return (false);
85 	}
86 	mdirp->m_parent_pathlen = dsa->dsa_pathlen;
87 
88 	if (!list_insert_tail(lp, mdirp)) {
89 		mclosedir(mdirp);
90 		list_destroy(lp);
91 		return (false);
92 	}
93 
94 	do {
95 		if (cvsync_isinterrupted()) {
96 			list_destroy(lp);
97 			return (false);
98 		}
99 
100 		if ((mdirp = list_remove_tail(lp)) == NULL) {
101 			list_destroy(lp);
102 			return (false);
103 		}
104 
105 		while (mdirp->m_offset < mdirp->m_nentries) {
106 			entries = mdirp->m_entries;
107 			mdp = &entries[mdirp->m_offset++];
108 			if (mdp->md_dead)
109 				continue;
110 
111 			switch (mdp->md_stat.st_mode & S_IFMT) {
112 			case S_IFDIR:
113 				if (!dirscan_rcs_down(dsa, mdp)) {
114 					mclosedir(mdirp);
115 					list_destroy(lp);
116 					return (false);
117 				}
118 
119 				len = dsa->dsa_pathlen + mdp->md_namelen + 1;
120 				if (len >= dsa->dsa_pathmax) {
121 					mclosedir(mdirp);
122 					list_destroy(lp);
123 					return (false);
124 				}
125 				(void)memcpy(&dsa->dsa_path[dsa->dsa_pathlen],
126 					     mdp->md_name, mdp->md_namelen);
127 				dsa->dsa_path[len - 1] = '/';
128 				dsa->dsa_path[len] = '\0';
129 
130 				if (!list_insert_tail(lp, mdirp)) {
131 					mclosedir(mdirp);
132 					list_destroy(lp);
133 					return (false);
134 				}
135 
136 				mdirp = dirscan_rcs_opendir(dsa, len);
137 				if (mdirp == NULL) {
138 					list_destroy(lp);
139 					return (false);
140 				}
141 				mdirp->m_parent = mdp;
142 				mdirp->m_parent_pathlen = dsa->dsa_pathlen;
143 
144 				dsa->dsa_pathlen = len;
145 
146 				break;
147 			case S_IFREG:
148 				if (!dirscan_rcs_file(dsa, mdp)) {
149 					mclosedir(mdirp);
150 					list_destroy(lp);
151 					return (false);
152 				}
153 				break;
154 			case S_IFLNK:
155 				if (!dirscan_rcs_symlink(dsa, mdp)) {
156 					mclosedir(mdirp);
157 					list_destroy(lp);
158 					return (false);
159 				}
160 				break;
161 			default:
162 				mclosedir(mdirp);
163 				list_destroy(lp);
164 				return (false);
165 			}
166 		}
167 
168 		if (mdirp->m_parent != NULL) {
169 			if (!dirscan_rcs_up(dsa)) {
170 				mclosedir(mdirp);
171 				list_destroy(lp);
172 				return (false);
173 			}
174 		}
175 
176 		dsa->dsa_pathlen = mdirp->m_parent_pathlen;
177 
178 		mclosedir(mdirp);
179 	} while (!list_isempty(lp));
180 
181 	list_destroy(lp);
182 
183 	return (true);
184 }
185 
186 bool
dirscan_rcs_down(struct dirscan_args * dsa,struct mdirent_rcs * mdp)187 dirscan_rcs_down(struct dirscan_args *dsa, struct mdirent_rcs *mdp)
188 {
189 	struct collection *cl = dsa->dsa_collection;
190 	uint16_t mode = RCS_MODE(mdp->md_stat.st_mode, cl->cl_umask);
191 	uint8_t *cmd = dsa->dsa_cmd;
192 	size_t base, len;
193 
194 	if (mdp->md_namelen > dsa->dsa_namemax)
195 		return (false);
196 	if ((base = mdp->md_namelen + 4) > dsa->dsa_cmdmax)
197 		return (false);
198 
199 	cmd[2] = DIRCMP_DOWN;
200 	cmd[3] = mdp->md_namelen;
201 	if ((len = attr_rcs_encode_dir(&cmd[4], dsa->dsa_cmdmax - base,
202 				       mode)) == 0) {
203 		return (false);
204 	}
205 	SetWord(cmd, len + base - 2);
206 	if (!mux_send(dsa->dsa_mux, MUX_DIRCMP, cmd, 4))
207 		return (false);
208 	if (!mux_send(dsa->dsa_mux, MUX_DIRCMP, mdp->md_name, mdp->md_namelen))
209 		return (false);
210 	if (!mux_send(dsa->dsa_mux, MUX_DIRCMP, &cmd[4], len))
211 		return (false);
212 
213 	return (true);
214 }
215 
216 bool
dirscan_rcs_up(struct dirscan_args * dsa)217 dirscan_rcs_up(struct dirscan_args *dsa)
218 {
219 	static const uint8_t _cmd[3] = { 0x00, 0x01, DIRCMP_UP };
220 
221 	if (!mux_send(dsa->dsa_mux, MUX_DIRCMP, _cmd, sizeof(_cmd)))
222 		return (false);
223 
224 	return (true);
225 }
226 
227 bool
dirscan_rcs_file(struct dirscan_args * dsa,struct mdirent_rcs * mdp)228 dirscan_rcs_file(struct dirscan_args *dsa, struct mdirent_rcs *mdp)
229 {
230 	struct stat *st = &mdp->md_stat;
231 	uint16_t mode;
232 	uint8_t *cmd = dsa->dsa_cmd;
233 	size_t base, len;
234 
235 	if (mdp->md_namelen > dsa->dsa_namemax)
236 		return (false);
237 	if ((base = mdp->md_namelen + 4) > dsa->dsa_cmdmax)
238 		return (false);
239 
240 	mode = RCS_MODE(st->st_mode, dsa->dsa_collection->cl_umask);
241 	len = dsa->dsa_cmdmax - base;
242 
243 	if (IS_FILE_RCS(mdp->md_name, mdp->md_namelen)) {
244 		if (mdp->md_attic)
245 			cmd[2] = DIRCMP_RCS_ATTIC;
246 		else
247 			cmd[2] = DIRCMP_RCS;
248 		len = attr_rcs_encode_rcs(&cmd[4], len, st->st_mtime, mode);
249 	} else {
250 		cmd[2] = DIRCMP_FILE;
251 		len = attr_rcs_encode_file(&cmd[4], len, st->st_mtime,
252 					   st->st_size, mode);
253 	}
254 	if (len == 0)
255 		return (false);
256 
257 	SetWord(cmd, len + base - 2);
258 	cmd[3] = mdp->md_namelen;
259 	if (!mux_send(dsa->dsa_mux, MUX_DIRCMP, cmd, 4))
260 		return (false);
261 	if (!mux_send(dsa->dsa_mux, MUX_DIRCMP, mdp->md_name, mdp->md_namelen))
262 		return (false);
263 	if (!mux_send(dsa->dsa_mux, MUX_DIRCMP, &cmd[4], len))
264 		return (false);
265 
266 	return (true);
267 }
268 
269 bool
dirscan_rcs_symlink(struct dirscan_args * dsa,struct mdirent_rcs * mdp)270 dirscan_rcs_symlink(struct dirscan_args *dsa, struct mdirent_rcs *mdp)
271 {
272 	uint8_t *cmd = dsa->dsa_cmd;
273 	size_t len;
274 	int auxlen;
275 
276 	if (dsa->dsa_pathlen + mdp->md_namelen + 1 > dsa->dsa_pathmax)
277 		return (false);
278 	(void)memcpy(&dsa->dsa_path[dsa->dsa_pathlen], mdp->md_name,
279 		     mdp->md_namelen);
280 	dsa->dsa_path[dsa->dsa_pathlen + mdp->md_namelen] = '\0';
281 
282 	if ((auxlen = readlink(dsa->dsa_path, dsa->dsa_symlink,
283 			       dsa->dsa_pathmax)) == -1) {
284 		return (false);
285 	}
286 
287 	if ((len = mdp->md_namelen + auxlen + 4) > dsa->dsa_cmdmax)
288 		return (false);
289 
290 	SetWord(cmd, len - 2);
291 	cmd[2] = DIRCMP_SYMLINK;
292 	cmd[3] = mdp->md_namelen;
293 	if (!mux_send(dsa->dsa_mux, MUX_DIRCMP, cmd, 4))
294 		return (false);
295 	if (!mux_send(dsa->dsa_mux, MUX_DIRCMP, mdp->md_name, mdp->md_namelen))
296 		return (false);
297 	if (!mux_send(dsa->dsa_mux, MUX_DIRCMP, dsa->dsa_symlink, auxlen))
298 		return (false);
299 
300 	return (true);
301 }
302 
303 struct mDIR *
dirscan_rcs_opendir(struct dirscan_args * dsa,size_t pathlen)304 dirscan_rcs_opendir(struct dirscan_args *dsa, size_t pathlen)
305 {
306 	struct mDIR *mdirp;
307 	struct mdirent_rcs *mdp;
308 	struct mdirent_args mda;
309 	struct collection *cl = dsa->dsa_collection;
310 	size_t rpathlen = pathlen - (dsa->dsa_rpath - dsa->dsa_path), len;
311 
312 	mda.mda_errormode = cl->cl_errormode;
313 	mda.mda_symfollow = false;
314 	mda.mda_remove = true;
315 
316 	if (rpathlen >= cl->cl_rprefixlen) {
317 		if ((mdirp = mopendir_rcs(dsa->dsa_path, pathlen,
318 					  dsa->dsa_pathmax, &mda)) == NULL) {
319 			return (NULL);
320 		}
321 
322 		return (mdirp);
323 	}
324 
325 	if ((mdp = malloc(sizeof(*mdp))) == NULL)
326 		return (NULL);
327 
328 	for (len = rpathlen ; len < cl->cl_rprefixlen ; len++) {
329 		if (cl->cl_rprefix[len] == '/')
330 			break;
331 	}
332 	if ((len -= rpathlen) >= sizeof(mdp->md_name)) {
333 		free(mdp);
334 		return (NULL);
335 	}
336 	(void)memcpy(mdp->md_name, &cl->cl_rprefix[rpathlen], len);
337 	mdp->md_namelen = len;
338 	mdp->md_attic = false;
339 	mdp->md_dead = false;
340 
341 	len = cl->cl_prefixlen + rpathlen + mdp->md_namelen;
342 	if (len >= dsa->dsa_pathmax) {
343 		free(mdp);
344 		return (NULL);
345 	}
346 	(void)memcpy(&dsa->dsa_path[cl->cl_prefixlen], cl->cl_rprefix,
347 		     rpathlen + mdp->md_namelen);
348 	dsa->dsa_path[len] = '\0';
349 
350 	if (lstat(dsa->dsa_path, &mdp->md_stat) == -1) {
351 		free(mdp);
352 		mdp = NULL;
353 	} else {
354 		if (!S_ISDIR(mdp->md_stat.st_mode)) {
355 			free(mdp);
356 			return (NULL);
357 		}
358 	}
359 
360 	if ((mdirp = malloc(sizeof(*mdirp))) == NULL) {
361 		if (mdp != NULL)
362 			free(mdp);
363 		return (NULL);
364 	}
365 	mdirp->m_entries = mdp;
366 	mdirp->m_nentries = (mdp != NULL) ? 1 : 0;
367 	mdirp->m_offset = 0;
368 	mdirp->m_parent = NULL;
369 	mdirp->m_parent_pathlen = 0;
370 
371 	return (mdirp);
372 }
373