2 * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #include "attrstack.h"
47 /* Internal error codes. */
48 #define LISTER_ERR_WRITE (-1) /* Error writing to server. */
49 #define LISTER_ERR_STATUS (-2) /* Status file error in lstr->errmsg. */
52 struct config *config;
57 static int lister_batch(struct lister *);
58 static int lister_coll(struct lister *, struct coll *, struct status *);
59 static int lister_dodirdown(struct lister *, struct coll *,
60 struct statusrec *, struct attrstack *as);
61 static int lister_dodirup(struct lister *, struct coll *,
62 struct statusrec *, struct attrstack *as);
63 static int lister_dofile(struct lister *, struct coll *,
65 static int lister_dodead(struct lister *, struct coll *,
67 static int lister_dorcsfile(struct lister *, struct coll *,
69 static int lister_dorcsdead(struct lister *, struct coll *,
75 struct thread_args *args;
76 struct lister lbuf, *l;
81 l->config = args->config;
84 error = lister_batch(l);
86 case LISTER_ERR_WRITE:
87 xasprintf(&args->errmsg,
88 "TreeList failed: Network write failure: %s",
90 args->status = STATUS_TRANSIENTFAILURE;
92 case LISTER_ERR_STATUS:
93 xasprintf(&args->errmsg,
94 "TreeList failed: %s. Delete it and try again.",
97 args->status = STATUS_FAILURE;
101 args->status = STATUS_SUCCESS;
107 lister_batch(struct lister *l)
109 struct config *config;
117 STAILQ_FOREACH(coll, &config->colls, co_next) {
118 if (coll->co_options & CO_SKIP)
120 st = status_open(coll, -1, &l->errmsg);
122 return (LISTER_ERR_STATUS);
123 error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
126 return (LISTER_ERR_WRITE);
128 if (coll->co_options & CO_COMPRESS)
129 stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL);
130 error = lister_coll(l, coll, st);
131 status_close(st, NULL);
134 if (coll->co_options & CO_COMPRESS)
135 stream_filter_stop(wr);
138 error = proto_printf(wr, ".\n");
140 return (LISTER_ERR_WRITE);
144 /* List a single collection based on the status file. */
146 lister_coll(struct lister *l, struct coll *coll, struct status *st)
149 struct attrstack *as;
150 struct statusrec *sr;
153 int depth, error, ret, prunedepth;
157 prunedepth = INT_MAX;
158 as = attrstack_new();
159 while ((ret = status_get(st, NULL, 0, 0, &sr)) == 1) {
160 switch (sr->sr_type) {
163 if (depth < prunedepth) {
164 error = lister_dodirdown(l, coll, sr, as);
172 if (depth < prunedepth) {
173 error = lister_dodirup(l, coll, sr, as);
176 } else if (depth == prunedepth) {
177 /* Finished pruning. */
178 prunedepth = INT_MAX;
182 case SR_CHECKOUTLIVE:
183 if (depth < prunedepth) {
184 error = lister_dofile(l, coll, sr);
189 case SR_CHECKOUTDEAD:
190 if (depth < prunedepth) {
191 error = lister_dodead(l, coll, sr);
197 if (depth < prunedepth) {
198 if (!(coll->co_options & CO_CHECKOUTMODE)) {
199 error = lister_dorcsdead(l, coll, sr);
206 if (depth < prunedepth) {
207 if (!(coll->co_options & CO_CHECKOUTMODE)) {
208 error = lister_dorcsfile(l, coll, sr);
217 l->errmsg = status_errmsg(st);
218 error = LISTER_ERR_STATUS;
221 assert(status_eof(st));
223 error = proto_printf(wr, ".\n");
226 return (LISTER_ERR_WRITE);
229 for (i = 0; i < attrstack_size(as); i++) {
230 fa = attrstack_pop(as);
237 /* Handle a directory up entry found in the status file. */
239 lister_dodirdown(struct lister *l, struct coll *coll, struct statusrec *sr,
240 struct attrstack *as)
242 struct config *config;
244 struct fattr *fa, *fa2;
250 if (!globtree_test(coll->co_dirfilter, sr->sr_file))
252 if (coll->co_options & CO_TRUSTSTATUSFILE) {
253 fa = fattr_new(FT_DIRECTORY, -1);
255 xasprintf(&path, "%s/%s", coll->co_prefix, sr->sr_file);
256 fa = fattr_frompath(path, FATTR_NOFOLLOW);
258 /* The directory doesn't exist, prune
259 * everything below it. */
263 if (fattr_type(fa) == FT_SYMLINK) {
264 fa2 = fattr_frompath(path, FATTR_FOLLOW);
265 if (fa2 != NULL && fattr_type(fa2) == FT_DIRECTORY) {
266 /* XXX - When not in checkout mode, CVSup warns
267 * here about the file being a symlink to a
268 * directory instead of a directory. */
278 if (fattr_type(fa) != FT_DIRECTORY) {
280 /* Report it as something bogus so
281 * that it will be replaced. */
282 error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file),
283 fattr_bogus, config->fasupport, coll->co_attrignore);
285 return (LISTER_ERR_WRITE);
289 /* It really is a directory. */
290 attrstack_push(as, fa);
291 error = proto_printf(wr, "D %s\n", pathlast(sr->sr_file));
293 return (LISTER_ERR_WRITE);
297 /* Handle a directory up entry found in the status file. */
299 lister_dodirup(struct lister *l, struct coll *coll, struct statusrec *sr,
300 struct attrstack *as)
302 struct config *config;
303 const struct fattr *sendattr;
305 struct fattr *fa, *fa2;
310 fa = attrstack_pop(as);
311 if (coll->co_options & CO_TRUSTSTATUSFILE) {
313 fa = sr->sr_clientattr;
316 fa2 = sr->sr_clientattr;
317 if (fattr_equal(fa, fa2))
320 sendattr = fattr_bogus;
321 error = proto_printf(wr, "U %F\n", sendattr, config->fasupport,
322 coll->co_attrignore);
324 return (LISTER_ERR_WRITE);
325 if (!(coll->co_options & CO_TRUSTSTATUSFILE))
327 /* XXX CVSup flushes here for some reason with a comment saying
328 "Be smarter". We don't flush when listing other file types. */
333 /* Handle a checkout live entry found in the status file. */
335 lister_dofile(struct lister *l, struct coll *coll, struct statusrec *sr)
337 struct config *config;
339 const struct fattr *sendattr, *fa;
340 struct fattr *fa2, *rfa;
344 if (!globtree_test(coll->co_filefilter, sr->sr_file))
351 if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
352 path = checkoutpath(coll->co_prefix, sr->sr_file);
354 spath = coll_statuspath(coll);
355 xasprintf(&l->errmsg, "Error in \"%s\": "
356 "Invalid filename \"%s\"", spath, sr->sr_file);
358 return (LISTER_ERR_STATUS);
360 rfa = fattr_frompath(path, FATTR_NOFOLLOW);
364 * According to the checkouts file we should have
365 * this file but we don't. Maybe the user deleted
366 * the file, or maybe the checkouts file is wrong.
367 * List the file with bogus attributes to cause the
368 * server to get things back in sync again.
370 sendattr = fattr_bogus;
375 fa = sr->sr_clientattr;
377 fa2 = fattr_forcheckout(sr->sr_serverattr, coll->co_umask);
378 if (!fattr_equal(fa, sr->sr_clientattr) || !fattr_equal(fa, fa2) ||
379 strcmp(coll->co_tag, sr->sr_tag) != 0 ||
380 strcmp(coll->co_date, sr->sr_date) != 0) {
382 * The file corresponds to the information we have
383 * recorded about it, and its moded is correct for
384 * the requested umask setting.
386 sendattr = fattr_bogus;
389 * Either the file has been touched, or we are asking
390 * for a different revision than the one we recorded
391 * information about, or its mode isn't right (because
392 * it was last updated using a version of CVSup that
393 * wasn't so strict about modes).
395 sendattr = sr->sr_serverattr;
401 error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file), sendattr,
402 config->fasupport, coll->co_attrignore);
404 return (LISTER_ERR_WRITE);
408 /* Handle a rcs file live entry found in the status file. */
410 lister_dorcsfile(struct lister *l, struct coll *coll, struct statusrec *sr)
412 struct config *config;
414 const struct fattr *sendattr;
420 if (!globtree_test(coll->co_filefilter, sr->sr_file))
424 if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
425 path = cvspath(coll->co_prefix, sr->sr_file, 0);
427 spath = coll_statuspath(coll);
428 xasprintf(&l->errmsg, "Error in \"%s\": "
429 "Invalid filename \"%s\"", spath, sr->sr_file);
431 return (LISTER_ERR_STATUS);
433 fa = fattr_frompath(path, FATTR_NOFOLLOW);
436 fa = sr->sr_clientattr;
437 if (fa != NULL && fattr_equal(fa, sr->sr_clientattr)) {
439 * If the file is an RCS file, we use "loose" equality, so sizes
440 * may disagress because of differences in whitespace.
442 if (isrcs(sr->sr_file, &len) &&
443 !(coll->co_options & CO_NORCS) &&
444 !(coll->co_options & CO_STRICTCHECKRCS)) {
445 fattr_maskout(fa, FA_SIZE);
450 * If different, the user may have changed it, so we report
451 * bogus attributes to force a full comparison.
453 sendattr = fattr_bogus;
455 error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file), sendattr,
456 config->fasupport, coll->co_attrignore);
458 return (LISTER_ERR_WRITE);
462 /* Handle a checkout dead entry found in the status file. */
464 lister_dodead(struct lister *l, struct coll *coll, struct statusrec *sr)
466 struct config *config;
468 const struct fattr *sendattr;
473 if (!globtree_test(coll->co_filefilter, sr->sr_file))
477 if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
478 path = checkoutpath(coll->co_prefix, sr->sr_file);
480 spath = coll_statuspath(coll);
481 xasprintf(&l->errmsg, "Error in \"%s\": "
482 "Invalid filename \"%s\"", spath, sr->sr_file);
484 return (LISTER_ERR_STATUS);
486 fa = fattr_frompath(path, FATTR_NOFOLLOW);
488 if (fa != NULL && fattr_type(fa) != FT_DIRECTORY) {
490 * We shouldn't have this file but we do. Report
491 * it to the server, which will either send a
492 * deletion request, of (if the file has come alive)
493 * sent the correct version.
496 error = proto_printf(wr, "F %s %F\n",
497 pathlast(sr->sr_file), fattr_bogus,
498 config->fasupport, coll->co_attrignore);
500 return (LISTER_ERR_WRITE);
505 if (strcmp(coll->co_tag, sr->sr_tag) != 0 ||
506 strcmp(coll->co_date, sr->sr_date) != 0)
507 sendattr = fattr_bogus;
509 sendattr = sr->sr_serverattr;
510 error = proto_printf(wr, "f %s %F\n", pathlast(sr->sr_file), sendattr,
511 config->fasupport, coll->co_attrignore);
513 return (LISTER_ERR_WRITE);
517 /* Handle a rcs file dead entry found in the status file. */
519 lister_dorcsdead(struct lister *l, struct coll *coll, struct statusrec *sr)
521 struct config *config;
523 const struct fattr *sendattr;
529 if (!globtree_test(coll->co_filefilter, sr->sr_file))
533 if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
534 path = cvspath(coll->co_prefix, sr->sr_file, 1);
536 spath = coll_statuspath(coll);
537 xasprintf(&l->errmsg, "Error in \"%s\": "
538 "Invalid filename \"%s\"", spath, sr->sr_file);
540 return (LISTER_ERR_STATUS);
542 fa = fattr_frompath(path, FATTR_NOFOLLOW);
545 fa = sr->sr_clientattr;
546 if (fattr_equal(fa, sr->sr_clientattr)) {
548 * If the file is an RCS file, we use "loose" equality, so sizes
549 * may disagress because of differences in whitespace.
551 if (isrcs(sr->sr_file, &len) &&
552 !(coll->co_options & CO_NORCS) &&
553 !(coll->co_options & CO_STRICTCHECKRCS)) {
554 fattr_maskout(fa, FA_SIZE);
559 * If different, the user may have changed it, so we report
560 * bogus attributes to force a full comparison.
562 sendattr = fattr_bogus;
564 error = proto_printf(wr, "f %s %F\n", pathlast(sr->sr_file), sendattr,
565 config->fasupport, coll->co_attrignore);
567 return (LISTER_ERR_WRITE);