detailer.c revision 204556
1279377Simp/*- 2279377Simp * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> 3279377Simp * All rights reserved. 4279377Simp * 5279377Simp * Redistribution and use in source and binary forms, with or without 6279377Simp * modification, are permitted provided that the following conditions 7279377Simp * are met: 8279377Simp * 1. Redistributions of source code must retain the above copyright 9279377Simp * notice, this list of conditions and the following disclaimer. 10279377Simp * 2. Redistributions in binary form must reproduce the above copyright 11279377Simp * notice, this list of conditions and the following disclaimer in the 12279377Simp * documentation and/or other materials provided with the distribution. 13279377Simp * 14279377Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15279377Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16279377Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17279377Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18279377Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19279377Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20279377Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21279377Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22279377Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23279377Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24279377Simp * SUCH DAMAGE. 25279377Simp * 26279377Simp * $FreeBSD: head/usr.bin/csup/detailer.c 204556 2010-03-02 07:26:07Z lulf $ 27279377Simp */ 28279377Simp 29279377Simp#include <assert.h> 30279377Simp#include <errno.h> 31279377Simp#include <stdlib.h> 32279377Simp#include <string.h> 33279377Simp#include <stdio.h> 34279377Simp 35279377Simp#include <sys/types.h> 36279377Simp#include <sys/stat.h> 37279377Simp#include <unistd.h> 38279377Simp 39279377Simp#include "config.h" 40279377Simp#include "detailer.h" 41279377Simp#include "fixups.h" 42279377Simp#include "globtree.h" 43279377Simp#include "misc.h" 44279377Simp#include "mux.h" 45279377Simp#include "proto.h" 46279377Simp#include "rcsfile.h" 47279377Simp#include "rsyncfile.h" 48279377Simp#include "status.h" 49279377Simp#include "stream.h" 50279377Simp 51279377Simp/* Internal error codes. */ 52279377Simp#define DETAILER_ERR_PROTO (-1) /* Protocol error. */ 53279377Simp#define DETAILER_ERR_MSG (-2) /* Error is in detailer->errmsg. */ 54279377Simp#define DETAILER_ERR_READ (-3) /* Error reading from server. */ 55279377Simp#define DETAILER_ERR_WRITE (-4) /* Error writing to server. */ 56279377Simp 57279377Simpstruct detailer { 58279377Simp struct config *config; 59279377Simp struct stream *rd; 60279377Simp struct stream *wr; 61279377Simp char *errmsg; 62279377Simp}; 63279377Simp 64279377Simpstatic int detailer_batch(struct detailer *); 65279377Simpstatic int detailer_coll(struct detailer *, struct coll *, 66279377Simp struct status *); 67279377Simpstatic int detailer_dofile_co(struct detailer *, struct coll *, 68279377Simp struct status *, char *); 69279377Simpstatic int detailer_dofile_rcs(struct detailer *, struct coll *, 70279377Simp char *, char *); 71279377Simpstatic int detailer_dofile_regular(struct detailer *, char *, char *); 72279377Simpstatic int detailer_dofile_rsync(struct detailer *, char *, char *); 73279377Simpstatic int detailer_checkrcsattr(struct detailer *, struct coll *, char *, 74279377Simp struct fattr *, int); 75279377Simpint detailer_send_details(struct detailer *, struct coll *, char *, 76279377Simp char *, struct fattr *); 77279377Simp 78279377Simpvoid * 79279377Simpdetailer(void *arg) 80279377Simp{ 81279377Simp struct thread_args *args; 82279377Simp struct detailer dbuf, *d; 83279377Simp int error; 84279377Simp 85279377Simp args = arg; 86279377Simp 87279377Simp d = &dbuf; 88279377Simp d->config = args->config; 89279377Simp d->rd = args->rd; 90279377Simp d->wr = args->wr; 91279377Simp d->errmsg = NULL; 92279377Simp 93279377Simp error = detailer_batch(d); 94279377Simp switch (error) { 95279377Simp case DETAILER_ERR_PROTO: 96279377Simp xasprintf(&args->errmsg, "Detailer failed: Protocol error"); 97279377Simp args->status = STATUS_FAILURE; 98279377Simp break; 99279377Simp case DETAILER_ERR_MSG: 100279377Simp xasprintf(&args->errmsg, "Detailer failed: %s", d->errmsg); 101279377Simp free(d->errmsg); 102279377Simp args->status = STATUS_FAILURE; 103279377Simp break; 104279377Simp case DETAILER_ERR_READ: 105279377Simp if (stream_eof(d->rd)) { 106279377Simp xasprintf(&args->errmsg, "Detailer failed: " 107279377Simp "Premature EOF from server"); 108279377Simp } else { 109279377Simp xasprintf(&args->errmsg, "Detailer failed: " 110279377Simp "Network read failure: %s", strerror(errno)); 111279377Simp } 112279377Simp args->status = STATUS_TRANSIENTFAILURE; 113279377Simp break; 114279377Simp case DETAILER_ERR_WRITE: 115279377Simp xasprintf(&args->errmsg, "Detailer failed: " 116279377Simp "Network write failure: %s", strerror(errno)); 117279377Simp args->status = STATUS_TRANSIENTFAILURE; 118279377Simp break; 119279377Simp default: 120279377Simp assert(error == 0); 121279377Simp args->status = STATUS_SUCCESS; 122279377Simp } 123279377Simp return (NULL); 124279377Simp} 125279377Simp 126279377Simpstatic int 127279377Simpdetailer_batch(struct detailer *d) 128279377Simp{ 129279377Simp struct config *config; 130279377Simp struct stream *rd, *wr; 131279377Simp struct coll *coll; 132279377Simp struct status *st; 133279377Simp struct fixup *fixup; 134279377Simp char *cmd, *collname, *line, *release; 135279377Simp int error, fixupseof; 136279377Simp 137279377Simp config = d->config; 138279377Simp rd = d->rd; 139279377Simp wr = d->wr; 140279377Simp STAILQ_FOREACH(coll, &config->colls, co_next) { 141279377Simp if (coll->co_options & CO_SKIP) 142279377Simp continue; 143279377Simp line = stream_getln(rd, NULL); 144279377Simp cmd = proto_get_ascii(&line); 145279377Simp collname = proto_get_ascii(&line); 146279377Simp release = proto_get_ascii(&line); 147279377Simp error = proto_get_time(&line, &coll->co_scantime); 148279377Simp if (error || line != NULL || strcmp(cmd, "COLL") != 0 || 149279377Simp strcmp(collname, coll->co_name) != 0 || 150279377Simp strcmp(release, coll->co_release) != 0) 151279377Simp return (DETAILER_ERR_PROTO); 152279377Simp error = proto_printf(wr, "COLL %s %s\n", coll->co_name, 153279377Simp coll->co_release); 154279377Simp if (error) 155279377Simp return (DETAILER_ERR_WRITE); 156279377Simp stream_flush(wr); 157279377Simp if (coll->co_options & CO_COMPRESS) { 158279377Simp stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL); 159279377Simp stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL); 160279377Simp } 161279377Simp st = status_open(coll, -1, &d->errmsg); 162279377Simp if (st == NULL) 163279377Simp return (DETAILER_ERR_MSG); 164279377Simp error = detailer_coll(d, coll, st); 165279377Simp status_close(st, NULL); 166279377Simp if (error) 167279377Simp return (error); 168279377Simp if (coll->co_options & CO_COMPRESS) { 169279377Simp stream_filter_stop(rd); 170279377Simp stream_filter_stop(wr); 171279377Simp } 172279377Simp stream_flush(wr); 173279377Simp } 174279377Simp line = stream_getln(rd, NULL); 175279377Simp if (line == NULL) 176279377Simp return (DETAILER_ERR_READ); 177279377Simp if (strcmp(line, ".") != 0) 178279377Simp return (DETAILER_ERR_PROTO); 179279377Simp error = proto_printf(wr, ".\n"); 180279377Simp if (error) 181279377Simp return (DETAILER_ERR_WRITE); 182279377Simp stream_flush(wr); 183279377Simp 184279377Simp /* Now send fixups if needed. */ 185279377Simp fixup = NULL; 186279377Simp fixupseof = 0; 187279377Simp STAILQ_FOREACH(coll, &config->colls, co_next) { 188279377Simp if (coll->co_options & CO_SKIP) 189279377Simp continue; 190279377Simp error = proto_printf(wr, "COLL %s %s\n", coll->co_name, 191279377Simp coll->co_release); 192279377Simp if (error) 193279377Simp return (DETAILER_ERR_WRITE); 194279377Simp if (coll->co_options & CO_COMPRESS) 195279377Simp stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL); 196279377Simp while (!fixupseof) { 197279377Simp if (fixup == NULL) 198279377Simp fixup = fixups_get(config->fixups); 199279377Simp if (fixup == NULL) { 200279377Simp fixupseof = 1; 201279377Simp break; 202279377Simp } 203279377Simp if (fixup->f_coll != coll) 204279377Simp break; 205279377Simp if (coll->co_options & CO_CHECKOUTMODE) 206279377Simp error = proto_printf(wr, "Y %s %s %s\n", 207279377Simp fixup->f_name, coll->co_tag, coll->co_date); 208279377Simp else { 209279377Simp error = proto_printf(wr, "A %s\n", 210279377Simp fixup->f_name); 211279377Simp } 212279377Simp if (error) 213279377Simp return (DETAILER_ERR_WRITE); 214279377Simp fixup = NULL; 215279377Simp } 216279377Simp error = proto_printf(wr, ".\n"); 217279377Simp if (error) 218279377Simp return (DETAILER_ERR_WRITE); 219279377Simp if (coll->co_options & CO_COMPRESS) 220279377Simp stream_filter_stop(wr); 221279377Simp stream_flush(wr); 222279377Simp } 223279377Simp error = proto_printf(wr, ".\n"); 224279377Simp if (error) 225279377Simp return (DETAILER_ERR_WRITE); 226279377Simp return (0); 227279377Simp} 228279377Simp 229279377Simpstatic int 230279377Simpdetailer_coll(struct detailer *d, struct coll *coll, struct status *st) 231279377Simp{ 232279377Simp struct fattr *rcsattr; 233279377Simp struct stream *rd, *wr; 234279377Simp char *attr, *cmd, *file, *line, *msg, *path, *target; 235279377Simp int error, attic; 236279377Simp 237279377Simp rd = d->rd; 238279377Simp wr = d->wr; 239279377Simp attic = 0; 240279377Simp line = stream_getln(rd, NULL); 241279377Simp if (line == NULL) 242279377Simp return (DETAILER_ERR_READ); 243279377Simp while (strcmp(line, ".") != 0) { 244279377Simp cmd = proto_get_ascii(&line); 245279377Simp if (cmd == NULL || strlen(cmd) != 1) 246279377Simp return (DETAILER_ERR_PROTO); 247279377Simp switch (cmd[0]) { 248279377Simp case 'D': 249279377Simp /* Delete file. */ 250279377Simp file = proto_get_ascii(&line); 251279377Simp if (file == NULL || line != NULL) 252279377Simp return (DETAILER_ERR_PROTO); 253279377Simp error = proto_printf(wr, "D %s\n", file); 254279377Simp if (error) 255279377Simp return (DETAILER_ERR_WRITE); 256279377Simp break; 257279377Simp case 'I': 258279377Simp case 'i': 259279377Simp case 'j': 260279377Simp /* Directory operations. */ 261279377Simp file = proto_get_ascii(&line); 262279377Simp if (file == NULL || line != NULL) 263279377Simp return (DETAILER_ERR_PROTO); 264279377Simp error = proto_printf(wr, "%s %s\n", cmd, file); 265279377Simp if (error) 266279377Simp return (DETAILER_ERR_WRITE); 267279377Simp break; 268279377Simp case 'J': 269279377Simp /* Set directory attributes. */ 270279377Simp file = proto_get_ascii(&line); 271279377Simp attr = proto_get_ascii(&line); 272279377Simp if (file == NULL || line != NULL || attr == NULL) 273279377Simp return (DETAILER_ERR_PROTO); 274279377Simp error = proto_printf(wr, "%s %s %s\n", cmd, file, attr); 275279377Simp if (error) 276279377Simp return (DETAILER_ERR_WRITE); 277279377Simp break; 278279377Simp case 'H': 279279377Simp case 'h': 280279377Simp /* Create a hard link. */ 281279377Simp file = proto_get_ascii(&line); 282279377Simp target = proto_get_ascii(&line); 283279377Simp if (file == NULL || target == NULL) 284279377Simp return (DETAILER_ERR_PROTO); 285279377Simp error = proto_printf(wr, "%s %s %s\n", cmd, file, 286279377Simp target); 287279377Simp break; 288279377Simp case 't': 289279377Simp file = proto_get_ascii(&line); 290279377Simp attr = proto_get_ascii(&line); 291279377Simp if (file == NULL || attr == NULL || line != NULL) { 292279377Simp return (DETAILER_ERR_PROTO); 293279377Simp } 294279377Simp rcsattr = fattr_decode(attr); 295279377Simp if (rcsattr == NULL) { 296279377Simp return (DETAILER_ERR_PROTO); 297279377Simp } 298279377Simp error = detailer_checkrcsattr(d, coll, file, rcsattr, 299279377Simp 1); 300279377Simp break; 301279377Simp 302279377Simp case 'T': 303279377Simp file = proto_get_ascii(&line); 304279377Simp attr = proto_get_ascii(&line); 305279377Simp if (file == NULL || attr == NULL || line != NULL) 306279377Simp return (DETAILER_ERR_PROTO); 307279377Simp rcsattr = fattr_decode(attr); 308279377Simp if (rcsattr == NULL) 309279377Simp return (DETAILER_ERR_PROTO); 310279377Simp error = detailer_checkrcsattr(d, coll, file, rcsattr, 311279377Simp 0); 312279377Simp break; 313279377Simp 314279377Simp case 'U': 315279377Simp /* Add or update file. */ 316279377Simp file = proto_get_ascii(&line); 317279377Simp if (file == NULL || line != NULL) 318279377Simp return (DETAILER_ERR_PROTO); 319279377Simp if (coll->co_options & CO_CHECKOUTMODE) { 320279377Simp error = detailer_dofile_co(d, coll, st, file); 321279377Simp } else { 322279377Simp path = cvspath(coll->co_prefix, file, 0); 323279377Simp rcsattr = fattr_frompath(path, FATTR_NOFOLLOW); 324279377Simp error = detailer_send_details(d, coll, file, 325279377Simp path, rcsattr); 326279377Simp if (rcsattr != NULL) 327279377Simp fattr_free(rcsattr); 328279377Simp free(path); 329279377Simp } 330279377Simp if (error) 331279377Simp return (error); 332279377Simp break; 333279377Simp case '!': 334279377Simp /* Warning from server. */ 335279377Simp msg = proto_get_rest(&line); 336279377Simp if (msg == NULL) 337279377Simp return (DETAILER_ERR_PROTO); 338279377Simp lprintf(-1, "Server warning: %s\n", msg); 339279377Simp break; 340279377Simp default: 341279377Simp return (DETAILER_ERR_PROTO); 342279377Simp } 343279377Simp stream_flush(wr); 344279377Simp line = stream_getln(rd, NULL); 345279377Simp if (line == NULL) 346279377Simp return (DETAILER_ERR_READ); 347279377Simp } 348279377Simp error = proto_printf(wr, ".\n"); 349279377Simp if (error) 350279377Simp return (DETAILER_ERR_WRITE); 351279377Simp return (0); 352279377Simp} 353279377Simp 354279377Simp/* 355279377Simp * Tell the server to update a regular file. 356279377Simp */ 357279377Simpstatic int 358279377Simpdetailer_dofile_regular(struct detailer *d, char *name, char *path) 359279377Simp{ 360279377Simp struct stream *wr; 361279377Simp struct stat st; 362279377Simp char md5[MD5_DIGEST_SIZE]; 363279377Simp int error; 364279377Simp 365279377Simp wr = d->wr; 366279377Simp error = stat(path, &st); 367279377Simp /* If we don't have it or it's unaccessible, we want it again. */ 368279377Simp if (error) { 369279377Simp proto_printf(wr, "A %s\n", name); 370279377Simp return (0); 371279377Simp } 372279377Simp 373279377Simp /* If not, we want the file to be updated. */ 374279377Simp error = MD5_File(path, md5); 375279377Simp if (error) { 376279377Simp lprintf(-1, "Error reading \"%s\"\n", name); 377279377Simp return (error); 378279377Simp } 379279377Simp error = proto_printf(wr, "R %s %O %s\n", name, st.st_size, md5); 380279377Simp if (error) 381279377Simp return (DETAILER_ERR_WRITE); 382279377Simp return (0); 383279377Simp} 384279377Simp 385279377Simp/* 386279377Simp * Tell the server to update a file with the rsync algorithm. 387279377Simp */ 388279377Simpstatic int 389279377Simpdetailer_dofile_rsync(struct detailer *d, char *name, char *path) 390279377Simp{ 391279377Simp struct stream *wr; 392279377Simp struct rsyncfile *rf; 393279377Simp 394279377Simp wr = d->wr; 395279377Simp rf = rsync_open(path, 0, 1); 396279377Simp if (rf == NULL) { 397279377Simp /* Fallback if we fail in opening it. */ 398279377Simp proto_printf(wr, "A %s\n", name); 399279377Simp return (0); 400279377Simp } 401279377Simp proto_printf(wr, "r %s %z %z\n", name, rsync_filesize(rf), 402279377Simp rsync_blocksize(rf)); 403279377Simp /* Detail the blocks. */ 404279377Simp while (rsync_nextblock(rf) != 0) 405279377Simp proto_printf(wr, "%s %s\n", rsync_rsum(rf), rsync_blockmd5(rf)); 406279377Simp proto_printf(wr, ".\n"); 407279377Simp rsync_close(rf); 408279377Simp return (0); 409279377Simp} 410279377Simp 411279377Simp/* 412279377Simp * Tell the server to update an RCS file that we have, or send it if we don't. 413279377Simp */ 414279377Simpstatic int 415279377Simpdetailer_dofile_rcs(struct detailer *d, struct coll *coll, char *name, 416279377Simp char *path) 417279377Simp{ 418279377Simp struct stream *wr; 419279377Simp struct fattr *fa; 420279377Simp struct rcsfile *rf; 421279377Simp int error; 422279377Simp 423279377Simp wr = d->wr; 424279377Simp path = atticpath(coll->co_prefix, name); 425279377Simp fa = fattr_frompath(path, FATTR_NOFOLLOW); 426279377Simp if (fa == NULL) { 427279377Simp /* We don't have it, so send request to get it. */ 428279377Simp error = proto_printf(wr, "A %s\n", name); 429279377Simp if (error) 430279377Simp return (DETAILER_ERR_WRITE); 431279377Simp free(path); 432279377Simp return (0); 433279377Simp } 434279377Simp 435279377Simp rf = rcsfile_frompath(path, name, coll->co_cvsroot, coll->co_tag, 1); 436279377Simp free(path); 437279377Simp if (rf == NULL) { 438279377Simp error = proto_printf(wr, "A %s\n", name); 439279377Simp if (error) 440279377Simp return (DETAILER_ERR_WRITE); 441279377Simp return (0); 442279377Simp } 443279377Simp /* Tell to update the RCS file. The client version details follow. */ 444279377Simp rcsfile_send_details(rf, wr); 445279377Simp rcsfile_free(rf); 446279377Simp fattr_free(fa); 447279377Simp return (0); 448279377Simp} 449279377Simp 450279377Simpstatic int 451279377Simpdetailer_dofile_co(struct detailer *d, struct coll *coll, struct status *st, 452279377Simp char *file) 453279377Simp{ 454279377Simp struct stream *wr; 455279377Simp struct fattr *fa; 456279377Simp struct statusrec *sr; 457279377Simp char md5[MD5_DIGEST_SIZE]; 458279377Simp char *path; 459279377Simp int error, ret; 460279377Simp 461279377Simp wr = d->wr; 462279377Simp path = checkoutpath(coll->co_prefix, file); 463279377Simp if (path == NULL) 464279377Simp return (DETAILER_ERR_PROTO); 465279377Simp fa = fattr_frompath(path, FATTR_NOFOLLOW); 466279377Simp if (fa == NULL) { 467279377Simp /* We don't have the file, so the only option at this 468279377Simp point is to tell the server to send it. The server 469279377Simp may figure out that the file is dead, in which case 470279377Simp it will tell us. */ 471279377Simp error = proto_printf(wr, "C %s %s %s\n", 472279377Simp file, coll->co_tag, coll->co_date); 473279377Simp free(path); 474279377Simp if (error) 475279377Simp return (DETAILER_ERR_WRITE); 476279377Simp return (0); 477279377Simp } 478279377Simp ret = status_get(st, file, 0, 0, &sr); 479279377Simp if (ret == -1) { 480279377Simp d->errmsg = status_errmsg(st); 481279377Simp free(path); 482279377Simp return (DETAILER_ERR_MSG); 483279377Simp } 484279377Simp if (ret == 0) 485279377Simp sr = NULL; 486279377Simp 487279377Simp /* If our recorded information doesn't match the file that the 488279377Simp client has, then ignore the recorded information. */ 489279377Simp if (sr != NULL && (sr->sr_type != SR_CHECKOUTLIVE || 490279377Simp !fattr_equal(sr->sr_clientattr, fa))) 491279377Simp sr = NULL; 492279377Simp fattr_free(fa); 493279377Simp if (sr != NULL && strcmp(sr->sr_revdate, ".") != 0) { 494279377Simp error = proto_printf(wr, "U %s %s %s %s %s\n", file, 495279377Simp coll->co_tag, coll->co_date, sr->sr_revnum, sr->sr_revdate); 496279377Simp free(path); 497279377Simp if (error) 498279377Simp return (DETAILER_ERR_WRITE); 499279377Simp return (0); 500279377Simp } 501279377Simp 502279377Simp /* 503279377Simp * We don't have complete and/or accurate recorded information 504279377Simp * about what version of the file we have. Compute the file's 505279377Simp * checksum as an aid toward identifying which version it is. 506279377Simp */ 507279377Simp error = MD5_File(path, md5); 508279377Simp if (error) { 509279377Simp xasprintf(&d->errmsg, 510279377Simp "Cannot calculate checksum for \"%s\": %s", path, 511279377Simp strerror(errno)); 512279377Simp return (DETAILER_ERR_MSG); 513279377Simp } 514279377Simp free(path); 515279377Simp if (sr == NULL) { 516279377Simp error = proto_printf(wr, "S %s %s %s %s\n", file, 517279377Simp coll->co_tag, coll->co_date, md5); 518279377Simp } else { 519279377Simp error = proto_printf(wr, "s %s %s %s %s %s\n", file, 520279377Simp coll->co_tag, coll->co_date, sr->sr_revnum, md5); 521279377Simp } 522279377Simp if (error) 523279377Simp return (DETAILER_ERR_WRITE); 524279377Simp return (0); 525279377Simp} 526279377Simp 527279377Simpint 528279377Simpdetailer_checkrcsattr(struct detailer *d, struct coll *coll, char *name, 529279377Simp struct fattr *server_attr, int attic) 530279377Simp{ 531279377Simp struct fattr *client_attr; 532279377Simp char *attr, *path; 533279377Simp int error; 534279377Simp 535279377Simp /* 536279377Simp * I don't think we can use the status file, since it only records file 537279377Simp * attributes in cvsmode. 538279377Simp */ 539279377Simp client_attr = NULL; 540279377Simp path = cvspath(coll->co_prefix, name, attic); 541279377Simp if (path == NULL) { 542279377Simp return (DETAILER_ERR_PROTO); 543279377Simp } 544279377Simp 545279377Simp if (access(path, F_OK) == 0 && 546279377Simp ((client_attr = fattr_frompath(path, FATTR_NOFOLLOW)) != NULL) && 547279377Simp fattr_equal(client_attr, server_attr)) { 548279377Simp attr = fattr_encode(client_attr, NULL, 0); 549279377Simp if (attic) { 550279377Simp error = proto_printf(d->wr, "l %s %s\n", name, attr); 551279377Simp } else { 552279377Simp error = proto_printf(d->wr, "L %s %s\n", name, attr); 553279377Simp } 554279377Simp free(attr); 555279377Simp free(path); 556279377Simp fattr_free(client_attr); 557279377Simp if (error) 558279377Simp return (DETAILER_ERR_WRITE); 559279377Simp return (0); 560279377Simp } 561279377Simp /* We don't have it, so tell the server to send it. */ 562279377Simp error = detailer_send_details(d, coll, name, path, client_attr); 563279377Simp fattr_free(client_attr); 564279377Simp free(path); 565279377Simp return (error); 566279377Simp} 567279377Simp 568279377Simpint 569279377Simpdetailer_send_details(struct detailer *d, struct coll *coll, char *name, 570279377Simp char *path, struct fattr *fa) 571279377Simp{ 572279377Simp int error; 573279377Simp size_t len; 574279377Simp 575279377Simp /* 576279377Simp * Try to check if the file exists either live or dead to see if we can 577279377Simp * edit it and put it live or dead, rather than receiving the entire 578279377Simp * file. 579279377Simp */ 580279377Simp if (fa == NULL) { 581279377Simp path = atticpath(coll->co_prefix, name); 582279377Simp fa = fattr_frompath(path, FATTR_NOFOLLOW); 583279377Simp } 584279377Simp if (fa == NULL) { 585279377Simp error = proto_printf(d->wr, "A %s\n", name); 586279377Simp if (error) 587279377Simp return (DETAILER_ERR_WRITE); 588279377Simp } else if (fattr_type(fa) == FT_FILE) { 589279377Simp if (isrcs(name, &len) && !(coll->co_options & CO_NORCS)) { 590279377Simp detailer_dofile_rcs(d, coll, name, path); 591279377Simp } else if (!(coll->co_options & CO_NORSYNC) && 592279377Simp !globtree_test(coll->co_norsync, name)) { 593279377Simp detailer_dofile_rsync(d, name, path); 594279377Simp } else { 595279377Simp detailer_dofile_regular(d, name, path); 596279377Simp } 597279377Simp } else { 598279377Simp error = proto_printf(d->wr, "N %s\n", name); 599279377Simp if (error) 600279377Simp return (DETAILER_ERR_WRITE); 601279377Simp } 602279377Simp return (0); 603279377Simp} 604279377Simp