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