1156230Smux/*- 2156230Smux * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> 3156230Smux * All rights reserved. 4156230Smux * 5156230Smux * Redistribution and use in source and binary forms, with or without 6156230Smux * modification, are permitted provided that the following conditions 7156230Smux * are met: 8156230Smux * 1. Redistributions of source code must retain the above copyright 9156230Smux * notice, this list of conditions and the following disclaimer. 10156230Smux * 2. Redistributions in binary form must reproduce the above copyright 11156230Smux * notice, this list of conditions and the following disclaimer in the 12156230Smux * documentation and/or other materials provided with the distribution. 13156230Smux * 14156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17156230Smux * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24156230Smux * SUCH DAMAGE. 25156230Smux * 26156230Smux * $FreeBSD$ 27156230Smux */ 28156230Smux 29156230Smux#include <assert.h> 30156230Smux#include <errno.h> 31156230Smux#include <stdlib.h> 32156230Smux#include <string.h> 33186781Slulf#include <stdio.h> 34156230Smux 35186781Slulf#include <sys/types.h> 36186781Slulf#include <sys/stat.h> 37186781Slulf#include <unistd.h> 38186781Slulf 39156230Smux#include "config.h" 40156230Smux#include "detailer.h" 41156230Smux#include "fixups.h" 42186781Slulf#include "globtree.h" 43156230Smux#include "misc.h" 44156230Smux#include "mux.h" 45156230Smux#include "proto.h" 46186781Slulf#include "rcsfile.h" 47186781Slulf#include "rsyncfile.h" 48156230Smux#include "status.h" 49156230Smux#include "stream.h" 50156230Smux 51156230Smux/* Internal error codes. */ 52156230Smux#define DETAILER_ERR_PROTO (-1) /* Protocol error. */ 53156230Smux#define DETAILER_ERR_MSG (-2) /* Error is in detailer->errmsg. */ 54156230Smux#define DETAILER_ERR_READ (-3) /* Error reading from server. */ 55156230Smux#define DETAILER_ERR_WRITE (-4) /* Error writing to server. */ 56156230Smux 57156230Smuxstruct detailer { 58156230Smux struct config *config; 59156230Smux struct stream *rd; 60156230Smux struct stream *wr; 61156230Smux char *errmsg; 62156230Smux}; 63156230Smux 64156230Smuxstatic int detailer_batch(struct detailer *); 65156230Smuxstatic int detailer_coll(struct detailer *, struct coll *, 66156230Smux struct status *); 67186781Slulfstatic int detailer_dofile_co(struct detailer *, struct coll *, 68156230Smux struct status *, char *); 69186781Slulfstatic int detailer_dofile_rcs(struct detailer *, struct coll *, 70186781Slulf char *, char *); 71186781Slulfstatic int detailer_dofile_regular(struct detailer *, char *, char *); 72186781Slulfstatic int detailer_dofile_rsync(struct detailer *, char *, char *); 73186781Slulfstatic int detailer_checkrcsattr(struct detailer *, struct coll *, char *, 74186781Slulf struct fattr *, int); 75186781Slulfint detailer_send_details(struct detailer *, struct coll *, char *, 76186781Slulf char *, struct fattr *); 77156230Smux 78156230Smuxvoid * 79156230Smuxdetailer(void *arg) 80156230Smux{ 81156230Smux struct thread_args *args; 82156230Smux struct detailer dbuf, *d; 83156230Smux int error; 84156230Smux 85156230Smux args = arg; 86156230Smux 87156230Smux d = &dbuf; 88156230Smux d->config = args->config; 89156230Smux d->rd = args->rd; 90156230Smux d->wr = args->wr; 91156230Smux d->errmsg = NULL; 92156230Smux 93156230Smux error = detailer_batch(d); 94156230Smux switch (error) { 95156230Smux case DETAILER_ERR_PROTO: 96156230Smux xasprintf(&args->errmsg, "Detailer failed: Protocol error"); 97156230Smux args->status = STATUS_FAILURE; 98156230Smux break; 99156230Smux case DETAILER_ERR_MSG: 100156230Smux xasprintf(&args->errmsg, "Detailer failed: %s", d->errmsg); 101156230Smux free(d->errmsg); 102156230Smux args->status = STATUS_FAILURE; 103156230Smux break; 104156230Smux case DETAILER_ERR_READ: 105156230Smux if (stream_eof(d->rd)) { 106156230Smux xasprintf(&args->errmsg, "Detailer failed: " 107156230Smux "Premature EOF from server"); 108156230Smux } else { 109156230Smux xasprintf(&args->errmsg, "Detailer failed: " 110156230Smux "Network read failure: %s", strerror(errno)); 111156230Smux } 112156230Smux args->status = STATUS_TRANSIENTFAILURE; 113156230Smux break; 114156230Smux case DETAILER_ERR_WRITE: 115156230Smux xasprintf(&args->errmsg, "Detailer failed: " 116156230Smux "Network write failure: %s", strerror(errno)); 117156230Smux args->status = STATUS_TRANSIENTFAILURE; 118156230Smux break; 119156230Smux default: 120156230Smux assert(error == 0); 121156230Smux args->status = STATUS_SUCCESS; 122156230Smux } 123156230Smux return (NULL); 124156230Smux} 125156230Smux 126156230Smuxstatic int 127156230Smuxdetailer_batch(struct detailer *d) 128156230Smux{ 129156230Smux struct config *config; 130156230Smux struct stream *rd, *wr; 131156230Smux struct coll *coll; 132156230Smux struct status *st; 133156230Smux struct fixup *fixup; 134156230Smux char *cmd, *collname, *line, *release; 135156230Smux int error, fixupseof; 136156230Smux 137156230Smux config = d->config; 138156230Smux rd = d->rd; 139156230Smux wr = d->wr; 140156230Smux STAILQ_FOREACH(coll, &config->colls, co_next) { 141156230Smux if (coll->co_options & CO_SKIP) 142156230Smux continue; 143156230Smux line = stream_getln(rd, NULL); 144156230Smux cmd = proto_get_ascii(&line); 145156230Smux collname = proto_get_ascii(&line); 146156230Smux release = proto_get_ascii(&line); 147156230Smux error = proto_get_time(&line, &coll->co_scantime); 148156230Smux if (error || line != NULL || strcmp(cmd, "COLL") != 0 || 149156230Smux strcmp(collname, coll->co_name) != 0 || 150156230Smux strcmp(release, coll->co_release) != 0) 151156230Smux return (DETAILER_ERR_PROTO); 152156230Smux error = proto_printf(wr, "COLL %s %s\n", coll->co_name, 153156230Smux coll->co_release); 154156230Smux if (error) 155156230Smux return (DETAILER_ERR_WRITE); 156156230Smux stream_flush(wr); 157156230Smux if (coll->co_options & CO_COMPRESS) { 158156230Smux stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL); 159156230Smux stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL); 160156230Smux } 161156230Smux st = status_open(coll, -1, &d->errmsg); 162156230Smux if (st == NULL) 163156230Smux return (DETAILER_ERR_MSG); 164156230Smux error = detailer_coll(d, coll, st); 165156230Smux status_close(st, NULL); 166156230Smux if (error) 167156230Smux return (error); 168156230Smux if (coll->co_options & CO_COMPRESS) { 169156230Smux stream_filter_stop(rd); 170156230Smux stream_filter_stop(wr); 171156230Smux } 172156230Smux stream_flush(wr); 173156230Smux } 174156230Smux line = stream_getln(rd, NULL); 175156230Smux if (line == NULL) 176156230Smux return (DETAILER_ERR_READ); 177156230Smux if (strcmp(line, ".") != 0) 178156230Smux return (DETAILER_ERR_PROTO); 179156230Smux error = proto_printf(wr, ".\n"); 180156230Smux if (error) 181156230Smux return (DETAILER_ERR_WRITE); 182156230Smux stream_flush(wr); 183156230Smux 184156230Smux /* Now send fixups if needed. */ 185156230Smux fixup = NULL; 186156230Smux fixupseof = 0; 187156230Smux STAILQ_FOREACH(coll, &config->colls, co_next) { 188156230Smux if (coll->co_options & CO_SKIP) 189156230Smux continue; 190156230Smux error = proto_printf(wr, "COLL %s %s\n", coll->co_name, 191156230Smux coll->co_release); 192156230Smux if (error) 193156230Smux return (DETAILER_ERR_WRITE); 194156230Smux if (coll->co_options & CO_COMPRESS) 195156230Smux stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL); 196156230Smux while (!fixupseof) { 197156230Smux if (fixup == NULL) 198156230Smux fixup = fixups_get(config->fixups); 199156230Smux if (fixup == NULL) { 200156230Smux fixupseof = 1; 201156230Smux break; 202156230Smux } 203156230Smux if (fixup->f_coll != coll) 204156230Smux break; 205186781Slulf if (coll->co_options & CO_CHECKOUTMODE) 206186781Slulf error = proto_printf(wr, "Y %s %s %s\n", 207186781Slulf fixup->f_name, coll->co_tag, coll->co_date); 208186781Slulf else { 209186781Slulf error = proto_printf(wr, "A %s\n", 210186781Slulf fixup->f_name); 211186781Slulf } 212156230Smux if (error) 213156230Smux return (DETAILER_ERR_WRITE); 214156230Smux fixup = NULL; 215156230Smux } 216156230Smux error = proto_printf(wr, ".\n"); 217156230Smux if (error) 218156230Smux return (DETAILER_ERR_WRITE); 219156230Smux if (coll->co_options & CO_COMPRESS) 220156230Smux stream_filter_stop(wr); 221156230Smux stream_flush(wr); 222156230Smux } 223156230Smux error = proto_printf(wr, ".\n"); 224156230Smux if (error) 225156230Smux return (DETAILER_ERR_WRITE); 226156230Smux return (0); 227156230Smux} 228156230Smux 229156230Smuxstatic int 230156230Smuxdetailer_coll(struct detailer *d, struct coll *coll, struct status *st) 231156230Smux{ 232186781Slulf struct fattr *rcsattr; 233156230Smux struct stream *rd, *wr; 234186781Slulf char *attr, *cmd, *file, *line, *msg, *path, *target; 235186781Slulf int error, attic; 236156230Smux 237156230Smux rd = d->rd; 238156230Smux wr = d->wr; 239186781Slulf attic = 0; 240156230Smux line = stream_getln(rd, NULL); 241156230Smux if (line == NULL) 242156230Smux return (DETAILER_ERR_READ); 243156230Smux while (strcmp(line, ".") != 0) { 244156230Smux cmd = proto_get_ascii(&line); 245156230Smux if (cmd == NULL || strlen(cmd) != 1) 246156230Smux return (DETAILER_ERR_PROTO); 247156230Smux switch (cmd[0]) { 248156230Smux case 'D': 249156230Smux /* Delete file. */ 250156230Smux file = proto_get_ascii(&line); 251156230Smux if (file == NULL || line != NULL) 252186781Slulf return (DETAILER_ERR_PROTO); 253156230Smux error = proto_printf(wr, "D %s\n", file); 254156230Smux if (error) 255156230Smux return (DETAILER_ERR_WRITE); 256156230Smux break; 257186781Slulf case 'I': 258186781Slulf case 'i': 259186781Slulf case 'j': 260186781Slulf /* Directory operations. */ 261186781Slulf file = proto_get_ascii(&line); 262186781Slulf if (file == NULL || line != NULL) 263186781Slulf return (DETAILER_ERR_PROTO); 264186781Slulf error = proto_printf(wr, "%s %s\n", cmd, file); 265186781Slulf if (error) 266186781Slulf return (DETAILER_ERR_WRITE); 267186781Slulf break; 268186781Slulf case 'J': 269186781Slulf /* Set directory attributes. */ 270186781Slulf file = proto_get_ascii(&line); 271186781Slulf attr = proto_get_ascii(&line); 272186781Slulf if (file == NULL || line != NULL || attr == NULL) 273186781Slulf return (DETAILER_ERR_PROTO); 274186781Slulf error = proto_printf(wr, "%s %s %s\n", cmd, file, attr); 275186781Slulf if (error) 276186781Slulf return (DETAILER_ERR_WRITE); 277186781Slulf break; 278186781Slulf case 'H': 279186781Slulf case 'h': 280186781Slulf /* Create a hard link. */ 281186781Slulf file = proto_get_ascii(&line); 282186781Slulf target = proto_get_ascii(&line); 283186781Slulf if (file == NULL || target == NULL) 284186781Slulf return (DETAILER_ERR_PROTO); 285186781Slulf error = proto_printf(wr, "%s %s %s\n", cmd, file, 286186781Slulf target); 287186781Slulf break; 288186781Slulf case 't': 289186781Slulf file = proto_get_ascii(&line); 290186781Slulf attr = proto_get_ascii(&line); 291186781Slulf if (file == NULL || attr == NULL || line != NULL) { 292186781Slulf return (DETAILER_ERR_PROTO); 293186781Slulf } 294186781Slulf rcsattr = fattr_decode(attr); 295186781Slulf if (rcsattr == NULL) { 296186781Slulf return (DETAILER_ERR_PROTO); 297186781Slulf } 298186781Slulf error = detailer_checkrcsattr(d, coll, file, rcsattr, 299186781Slulf 1); 300186781Slulf break; 301186781Slulf 302186781Slulf case 'T': 303186781Slulf file = proto_get_ascii(&line); 304186781Slulf attr = proto_get_ascii(&line); 305186781Slulf if (file == NULL || attr == NULL || line != NULL) 306186781Slulf return (DETAILER_ERR_PROTO); 307186781Slulf rcsattr = fattr_decode(attr); 308186781Slulf if (rcsattr == NULL) 309186781Slulf return (DETAILER_ERR_PROTO); 310186781Slulf error = detailer_checkrcsattr(d, coll, file, rcsattr, 311186781Slulf 0); 312186781Slulf break; 313186781Slulf 314156230Smux case 'U': 315156230Smux /* Add or update file. */ 316156230Smux file = proto_get_ascii(&line); 317156230Smux if (file == NULL || line != NULL) 318156230Smux return (DETAILER_ERR_PROTO); 319186781Slulf if (coll->co_options & CO_CHECKOUTMODE) { 320186781Slulf error = detailer_dofile_co(d, coll, st, file); 321186781Slulf } else { 322186781Slulf path = cvspath(coll->co_prefix, file, 0); 323186781Slulf rcsattr = fattr_frompath(path, FATTR_NOFOLLOW); 324186781Slulf error = detailer_send_details(d, coll, file, 325186781Slulf path, rcsattr); 326186781Slulf if (rcsattr != NULL) 327186781Slulf fattr_free(rcsattr); 328186781Slulf free(path); 329186781Slulf } 330156230Smux if (error) 331156230Smux return (error); 332156230Smux break; 333156230Smux case '!': 334156230Smux /* Warning from server. */ 335156230Smux msg = proto_get_rest(&line); 336156230Smux if (msg == NULL) 337156230Smux return (DETAILER_ERR_PROTO); 338156230Smux lprintf(-1, "Server warning: %s\n", msg); 339156230Smux break; 340156230Smux default: 341156230Smux return (DETAILER_ERR_PROTO); 342156230Smux } 343156230Smux stream_flush(wr); 344156230Smux line = stream_getln(rd, NULL); 345156230Smux if (line == NULL) 346156230Smux return (DETAILER_ERR_READ); 347156230Smux } 348156230Smux error = proto_printf(wr, ".\n"); 349156230Smux if (error) 350156230Smux return (DETAILER_ERR_WRITE); 351156230Smux return (0); 352156230Smux} 353156230Smux 354186781Slulf/* 355186781Slulf * Tell the server to update a regular file. 356186781Slulf */ 357156230Smuxstatic int 358186781Slulfdetailer_dofile_regular(struct detailer *d, char *name, char *path) 359156230Smux{ 360186781Slulf struct stream *wr; 361186781Slulf struct stat st; 362156230Smux char md5[MD5_DIGEST_SIZE]; 363186781Slulf int error; 364186781Slulf 365186781Slulf wr = d->wr; 366186781Slulf error = stat(path, &st); 367186781Slulf /* If we don't have it or it's unaccessible, we want it again. */ 368186781Slulf if (error) { 369186781Slulf proto_printf(wr, "A %s\n", name); 370186781Slulf return (0); 371186781Slulf } 372186781Slulf 373186781Slulf /* If not, we want the file to be updated. */ 374186781Slulf error = MD5_File(path, md5); 375186781Slulf if (error) { 376186781Slulf lprintf(-1, "Error reading \"%s\"\n", name); 377186781Slulf return (error); 378186781Slulf } 379186781Slulf error = proto_printf(wr, "R %s %O %s\n", name, st.st_size, md5); 380186781Slulf if (error) 381186781Slulf return (DETAILER_ERR_WRITE); 382186781Slulf return (0); 383186781Slulf} 384186781Slulf 385186781Slulf/* 386186781Slulf * Tell the server to update a file with the rsync algorithm. 387186781Slulf */ 388186781Slulfstatic int 389186781Slulfdetailer_dofile_rsync(struct detailer *d, char *name, char *path) 390186781Slulf{ 391156230Smux struct stream *wr; 392186781Slulf struct rsyncfile *rf; 393186781Slulf 394186781Slulf wr = d->wr; 395186781Slulf rf = rsync_open(path, 0, 1); 396186781Slulf if (rf == NULL) { 397186781Slulf /* Fallback if we fail in opening it. */ 398186781Slulf proto_printf(wr, "A %s\n", name); 399186781Slulf return (0); 400186781Slulf } 401186781Slulf proto_printf(wr, "r %s %z %z\n", name, rsync_filesize(rf), 402186781Slulf rsync_blocksize(rf)); 403186781Slulf /* Detail the blocks. */ 404186781Slulf while (rsync_nextblock(rf) != 0) 405186781Slulf proto_printf(wr, "%s %s\n", rsync_rsum(rf), rsync_blockmd5(rf)); 406186781Slulf proto_printf(wr, ".\n"); 407186781Slulf rsync_close(rf); 408186781Slulf return (0); 409186781Slulf} 410186781Slulf 411186781Slulf/* 412186781Slulf * Tell the server to update an RCS file that we have, or send it if we don't. 413186781Slulf */ 414186781Slulfstatic int 415186781Slulfdetailer_dofile_rcs(struct detailer *d, struct coll *coll, char *name, 416186781Slulf char *path) 417186781Slulf{ 418186781Slulf struct stream *wr; 419156230Smux struct fattr *fa; 420186781Slulf struct rcsfile *rf; 421186781Slulf int error; 422186781Slulf 423186781Slulf wr = d->wr; 424186781Slulf path = atticpath(coll->co_prefix, name); 425186781Slulf fa = fattr_frompath(path, FATTR_NOFOLLOW); 426186781Slulf if (fa == NULL) { 427186781Slulf /* We don't have it, so send request to get it. */ 428186781Slulf error = proto_printf(wr, "A %s\n", name); 429186781Slulf if (error) 430186781Slulf return (DETAILER_ERR_WRITE); 431186781Slulf free(path); 432186781Slulf return (0); 433186781Slulf } 434186781Slulf 435186781Slulf rf = rcsfile_frompath(path, name, coll->co_cvsroot, coll->co_tag, 1); 436186781Slulf free(path); 437186781Slulf if (rf == NULL) { 438186781Slulf error = proto_printf(wr, "A %s\n", name); 439186781Slulf if (error) 440186781Slulf return (DETAILER_ERR_WRITE); 441186781Slulf return (0); 442186781Slulf } 443186781Slulf /* Tell to update the RCS file. The client version details follow. */ 444186781Slulf rcsfile_send_details(rf, wr); 445186781Slulf rcsfile_free(rf); 446186781Slulf fattr_free(fa); 447186781Slulf return (0); 448186781Slulf} 449186781Slulf 450186781Slulfstatic int 451186781Slulfdetailer_dofile_co(struct detailer *d, struct coll *coll, struct status *st, 452186781Slulf char *file) 453186781Slulf{ 454186781Slulf struct stream *wr; 455186781Slulf struct fattr *fa; 456156230Smux struct statusrec *sr; 457186781Slulf char md5[MD5_DIGEST_SIZE]; 458156230Smux char *path; 459156230Smux int error, ret; 460156230Smux 461156230Smux wr = d->wr; 462156230Smux path = checkoutpath(coll->co_prefix, file); 463156230Smux if (path == NULL) 464156230Smux return (DETAILER_ERR_PROTO); 465156230Smux fa = fattr_frompath(path, FATTR_NOFOLLOW); 466156230Smux if (fa == NULL) { 467156230Smux /* We don't have the file, so the only option at this 468156230Smux point is to tell the server to send it. The server 469156230Smux may figure out that the file is dead, in which case 470156230Smux it will tell us. */ 471156230Smux error = proto_printf(wr, "C %s %s %s\n", 472156230Smux file, coll->co_tag, coll->co_date); 473156230Smux free(path); 474156230Smux if (error) 475156230Smux return (DETAILER_ERR_WRITE); 476156230Smux return (0); 477156230Smux } 478156230Smux ret = status_get(st, file, 0, 0, &sr); 479156230Smux if (ret == -1) { 480156230Smux d->errmsg = status_errmsg(st); 481156230Smux free(path); 482156230Smux return (DETAILER_ERR_MSG); 483156230Smux } 484156230Smux if (ret == 0) 485156230Smux sr = NULL; 486156230Smux 487156230Smux /* If our recorded information doesn't match the file that the 488156230Smux client has, then ignore the recorded information. */ 489156230Smux if (sr != NULL && (sr->sr_type != SR_CHECKOUTLIVE || 490156230Smux !fattr_equal(sr->sr_clientattr, fa))) 491156230Smux sr = NULL; 492156230Smux fattr_free(fa); 493156230Smux if (sr != NULL && strcmp(sr->sr_revdate, ".") != 0) { 494156230Smux error = proto_printf(wr, "U %s %s %s %s %s\n", file, 495156230Smux coll->co_tag, coll->co_date, sr->sr_revnum, sr->sr_revdate); 496156230Smux free(path); 497156230Smux if (error) 498156230Smux return (DETAILER_ERR_WRITE); 499156230Smux return (0); 500156230Smux } 501156230Smux 502156230Smux /* 503156230Smux * We don't have complete and/or accurate recorded information 504156230Smux * about what version of the file we have. Compute the file's 505156230Smux * checksum as an aid toward identifying which version it is. 506156230Smux */ 507156230Smux error = MD5_File(path, md5); 508156230Smux if (error) { 509156230Smux xasprintf(&d->errmsg, 510156230Smux "Cannot calculate checksum for \"%s\": %s", path, 511156230Smux strerror(errno)); 512156230Smux return (DETAILER_ERR_MSG); 513156230Smux } 514156230Smux free(path); 515156230Smux if (sr == NULL) { 516156230Smux error = proto_printf(wr, "S %s %s %s %s\n", file, 517156230Smux coll->co_tag, coll->co_date, md5); 518156230Smux } else { 519156230Smux error = proto_printf(wr, "s %s %s %s %s %s\n", file, 520156230Smux coll->co_tag, coll->co_date, sr->sr_revnum, md5); 521156230Smux } 522156230Smux if (error) 523156230Smux return (DETAILER_ERR_WRITE); 524156230Smux return (0); 525156230Smux} 526186781Slulf 527186781Slulfint 528186781Slulfdetailer_checkrcsattr(struct detailer *d, struct coll *coll, char *name, 529186781Slulf struct fattr *server_attr, int attic) 530186781Slulf{ 531186781Slulf struct fattr *client_attr; 532186781Slulf char *attr, *path; 533186781Slulf int error; 534186781Slulf 535186781Slulf /* 536186781Slulf * I don't think we can use the status file, since it only records file 537186781Slulf * attributes in cvsmode. 538186781Slulf */ 539186781Slulf client_attr = NULL; 540186781Slulf path = cvspath(coll->co_prefix, name, attic); 541186781Slulf if (path == NULL) { 542186781Slulf return (DETAILER_ERR_PROTO); 543186781Slulf } 544186781Slulf 545186781Slulf if (access(path, F_OK) == 0 && 546186781Slulf ((client_attr = fattr_frompath(path, FATTR_NOFOLLOW)) != NULL) && 547186781Slulf fattr_equal(client_attr, server_attr)) { 548186781Slulf attr = fattr_encode(client_attr, NULL, 0); 549186781Slulf if (attic) { 550186781Slulf error = proto_printf(d->wr, "l %s %s\n", name, attr); 551186781Slulf } else { 552186781Slulf error = proto_printf(d->wr, "L %s %s\n", name, attr); 553186781Slulf } 554186781Slulf free(attr); 555186781Slulf free(path); 556186781Slulf fattr_free(client_attr); 557186781Slulf if (error) 558186781Slulf return (DETAILER_ERR_WRITE); 559186781Slulf return (0); 560186781Slulf } 561186781Slulf /* We don't have it, so tell the server to send it. */ 562186781Slulf error = detailer_send_details(d, coll, name, path, client_attr); 563186781Slulf fattr_free(client_attr); 564186781Slulf free(path); 565186781Slulf return (error); 566186781Slulf} 567186781Slulf 568186781Slulfint 569186781Slulfdetailer_send_details(struct detailer *d, struct coll *coll, char *name, 570186781Slulf char *path, struct fattr *fa) 571186781Slulf{ 572186781Slulf int error; 573186781Slulf size_t len; 574186781Slulf 575186781Slulf /* 576186781Slulf * Try to check if the file exists either live or dead to see if we can 577186781Slulf * edit it and put it live or dead, rather than receiving the entire 578186781Slulf * file. 579186781Slulf */ 580186781Slulf if (fa == NULL) { 581186781Slulf path = atticpath(coll->co_prefix, name); 582186781Slulf fa = fattr_frompath(path, FATTR_NOFOLLOW); 583186781Slulf } 584186781Slulf if (fa == NULL) { 585186781Slulf error = proto_printf(d->wr, "A %s\n", name); 586186781Slulf if (error) 587186781Slulf return (DETAILER_ERR_WRITE); 588186781Slulf } else if (fattr_type(fa) == FT_FILE) { 589186781Slulf if (isrcs(name, &len) && !(coll->co_options & CO_NORCS)) { 590186781Slulf detailer_dofile_rcs(d, coll, name, path); 591186781Slulf } else if (!(coll->co_options & CO_NORSYNC) && 592186781Slulf !globtree_test(coll->co_norsync, name)) { 593186781Slulf detailer_dofile_rsync(d, name, path); 594186781Slulf } else { 595186781Slulf detailer_dofile_regular(d, name, path); 596186781Slulf } 597186781Slulf } else { 598186781Slulf error = proto_printf(d->wr, "N %s\n", name); 599186781Slulf if (error) 600186781Slulf return (DETAILER_ERR_WRITE); 601186781Slulf } 602186781Slulf return (0); 603186781Slulf} 604