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