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 *,
71 struct thread_args *args;
72 struct lister lbuf, *l;
77 l->config = args->config;
80 error = lister_batch(l);
82 case LISTER_ERR_WRITE:
83 xasprintf(&args->errmsg,
84 "TreeList failed: Network write failure: %s",
86 args->status = STATUS_TRANSIENTFAILURE;
88 case LISTER_ERR_STATUS:
89 xasprintf(&args->errmsg,
90 "TreeList failed: %s. Delete it and try again.",
93 args->status = STATUS_FAILURE;
97 args->status = STATUS_SUCCESS;
103 lister_batch(struct lister *l)
105 struct config *config;
113 STAILQ_FOREACH(coll, &config->colls, co_next) {
114 if (coll->co_options & CO_SKIP)
116 st = status_open(coll, -1, &l->errmsg);
118 return (LISTER_ERR_STATUS);
119 error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
122 return (LISTER_ERR_WRITE);
124 if (coll->co_options & CO_COMPRESS)
125 stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL);
126 error = lister_coll(l, coll, st);
127 status_close(st, NULL);
130 if (coll->co_options & CO_COMPRESS)
131 stream_filter_stop(wr);
134 error = proto_printf(wr, ".\n");
136 return (LISTER_ERR_WRITE);
140 /* List a single collection based on the status file. */
142 lister_coll(struct lister *l, struct coll *coll, struct status *st)
145 struct attrstack *as;
146 struct statusrec *sr;
149 int depth, error, ret, prunedepth;
153 prunedepth = INT_MAX;
154 as = attrstack_new();
155 while ((ret = status_get(st, NULL, 0, 0, &sr)) == 1) {
156 switch (sr->sr_type) {
159 if (depth < prunedepth) {
160 error = lister_dodirdown(l, coll, sr, as);
168 if (depth < prunedepth) {
169 error = lister_dodirup(l, coll, sr, as);
172 } else if (depth == prunedepth) {
173 /* Finished pruning. */
174 prunedepth = INT_MAX;
178 case SR_CHECKOUTLIVE:
179 if (depth < prunedepth) {
180 error = lister_dofile(l, coll, sr);
185 case SR_CHECKOUTDEAD:
186 if (depth < prunedepth) {
187 error = lister_dodead(l, coll, sr);
195 l->errmsg = status_errmsg(st);
196 error = LISTER_ERR_STATUS;
199 assert(status_eof(st));
201 error = proto_printf(wr, ".\n");
204 return (LISTER_ERR_WRITE);
207 for (i = 0; i < attrstack_size(as); i++) {
208 fa = attrstack_pop(as);
215 /* Handle a directory up entry found in the status file. */
217 lister_dodirdown(struct lister *l, struct coll *coll, struct statusrec *sr,
218 struct attrstack *as)
220 struct config *config;
222 struct fattr *fa, *fa2;
228 if (!globtree_test(coll->co_dirfilter, sr->sr_file))
230 if (coll->co_options & CO_TRUSTSTATUSFILE) {
231 fa = fattr_new(FT_DIRECTORY, -1);
233 xasprintf(&path, "%s/%s", coll->co_prefix, sr->sr_file);
234 fa = fattr_frompath(path, FATTR_NOFOLLOW);
236 /* The directory doesn't exist, prune
237 * everything below it. */
241 if (fattr_type(fa) == FT_SYMLINK) {
242 fa2 = fattr_frompath(path, FATTR_FOLLOW);
243 if (fa2 != NULL && fattr_type(fa2) == FT_DIRECTORY) {
244 /* XXX - When not in checkout mode, CVSup warns
245 * here about the file being a symlink to a
246 * directory instead of a directory. */
256 if (fattr_type(fa) != FT_DIRECTORY) {
258 /* Report it as something bogus so
259 * that it will be replaced. */
260 error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file),
261 fattr_bogus, config->fasupport, coll->co_attrignore);
263 return (LISTER_ERR_WRITE);
267 /* It really is a directory. */
268 attrstack_push(as, fa);
269 error = proto_printf(wr, "D %s\n", pathlast(sr->sr_file));
271 return (LISTER_ERR_WRITE);
275 /* Handle a directory up entry found in the status file. */
277 lister_dodirup(struct lister *l, struct coll *coll, struct statusrec *sr,
278 struct attrstack *as)
280 struct config *config;
281 const struct fattr *sendattr;
283 struct fattr *fa, *fa2;
288 fa = attrstack_pop(as);
289 if (coll->co_options & CO_TRUSTSTATUSFILE) {
291 fa = sr->sr_clientattr;
294 fa2 = sr->sr_clientattr;
295 if (fattr_equal(fa, fa2))
298 sendattr = fattr_bogus;
299 error = proto_printf(wr, "U %F\n", sendattr, config->fasupport,
300 coll->co_attrignore);
302 return (LISTER_ERR_WRITE);
303 if (!(coll->co_options & CO_TRUSTSTATUSFILE))
305 /* XXX CVSup flushes here for some reason with a comment saying
306 "Be smarter". We don't flush when listing other file types. */
311 /* Handle a checkout live entry found in the status file. */
313 lister_dofile(struct lister *l, struct coll *coll, struct statusrec *sr)
315 struct config *config;
317 const struct fattr *sendattr, *fa;
318 struct fattr *fa2, *rfa;
322 if (!globtree_test(coll->co_filefilter, sr->sr_file))
329 if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
330 path = checkoutpath(coll->co_prefix, sr->sr_file);
332 spath = coll_statuspath(coll);
333 xasprintf(&l->errmsg, "Error in \"%s\": "
334 "Invalid filename \"%s\"", spath, sr->sr_file);
336 return (LISTER_ERR_STATUS);
338 rfa = fattr_frompath(path, FATTR_NOFOLLOW);
342 * According to the checkouts file we should have
343 * this file but we don't. Maybe the user deleted
344 * the file, or maybe the checkouts file is wrong.
345 * List the file with bogus attributes to cause the
346 * server to get things back in sync again.
348 sendattr = fattr_bogus;
353 fa = sr->sr_clientattr;
355 fa2 = fattr_forcheckout(sr->sr_serverattr, coll->co_umask);
356 if (!fattr_equal(fa, sr->sr_clientattr) || !fattr_equal(fa, fa2) ||
357 strcmp(coll->co_tag, sr->sr_tag) != 0 ||
358 strcmp(coll->co_date, sr->sr_date) != 0) {
360 * The file corresponds to the information we have
361 * recorded about it, and its moded is correct for
362 * the requested umask setting.
364 sendattr = fattr_bogus;
367 * Either the file has been touched, or we are asking
368 * for a different revision than the one we recorded
369 * information about, or its mode isn't right (because
370 * it was last updated using a version of CVSup that
371 * wasn't so strict about modes).
373 sendattr = sr->sr_serverattr;
379 error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file), sendattr,
380 config->fasupport, coll->co_attrignore);
382 return (LISTER_ERR_WRITE);
386 /* Handle a checkout dead entry found in the status file. */
388 lister_dodead(struct lister *l, struct coll *coll, struct statusrec *sr)
390 struct config *config;
392 const struct fattr *sendattr;
397 if (!globtree_test(coll->co_filefilter, sr->sr_file))
401 if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
402 path = checkoutpath(coll->co_prefix, sr->sr_file);
404 spath = coll_statuspath(coll);
405 xasprintf(&l->errmsg, "Error in \"%s\": "
406 "Invalid filename \"%s\"", spath, sr->sr_file);
408 return (LISTER_ERR_STATUS);
410 fa = fattr_frompath(path, FATTR_NOFOLLOW);
412 if (fa != NULL && fattr_type(fa) != FT_DIRECTORY) {
414 * We shouldn't have this file but we do. Report
415 * it to the server, which will either send a
416 * deletion request, of (if the file has come alive)
417 * sent the correct version.
420 error = proto_printf(wr, "F %s %F\n",
421 pathlast(sr->sr_file), fattr_bogus,
422 config->fasupport, coll->co_attrignore);
424 return (LISTER_ERR_WRITE);
429 if (strcmp(coll->co_tag, sr->sr_tag) != 0 ||
430 strcmp(coll->co_date, sr->sr_date) != 0)
431 sendattr = fattr_bogus;
433 sendattr = sr->sr_serverattr;
434 error = proto_printf(wr, "f %s %F\n", pathlast(sr->sr_file), sendattr,
435 config->fasupport, coll->co_attrignore);
437 return (LISTER_ERR_WRITE);