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 <sys/types.h> 30156230Smux#include <sys/stat.h> 31156230Smux 32156230Smux#include <assert.h> 33186781Slulf#include <err.h> 34156230Smux#include <errno.h> 35156230Smux#include <fcntl.h> 36156230Smux#include <stddef.h> 37156230Smux#include <stdio.h> 38156230Smux#include <stdlib.h> 39156230Smux#include <string.h> 40156230Smux#include <unistd.h> 41156230Smux 42156230Smux#include "config.h" 43156230Smux#include "diff.h" 44156230Smux#include "fattr.h" 45156230Smux#include "fixups.h" 46156230Smux#include "keyword.h" 47156230Smux#include "updater.h" 48156230Smux#include "misc.h" 49156230Smux#include "mux.h" 50156230Smux#include "proto.h" 51186781Slulf#include "rcsfile.h" 52156230Smux#include "status.h" 53156230Smux#include "stream.h" 54156230Smux 55156230Smux/* Internal error codes. */ 56156230Smux#define UPDATER_ERR_PROTO (-1) /* Protocol error. */ 57156230Smux#define UPDATER_ERR_MSG (-2) /* Error is in updater->errmsg. */ 58156230Smux#define UPDATER_ERR_READ (-3) /* Error reading from server. */ 59156701Smux#define UPDATER_ERR_DELETELIM (-4) /* File deletion limit exceeded. */ 60156230Smux 61186781Slulf#define BUFSIZE 4096 62186781Slulf 63156230Smux/* Everything needed to update a file. */ 64156230Smuxstruct file_update { 65156230Smux struct statusrec srbuf; 66156230Smux char *destpath; 67156701Smux char *temppath; 68186781Slulf char *origpath; 69156230Smux char *coname; /* Points somewhere in destpath. */ 70156230Smux char *wantmd5; 71156230Smux struct coll *coll; 72156230Smux struct status *st; 73156230Smux /* Those are only used for diff updating. */ 74156230Smux char *author; 75156230Smux struct stream *orig; 76156230Smux struct stream *to; 77186781Slulf int attic; 78156230Smux int expand; 79156230Smux}; 80156230Smux 81156230Smuxstruct updater { 82156230Smux struct config *config; 83156230Smux struct stream *rd; 84156230Smux char *errmsg; 85156701Smux int deletecount; 86156230Smux}; 87156230Smux 88156230Smuxstatic struct file_update *fup_new(struct coll *, struct status *); 89186781Slulfstatic int fup_prepare(struct file_update *, char *, int); 90156230Smuxstatic void fup_cleanup(struct file_update *); 91156230Smuxstatic void fup_free(struct file_update *); 92156230Smux 93156230Smuxstatic void updater_prunedirs(char *, char *); 94156230Smuxstatic int updater_batch(struct updater *, int); 95156230Smuxstatic int updater_docoll(struct updater *, struct file_update *, int); 96156701Smuxstatic int updater_delete(struct updater *, struct file_update *); 97156701Smuxstatic void updater_deletefile(const char *); 98156230Smuxstatic int updater_checkout(struct updater *, struct file_update *, int); 99186781Slulfstatic int updater_addfile(struct updater *, struct file_update *, 100186781Slulf char *, int); 101186781Slulfint updater_addelta(struct rcsfile *, struct stream *, char *); 102156230Smuxstatic int updater_setattrs(struct updater *, struct file_update *, 103156230Smux char *, char *, char *, char *, char *, struct fattr *); 104186781Slulfstatic int updater_setdirattrs(struct updater *, struct coll *, 105186781Slulf struct file_update *, char *, char *); 106156701Smuxstatic int updater_updatefile(struct updater *, struct file_update *fup, 107156230Smux const char *, int); 108186781Slulfstatic int updater_updatenode(struct updater *, struct coll *, 109186781Slulf struct file_update *, char *, char *); 110156230Smuxstatic int updater_diff(struct updater *, struct file_update *); 111156230Smuxstatic int updater_diff_batch(struct updater *, struct file_update *); 112156230Smuxstatic int updater_diff_apply(struct updater *, struct file_update *, 113156230Smux char *); 114186781Slulfstatic int updater_rcsedit(struct updater *, struct file_update *, char *, 115186781Slulf char *); 116186781Slulfint updater_append_file(struct updater *, struct file_update *, 117186781Slulf off_t); 118186781Slulfstatic int updater_rsync(struct updater *, struct file_update *, size_t); 119186781Slulfstatic int updater_read_checkout(struct stream *, struct stream *); 120156230Smux 121156230Smuxstatic struct file_update * 122156230Smuxfup_new(struct coll *coll, struct status *st) 123156230Smux{ 124156230Smux struct file_update *fup; 125156230Smux 126156230Smux fup = xmalloc(sizeof(struct file_update)); 127156230Smux memset(fup, 0, sizeof(*fup)); 128156230Smux fup->coll = coll; 129156230Smux fup->st = st; 130156230Smux return (fup); 131156230Smux} 132156230Smux 133156230Smuxstatic int 134186781Slulffup_prepare(struct file_update *fup, char *name, int attic) 135156230Smux{ 136156230Smux struct coll *coll; 137156230Smux 138156230Smux coll = fup->coll; 139186781Slulf fup->attic = 0; 140186781Slulf fup->origpath = NULL; 141186781Slulf 142186781Slulf if (coll->co_options & CO_CHECKOUTMODE) 143186781Slulf fup->destpath = checkoutpath(coll->co_prefix, name); 144186781Slulf else { 145186781Slulf fup->destpath = cvspath(coll->co_prefix, name, attic); 146186781Slulf fup->origpath = atticpath(coll->co_prefix, name); 147186781Slulf /* If they're equal, we don't need special care. */ 148186781Slulf if (fup->origpath != NULL && 149186781Slulf strcmp(fup->origpath, fup->destpath) == 0) { 150186781Slulf free(fup->origpath); 151186781Slulf fup->origpath = NULL; 152186781Slulf } 153186781Slulf fup->attic = attic; 154186781Slulf } 155156230Smux if (fup->destpath == NULL) 156156230Smux return (-1); 157156230Smux fup->coname = fup->destpath + coll->co_prefixlen + 1; 158156230Smux return (0); 159156230Smux} 160156230Smux 161156230Smux/* Called after each file update to reinit the structure. */ 162156230Smuxstatic void 163156230Smuxfup_cleanup(struct file_update *fup) 164156230Smux{ 165156230Smux struct statusrec *sr; 166156230Smux 167156230Smux sr = &fup->srbuf; 168156230Smux 169156230Smux if (fup->destpath != NULL) { 170156230Smux free(fup->destpath); 171156230Smux fup->destpath = NULL; 172156230Smux } 173156701Smux if (fup->temppath != NULL) { 174156701Smux free(fup->temppath); 175156701Smux fup->temppath = NULL; 176156701Smux } 177186781Slulf if (fup->origpath != NULL) { 178186781Slulf free(fup->origpath); 179186781Slulf fup->origpath = NULL; 180186781Slulf } 181156230Smux fup->coname = NULL; 182156230Smux if (fup->author != NULL) { 183156230Smux free(fup->author); 184156230Smux fup->author = NULL; 185156230Smux } 186156230Smux fup->expand = 0; 187156230Smux if (fup->wantmd5 != NULL) { 188156230Smux free(fup->wantmd5); 189156230Smux fup->wantmd5 = NULL; 190156230Smux } 191156230Smux if (fup->orig != NULL) { 192156230Smux stream_close(fup->orig); 193156230Smux fup->orig = NULL; 194156230Smux } 195156230Smux if (fup->to != NULL) { 196156230Smux stream_close(fup->to); 197156230Smux fup->to = NULL; 198156230Smux } 199156230Smux if (sr->sr_file != NULL) 200156230Smux free(sr->sr_file); 201156230Smux if (sr->sr_tag != NULL) 202156230Smux free(sr->sr_tag); 203156230Smux if (sr->sr_date != NULL) 204156230Smux free(sr->sr_date); 205156230Smux if (sr->sr_revnum != NULL) 206156230Smux free(sr->sr_revnum); 207156230Smux if (sr->sr_revdate != NULL) 208156230Smux free(sr->sr_revdate); 209156230Smux fattr_free(sr->sr_clientattr); 210156230Smux fattr_free(sr->sr_serverattr); 211156230Smux memset(sr, 0, sizeof(*sr)); 212156230Smux} 213156230Smux 214156230Smuxstatic void 215156230Smuxfup_free(struct file_update *fup) 216156230Smux{ 217156230Smux 218156230Smux fup_cleanup(fup); 219156230Smux free(fup); 220156230Smux} 221156230Smux 222156230Smuxvoid * 223156230Smuxupdater(void *arg) 224156230Smux{ 225156230Smux struct thread_args *args; 226156230Smux struct updater upbuf, *up; 227156230Smux int error; 228156230Smux 229156230Smux args = arg; 230156230Smux 231156230Smux up = &upbuf; 232156230Smux up->config = args->config; 233156230Smux up->rd = args->rd; 234156230Smux up->errmsg = NULL; 235156701Smux up->deletecount = 0; 236156230Smux 237156230Smux error = updater_batch(up, 0); 238156230Smux 239156230Smux /* 240156230Smux * Make sure to close the fixups even in case of an error, 241225979Sadrian * so that the detailer thread doesn't block indefinitely. 242156230Smux */ 243156230Smux fixups_close(up->config->fixups); 244156230Smux if (!error) 245156230Smux error = updater_batch(up, 1); 246156230Smux switch (error) { 247156230Smux case UPDATER_ERR_PROTO: 248156230Smux xasprintf(&args->errmsg, "Updater failed: Protocol error"); 249156230Smux args->status = STATUS_FAILURE; 250156230Smux break; 251156230Smux case UPDATER_ERR_MSG: 252156230Smux xasprintf(&args->errmsg, "Updater failed: %s", up->errmsg); 253156230Smux free(up->errmsg); 254156230Smux args->status = STATUS_FAILURE; 255156230Smux break; 256156230Smux case UPDATER_ERR_READ: 257156230Smux if (stream_eof(up->rd)) { 258156230Smux xasprintf(&args->errmsg, "Updater failed: " 259156230Smux "Premature EOF from server"); 260156230Smux } else { 261156230Smux xasprintf(&args->errmsg, "Updater failed: " 262156230Smux "Network read failure: %s", strerror(errno)); 263156230Smux } 264156230Smux args->status = STATUS_TRANSIENTFAILURE; 265156230Smux break; 266156701Smux case UPDATER_ERR_DELETELIM: 267156701Smux xasprintf(&args->errmsg, "Updater failed: " 268156701Smux "File deletion limit exceeded"); 269156701Smux args->status = STATUS_FAILURE; 270156701Smux break; 271156230Smux default: 272156230Smux assert(error == 0); 273156230Smux args->status = STATUS_SUCCESS; 274156230Smux }; 275156230Smux return (NULL); 276156230Smux} 277156230Smux 278156230Smuxstatic int 279156230Smuxupdater_batch(struct updater *up, int isfixups) 280156230Smux{ 281156230Smux struct stream *rd; 282156230Smux struct coll *coll; 283156230Smux struct status *st; 284156230Smux struct file_update *fup; 285156230Smux char *line, *cmd, *errmsg, *collname, *release; 286156230Smux int error; 287156230Smux 288156230Smux rd = up->rd; 289156230Smux STAILQ_FOREACH(coll, &up->config->colls, co_next) { 290156230Smux if (coll->co_options & CO_SKIP) 291156230Smux continue; 292156230Smux umask(coll->co_umask); 293156230Smux line = stream_getln(rd, NULL); 294156230Smux if (line == NULL) 295156230Smux return (UPDATER_ERR_READ); 296156230Smux cmd = proto_get_ascii(&line); 297156230Smux collname = proto_get_ascii(&line); 298156230Smux release = proto_get_ascii(&line); 299156230Smux if (release == NULL || line != NULL) 300156230Smux return (UPDATER_ERR_PROTO); 301156230Smux if (strcmp(cmd, "COLL") != 0 || 302156230Smux strcmp(collname, coll->co_name) != 0 || 303156230Smux strcmp(release, coll->co_release) != 0) 304156230Smux return (UPDATER_ERR_PROTO); 305156230Smux 306156230Smux if (!isfixups) 307156230Smux lprintf(1, "Updating collection %s/%s\n", coll->co_name, 308156230Smux coll->co_release); 309156230Smux 310156230Smux if (coll->co_options & CO_COMPRESS) 311156230Smux stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL); 312156230Smux 313156230Smux st = status_open(coll, coll->co_scantime, &errmsg); 314156230Smux if (st == NULL) { 315156230Smux up->errmsg = errmsg; 316156230Smux return (UPDATER_ERR_MSG); 317156230Smux } 318156701Smux fup = fup_new(coll, st); 319156230Smux error = updater_docoll(up, fup, isfixups); 320156230Smux status_close(st, &errmsg); 321156230Smux fup_free(fup); 322156230Smux if (errmsg != NULL) { 323156230Smux /* Discard previous error. */ 324156230Smux if (up->errmsg != NULL) 325156230Smux free(up->errmsg); 326156230Smux up->errmsg = errmsg; 327156230Smux return (UPDATER_ERR_MSG); 328156230Smux } 329156230Smux if (error) 330156230Smux return (error); 331156230Smux 332156230Smux if (coll->co_options & CO_COMPRESS) 333156230Smux stream_filter_stop(rd); 334156230Smux } 335156230Smux line = stream_getln(rd, NULL); 336156230Smux if (line == NULL) 337156230Smux return (UPDATER_ERR_READ); 338156230Smux if (strcmp(line, ".") != 0) 339156230Smux return (UPDATER_ERR_PROTO); 340156230Smux return (0); 341156230Smux} 342156230Smux 343156230Smuxstatic int 344156230Smuxupdater_docoll(struct updater *up, struct file_update *fup, int isfixups) 345156230Smux{ 346156230Smux struct stream *rd; 347156230Smux struct coll *coll; 348156230Smux struct statusrec srbuf, *sr; 349156230Smux struct fattr *rcsattr, *tmp; 350186781Slulf char *attr, *cmd, *blocksize, *line, *msg; 351156230Smux char *name, *tag, *date, *revdate; 352156230Smux char *expand, *wantmd5, *revnum; 353186781Slulf char *optstr, *rcsopt, *pos; 354156230Smux time_t t; 355186781Slulf off_t position; 356186781Slulf int attic, error, needfixupmsg; 357156230Smux 358156230Smux error = 0; 359156230Smux rd = up->rd; 360156230Smux coll = fup->coll; 361156230Smux needfixupmsg = isfixups; 362156230Smux while ((line = stream_getln(rd, NULL)) != NULL) { 363156230Smux if (strcmp(line, ".") == 0) 364156230Smux break; 365156230Smux memset(&srbuf, 0, sizeof(srbuf)); 366156230Smux if (needfixupmsg) { 367156230Smux lprintf(1, "Applying fixups for collection %s/%s\n", 368156230Smux coll->co_name, coll->co_release); 369156230Smux needfixupmsg = 0; 370156230Smux } 371156230Smux cmd = proto_get_ascii(&line); 372156230Smux if (cmd == NULL || strlen(cmd) != 1) 373156230Smux return (UPDATER_ERR_PROTO); 374156230Smux switch (cmd[0]) { 375156230Smux case 'T': 376156230Smux /* Update recorded information for checked-out file. */ 377156230Smux name = proto_get_ascii(&line); 378156230Smux tag = proto_get_ascii(&line); 379156230Smux date = proto_get_ascii(&line); 380156230Smux revnum = proto_get_ascii(&line); 381156230Smux revdate = proto_get_ascii(&line); 382156230Smux attr = proto_get_ascii(&line); 383156230Smux if (attr == NULL || line != NULL) 384156230Smux return (UPDATER_ERR_PROTO); 385156230Smux 386156230Smux rcsattr = fattr_decode(attr); 387156230Smux if (rcsattr == NULL) 388156230Smux return (UPDATER_ERR_PROTO); 389156230Smux 390186781Slulf error = fup_prepare(fup, name, 0); 391156230Smux if (error) 392156230Smux return (UPDATER_ERR_PROTO); 393156230Smux error = updater_setattrs(up, fup, name, tag, date, 394156230Smux revnum, revdate, rcsattr); 395156230Smux fattr_free(rcsattr); 396156230Smux if (error) 397156230Smux return (error); 398156230Smux break; 399156230Smux case 'c': 400156230Smux /* Checkout dead file. */ 401156230Smux name = proto_get_ascii(&line); 402156230Smux tag = proto_get_ascii(&line); 403156230Smux date = proto_get_ascii(&line); 404156230Smux attr = proto_get_ascii(&line); 405156230Smux if (attr == NULL || line != NULL) 406156230Smux return (UPDATER_ERR_PROTO); 407156230Smux 408186781Slulf error = fup_prepare(fup, name, 0); 409156230Smux if (error) 410156230Smux return (UPDATER_ERR_PROTO); 411156230Smux /* Theoritically, the file does not exist on the client. 412156230Smux Just to make sure, we'll delete it here, if it 413156230Smux exists. */ 414156701Smux if (access(fup->destpath, F_OK) == 0) { 415156701Smux error = updater_delete(up, fup); 416156701Smux if (error) 417156701Smux return (error); 418156701Smux } 419156230Smux 420156230Smux sr = &srbuf; 421156230Smux sr->sr_type = SR_CHECKOUTDEAD; 422156230Smux sr->sr_file = name; 423156230Smux sr->sr_tag = tag; 424156230Smux sr->sr_date = date; 425156230Smux sr->sr_serverattr = fattr_decode(attr); 426156230Smux if (sr->sr_serverattr == NULL) 427156230Smux return (UPDATER_ERR_PROTO); 428156230Smux 429156230Smux error = status_put(fup->st, sr); 430156230Smux fattr_free(sr->sr_serverattr); 431156230Smux if (error) { 432156230Smux up->errmsg = status_errmsg(fup->st); 433156230Smux return (UPDATER_ERR_MSG); 434156230Smux } 435156230Smux break; 436156230Smux case 'U': 437156230Smux /* Update live checked-out file. */ 438156230Smux name = proto_get_ascii(&line); 439156230Smux tag = proto_get_ascii(&line); 440156230Smux date = proto_get_ascii(&line); 441156230Smux proto_get_ascii(&line); /* XXX - oldRevNum */ 442156230Smux proto_get_ascii(&line); /* XXX - fromAttic */ 443156230Smux proto_get_ascii(&line); /* XXX - logLines */ 444156230Smux expand = proto_get_ascii(&line); 445156230Smux attr = proto_get_ascii(&line); 446156230Smux wantmd5 = proto_get_ascii(&line); 447156230Smux if (wantmd5 == NULL || line != NULL) 448156230Smux return (UPDATER_ERR_PROTO); 449156230Smux 450156230Smux sr = &fup->srbuf; 451156230Smux sr->sr_type = SR_CHECKOUTLIVE; 452156230Smux sr->sr_file = xstrdup(name); 453156230Smux sr->sr_date = xstrdup(date); 454156230Smux sr->sr_tag = xstrdup(tag); 455156230Smux sr->sr_serverattr = fattr_decode(attr); 456156230Smux if (sr->sr_serverattr == NULL) 457156230Smux return (UPDATER_ERR_PROTO); 458156230Smux 459156230Smux fup->expand = keyword_decode_expand(expand); 460156230Smux if (fup->expand == -1) 461156230Smux return (UPDATER_ERR_PROTO); 462186781Slulf error = fup_prepare(fup, name, 0); 463156230Smux if (error) 464156230Smux return (UPDATER_ERR_PROTO); 465156230Smux 466156230Smux fup->wantmd5 = xstrdup(wantmd5); 467156701Smux fup->temppath = tempname(fup->destpath); 468156230Smux error = updater_diff(up, fup); 469156230Smux if (error) 470156230Smux return (error); 471156230Smux break; 472156230Smux case 'u': 473156230Smux /* Update dead checked-out file. */ 474156230Smux name = proto_get_ascii(&line); 475156230Smux tag = proto_get_ascii(&line); 476156230Smux date = proto_get_ascii(&line); 477156230Smux attr = proto_get_ascii(&line); 478156230Smux if (attr == NULL || line != NULL) 479156230Smux return (UPDATER_ERR_PROTO); 480156230Smux 481186781Slulf error = fup_prepare(fup, name, 0); 482156230Smux if (error) 483156230Smux return (UPDATER_ERR_PROTO); 484156701Smux error = updater_delete(up, fup); 485156701Smux if (error) 486156701Smux return (error); 487156230Smux sr = &srbuf; 488156230Smux sr->sr_type = SR_CHECKOUTDEAD; 489156230Smux sr->sr_file = name; 490156230Smux sr->sr_tag = tag; 491156230Smux sr->sr_date = date; 492156230Smux sr->sr_serverattr = fattr_decode(attr); 493156230Smux if (sr->sr_serverattr == NULL) 494156230Smux return (UPDATER_ERR_PROTO); 495156230Smux error = status_put(fup->st, sr); 496156230Smux fattr_free(sr->sr_serverattr); 497156230Smux if (error) { 498156230Smux up->errmsg = status_errmsg(fup->st); 499156230Smux return (UPDATER_ERR_MSG); 500156230Smux } 501156230Smux break; 502156230Smux case 'C': 503156230Smux case 'Y': 504156230Smux /* Checkout file. */ 505156230Smux name = proto_get_ascii(&line); 506156230Smux tag = proto_get_ascii(&line); 507156230Smux date = proto_get_ascii(&line); 508156230Smux revnum = proto_get_ascii(&line); 509156230Smux revdate = proto_get_ascii(&line); 510156230Smux attr = proto_get_ascii(&line); 511156230Smux if (attr == NULL || line != NULL) 512156230Smux return (UPDATER_ERR_PROTO); 513156230Smux 514156230Smux sr = &fup->srbuf; 515156230Smux sr->sr_type = SR_CHECKOUTLIVE; 516156230Smux sr->sr_file = xstrdup(name); 517156230Smux sr->sr_tag = xstrdup(tag); 518156230Smux sr->sr_date = xstrdup(date); 519156230Smux sr->sr_revnum = xstrdup(revnum); 520156230Smux sr->sr_revdate = xstrdup(revdate); 521156230Smux sr->sr_serverattr = fattr_decode(attr); 522156230Smux if (sr->sr_serverattr == NULL) 523156230Smux return (UPDATER_ERR_PROTO); 524156230Smux 525156230Smux t = rcsdatetotime(revdate); 526156230Smux if (t == -1) 527156230Smux return (UPDATER_ERR_PROTO); 528156230Smux 529156230Smux sr->sr_clientattr = fattr_new(FT_FILE, t); 530156230Smux tmp = fattr_forcheckout(sr->sr_serverattr, 531156230Smux coll->co_umask); 532156230Smux fattr_override(sr->sr_clientattr, tmp, FA_MASK); 533156230Smux fattr_free(tmp); 534156230Smux fattr_mergedefault(sr->sr_clientattr); 535186781Slulf error = fup_prepare(fup, name, 0); 536156230Smux if (error) 537156230Smux return (UPDATER_ERR_PROTO); 538156701Smux fup->temppath = tempname(fup->destpath); 539156230Smux if (*cmd == 'Y') 540156230Smux error = updater_checkout(up, fup, 1); 541156230Smux else 542156230Smux error = updater_checkout(up, fup, 0); 543156230Smux if (error) 544156230Smux return (error); 545156230Smux break; 546156230Smux case 'D': 547156230Smux /* Delete file. */ 548156230Smux name = proto_get_ascii(&line); 549156230Smux if (name == NULL || line != NULL) 550156230Smux return (UPDATER_ERR_PROTO); 551186781Slulf error = fup_prepare(fup, name, 0); 552156230Smux if (error) 553156230Smux return (UPDATER_ERR_PROTO); 554156701Smux error = updater_delete(up, fup); 555156701Smux if (error) 556156701Smux return (error); 557156230Smux error = status_delete(fup->st, name, 0); 558156230Smux if (error) { 559156230Smux up->errmsg = status_errmsg(fup->st); 560156230Smux return (UPDATER_ERR_MSG); 561156230Smux } 562156230Smux break; 563186781Slulf case 'A': 564186781Slulf case 'a': 565186781Slulf case 'R': 566186781Slulf name = proto_get_ascii(&line); 567186781Slulf attr = proto_get_ascii(&line); 568186781Slulf if (name == NULL || attr == NULL || line != NULL) 569186781Slulf return (UPDATER_ERR_PROTO); 570186781Slulf attic = (cmd[0] == 'a'); 571186781Slulf error = fup_prepare(fup, name, attic); 572186781Slulf if (error) 573186781Slulf return (UPDATER_ERR_PROTO); 574186781Slulf 575186781Slulf fup->temppath = tempname(fup->destpath); 576186781Slulf sr = &fup->srbuf; 577186781Slulf sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; 578186781Slulf sr->sr_file = xstrdup(name); 579186781Slulf sr->sr_serverattr = fattr_decode(attr); 580186781Slulf if (sr->sr_serverattr == NULL) 581186781Slulf return (UPDATER_ERR_PROTO); 582186781Slulf if (attic) 583186781Slulf lprintf(1, " Create %s -> Attic\n", name); 584186781Slulf else 585186781Slulf lprintf(1, " Create %s\n", name); 586186781Slulf error = updater_addfile(up, fup, attr, 0); 587186781Slulf if (error) 588186781Slulf return (error); 589186781Slulf break; 590186781Slulf case 'r': 591186781Slulf name = proto_get_ascii(&line); 592186781Slulf attr = proto_get_ascii(&line); 593186781Slulf blocksize = proto_get_ascii(&line); 594186781Slulf wantmd5 = proto_get_ascii(&line); 595186781Slulf if (name == NULL || attr == NULL || blocksize == NULL || 596186781Slulf wantmd5 == NULL) { 597186781Slulf return (UPDATER_ERR_PROTO); 598186781Slulf } 599186781Slulf error = fup_prepare(fup, name, 0); 600186781Slulf if (error) 601186781Slulf return (UPDATER_ERR_PROTO); 602186781Slulf fup->wantmd5 = xstrdup(wantmd5); 603186781Slulf fup->temppath = tempname(fup->destpath); 604186781Slulf sr = &fup->srbuf; 605186781Slulf sr->sr_file = xstrdup(name); 606186781Slulf sr->sr_serverattr = fattr_decode(attr); 607186781Slulf sr->sr_type = SR_FILELIVE; 608186781Slulf if (sr->sr_serverattr == NULL) 609186781Slulf return (UPDATER_ERR_PROTO); 610186781Slulf error = updater_rsync(up, fup, strtol(blocksize, NULL, 611186781Slulf 10)); 612186781Slulf if (error) 613186781Slulf return (error); 614186781Slulf break; 615186781Slulf case 'I': 616186781Slulf /* 617186781Slulf * Create directory and add DirDown entry in status 618186781Slulf * file. 619186781Slulf */ 620186781Slulf name = proto_get_ascii(&line); 621186781Slulf if (name == NULL || line != NULL) 622186781Slulf return (UPDATER_ERR_PROTO); 623186781Slulf error = fup_prepare(fup, name, 0); 624186781Slulf if (error) 625186781Slulf return (UPDATER_ERR_PROTO); 626186781Slulf sr = &fup->srbuf; 627186781Slulf sr->sr_type = SR_DIRDOWN; 628186781Slulf sr->sr_file = xstrdup(name); 629186781Slulf sr->sr_serverattr = NULL; 630186781Slulf sr->sr_clientattr = fattr_new(FT_DIRECTORY, -1); 631186781Slulf fattr_mergedefault(sr->sr_clientattr); 632186781Slulf 633186781Slulf error = mkdirhier(fup->destpath, coll->co_umask); 634186781Slulf if (error) 635186781Slulf return (UPDATER_ERR_PROTO); 636186781Slulf if (access(fup->destpath, F_OK) != 0) { 637186781Slulf lprintf(1, " Mkdir %s\n", name); 638186781Slulf error = fattr_makenode(sr->sr_clientattr, 639186781Slulf fup->destpath); 640186781Slulf if (error) 641186781Slulf return (UPDATER_ERR_PROTO); 642186781Slulf } 643186781Slulf error = status_put(fup->st, sr); 644186781Slulf if (error) { 645186781Slulf up->errmsg = status_errmsg(fup->st); 646186781Slulf return (UPDATER_ERR_MSG); 647186781Slulf } 648186781Slulf break; 649186781Slulf case 'i': 650186781Slulf /* Remove DirDown entry in status file. */ 651186781Slulf name = proto_get_ascii(&line); 652186781Slulf if (name == NULL || line != NULL) 653186781Slulf return (UPDATER_ERR_PROTO); 654186781Slulf error = fup_prepare(fup, name, 0); 655186781Slulf if (error) 656186781Slulf return (UPDATER_ERR_PROTO); 657186781Slulf error = status_delete(fup->st, name, 0); 658186781Slulf if (error) { 659186781Slulf up->errmsg = status_errmsg(fup->st); 660186781Slulf return (UPDATER_ERR_MSG); 661186781Slulf } 662186781Slulf break; 663186781Slulf case 'J': 664186781Slulf /* 665186781Slulf * Set attributes of directory and update DirUp entry in 666186781Slulf * status file. 667186781Slulf */ 668186781Slulf name = proto_get_ascii(&line); 669186781Slulf if (name == NULL) 670186781Slulf return (UPDATER_ERR_PROTO); 671186781Slulf attr = proto_get_ascii(&line); 672186781Slulf if (attr == NULL || line != NULL) 673186781Slulf return (UPDATER_ERR_PROTO); 674186781Slulf error = fup_prepare(fup, name, 0); 675186781Slulf if (error) 676186781Slulf return (UPDATER_ERR_PROTO); 677186781Slulf error = updater_setdirattrs(up, coll, fup, name, attr); 678186781Slulf if (error) 679186781Slulf return (error); 680186781Slulf break; 681186781Slulf case 'j': 682186781Slulf /* 683186781Slulf * Remove directory and delete its DirUp entry in status 684186781Slulf * file. 685186781Slulf */ 686186781Slulf name = proto_get_ascii(&line); 687186781Slulf if (name == NULL || line != NULL) 688186781Slulf return (UPDATER_ERR_PROTO); 689186781Slulf error = fup_prepare(fup, name, 0); 690186781Slulf if (error) 691186781Slulf return (UPDATER_ERR_PROTO); 692186781Slulf lprintf(1, " Rmdir %s\n", name); 693186781Slulf updater_deletefile(fup->destpath); 694186781Slulf error = status_delete(fup->st, name, 0); 695186781Slulf if (error) { 696186781Slulf up->errmsg = status_errmsg(fup->st); 697186781Slulf return (UPDATER_ERR_MSG); 698186781Slulf } 699186781Slulf break; 700186781Slulf case 'L': 701186781Slulf case 'l': 702186781Slulf name = proto_get_ascii(&line); 703186781Slulf if (name == NULL) 704186781Slulf return (UPDATER_ERR_PROTO); 705186781Slulf attr = proto_get_ascii(&line); 706186781Slulf if (attr == NULL || line != NULL) 707186781Slulf return (UPDATER_ERR_PROTO); 708186781Slulf attic = (cmd[0] == 'l'); 709186781Slulf sr = &fup->srbuf; 710186781Slulf sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; 711186781Slulf sr->sr_file = xstrdup(name); 712186781Slulf sr->sr_serverattr = fattr_decode(attr); 713186781Slulf sr->sr_clientattr = fattr_decode(attr); 714186781Slulf if (sr->sr_serverattr == NULL || 715186781Slulf sr->sr_clientattr == NULL) 716186781Slulf return (UPDATER_ERR_PROTO); 717186781Slulf 718186781Slulf /* Save space. Described in detail in updatefile. */ 719186781Slulf if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) 720186781Slulf || fattr_getlinkcount(sr->sr_clientattr) <= 1) 721186781Slulf fattr_maskout(sr->sr_clientattr, 722186781Slulf FA_DEV | FA_INODE); 723186781Slulf fattr_maskout(sr->sr_clientattr, FA_FLAGS); 724186781Slulf error = status_put(fup->st, sr); 725186781Slulf if (error) { 726186781Slulf up->errmsg = status_errmsg(fup->st); 727186781Slulf return (UPDATER_ERR_MSG); 728186781Slulf } 729186781Slulf break; 730186781Slulf case 'N': 731186781Slulf case 'n': 732186781Slulf name = proto_get_ascii(&line); 733186781Slulf attr = proto_get_ascii(&line); 734186781Slulf if (name == NULL || attr == NULL || line != NULL) 735186781Slulf return (UPDATER_ERR_PROTO); 736186781Slulf attic = (cmd[0] == 'n'); 737186781Slulf error = fup_prepare(fup, name, attic); 738186781Slulf if (error) 739186781Slulf return (UPDATER_ERR_PROTO); 740186781Slulf sr = &fup->srbuf; 741186781Slulf sr->sr_type = (attic ? SR_FILEDEAD : SR_FILELIVE); 742186781Slulf sr->sr_file = xstrdup(name); 743186781Slulf sr->sr_serverattr = fattr_decode(attr); 744186781Slulf sr->sr_clientattr = fattr_new(FT_SYMLINK, -1); 745186781Slulf fattr_mergedefault(sr->sr_clientattr); 746186781Slulf fattr_maskout(sr->sr_clientattr, FA_FLAGS); 747186781Slulf error = updater_updatenode(up, coll, fup, name, attr); 748186781Slulf if (error) 749186781Slulf return (error); 750186781Slulf break; 751186781Slulf case 'V': 752186781Slulf case 'v': 753186781Slulf name = proto_get_ascii(&line); 754186781Slulf attr = proto_get_ascii(&line); 755186781Slulf optstr = proto_get_ascii(&line); 756186781Slulf wantmd5 = proto_get_ascii(&line); 757186781Slulf rcsopt = NULL; /* XXX: Not supported. */ 758186781Slulf if (attr == NULL || line != NULL || wantmd5 == NULL) 759186781Slulf return (UPDATER_ERR_PROTO); 760186781Slulf attic = (cmd[0] == 'v'); 761186781Slulf error = fup_prepare(fup, name, attic); 762186781Slulf if (error) 763186781Slulf return (UPDATER_ERR_PROTO); 764186781Slulf fup->temppath = tempname(fup->destpath); 765186781Slulf fup->wantmd5 = xstrdup(wantmd5); 766186781Slulf sr = &fup->srbuf; 767186781Slulf sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; 768186781Slulf sr->sr_file = xstrdup(name); 769186781Slulf sr->sr_serverattr = fattr_decode(attr); 770186781Slulf if (sr->sr_serverattr == NULL) 771186781Slulf return (UPDATER_ERR_PROTO); 772186781Slulf 773186781Slulf error = updater_rcsedit(up, fup, name, rcsopt); 774186781Slulf if (error) 775186781Slulf return (error); 776186781Slulf break; 777186781Slulf case 'X': 778186781Slulf case 'x': 779186781Slulf name = proto_get_ascii(&line); 780186781Slulf attr = proto_get_ascii(&line); 781186781Slulf if (name == NULL || attr == NULL || line != NULL) 782186781Slulf return (UPDATER_ERR_PROTO); 783186781Slulf attic = (cmd[0] == 'x'); 784186781Slulf error = fup_prepare(fup, name, attic); 785186781Slulf if (error) 786186781Slulf return (UPDATER_ERR_PROTO); 787186781Slulf 788186781Slulf fup->temppath = tempname(fup->destpath); 789186781Slulf sr = &fup->srbuf; 790186781Slulf sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; 791186781Slulf sr->sr_file = xstrdup(name); 792186781Slulf sr->sr_serverattr = fattr_decode(attr); 793186781Slulf if (sr->sr_serverattr == NULL) 794186781Slulf return (UPDATER_ERR_PROTO); 795186781Slulf lprintf(1, " Fixup %s\n", name); 796186781Slulf error = updater_addfile(up, fup, attr, 1); 797186781Slulf if (error) 798186781Slulf return (error); 799186781Slulf break; 800186781Slulf case 'Z': 801186781Slulf name = proto_get_ascii(&line); 802186781Slulf attr = proto_get_ascii(&line); 803186781Slulf pos = proto_get_ascii(&line); 804186781Slulf if (name == NULL || attr == NULL || pos == NULL || 805186781Slulf line != NULL) 806186781Slulf return (UPDATER_ERR_PROTO); 807186781Slulf error = fup_prepare(fup, name, 0); 808186781Slulf fup->temppath = tempname(fup->destpath); 809186781Slulf sr = &fup->srbuf; 810186781Slulf sr->sr_type = SR_FILELIVE; 811186781Slulf sr->sr_file = xstrdup(name); 812186781Slulf sr->sr_serverattr = fattr_decode(attr); 813186781Slulf if (sr->sr_serverattr == NULL) 814186781Slulf return (UPDATER_ERR_PROTO); 815186781Slulf position = strtol(pos, NULL, 10); 816186781Slulf lprintf(1, " Append to %s\n", name); 817186781Slulf error = updater_append_file(up, fup, position); 818186781Slulf if (error) 819186781Slulf return (error); 820186781Slulf break; 821156230Smux case '!': 822156230Smux /* Warning from server. */ 823156230Smux msg = proto_get_rest(&line); 824156230Smux if (msg == NULL) 825156230Smux return (UPDATER_ERR_PROTO); 826156230Smux lprintf(-1, "Server warning: %s\n", msg); 827156230Smux break; 828156230Smux default: 829156230Smux return (UPDATER_ERR_PROTO); 830156230Smux } 831156230Smux fup_cleanup(fup); 832156230Smux } 833156230Smux if (line == NULL) 834156230Smux return (UPDATER_ERR_READ); 835156230Smux return (0); 836156230Smux} 837156230Smux 838156230Smux/* Delete file. */ 839156701Smuxstatic int 840156701Smuxupdater_delete(struct updater *up, struct file_update *fup) 841156230Smux{ 842156701Smux struct config *config; 843156230Smux struct coll *coll; 844156230Smux 845156701Smux config = up->config; 846156230Smux coll = fup->coll; 847156230Smux if (coll->co_options & CO_DELETE) { 848156230Smux lprintf(1, " Delete %s\n", fup->coname); 849156701Smux if (config->deletelim >= 0 && 850156701Smux up->deletecount >= config->deletelim) 851156701Smux return (UPDATER_ERR_DELETELIM); 852156701Smux up->deletecount++; 853156701Smux updater_deletefile(fup->destpath); 854156230Smux if (coll->co_options & CO_CHECKOUTMODE) 855156230Smux updater_prunedirs(coll->co_prefix, fup->destpath); 856156230Smux } else { 857156230Smux lprintf(1," NoDelete %s\n", fup->coname); 858156230Smux } 859156701Smux return (0); 860156230Smux} 861156230Smux 862156701Smuxstatic void 863156701Smuxupdater_deletefile(const char *path) 864156701Smux{ 865156701Smux int error; 866156701Smux 867156701Smux error = fattr_delete(path); 868156701Smux if (error && errno != ENOENT) { 869156701Smux lprintf(-1, "Cannot delete \"%s\": %s\n", 870156701Smux path, strerror(errno)); 871156701Smux } 872156701Smux} 873156701Smux 874156230Smuxstatic int 875156230Smuxupdater_setattrs(struct updater *up, struct file_update *fup, char *name, 876156230Smux char *tag, char *date, char *revnum, char *revdate, struct fattr *rcsattr) 877156230Smux{ 878156230Smux struct statusrec sr; 879156230Smux struct status *st; 880156230Smux struct coll *coll; 881156230Smux struct fattr *fileattr, *fa; 882156230Smux char *path; 883156230Smux int error, rv; 884156230Smux 885156230Smux coll = fup->coll; 886156230Smux st = fup->st; 887156230Smux path = fup->destpath; 888156230Smux 889156230Smux fileattr = fattr_frompath(path, FATTR_NOFOLLOW); 890156230Smux if (fileattr == NULL) { 891156230Smux /* The file has vanished. */ 892156230Smux error = status_delete(st, name, 0); 893156230Smux if (error) { 894156230Smux up->errmsg = status_errmsg(st); 895156230Smux return (UPDATER_ERR_MSG); 896156230Smux } 897156230Smux return (0); 898156230Smux } 899156230Smux fa = fattr_forcheckout(rcsattr, coll->co_umask); 900156230Smux fattr_override(fileattr, fa, FA_MASK); 901156230Smux fattr_free(fa); 902156230Smux 903156230Smux rv = fattr_install(fileattr, path, NULL); 904156230Smux if (rv == -1) { 905156230Smux lprintf(1, " SetAttrs %s\n", fup->coname); 906156230Smux fattr_free(fileattr); 907156230Smux xasprintf(&up->errmsg, "Cannot set attributes for \"%s\": %s", 908156230Smux path, strerror(errno)); 909156230Smux return (UPDATER_ERR_MSG); 910156230Smux } 911156230Smux if (rv == 1) { 912156230Smux lprintf(1, " SetAttrs %s\n", fup->coname); 913156230Smux fattr_free(fileattr); 914156230Smux fileattr = fattr_frompath(path, FATTR_NOFOLLOW); 915156230Smux if (fileattr == NULL) { 916156230Smux /* We're being very unlucky. */ 917156230Smux error = status_delete(st, name, 0); 918156230Smux if (error) { 919156230Smux up->errmsg = status_errmsg(st); 920156230Smux return (UPDATER_ERR_MSG); 921156230Smux } 922156230Smux return (0); 923156230Smux } 924156230Smux } 925156230Smux 926156230Smux fattr_maskout(fileattr, FA_COIGNORE); 927156230Smux 928156230Smux sr.sr_type = SR_CHECKOUTLIVE; 929156230Smux sr.sr_file = name; 930156230Smux sr.sr_tag = tag; 931156230Smux sr.sr_date = date; 932156230Smux sr.sr_revnum = revnum; 933156230Smux sr.sr_revdate = revdate; 934156230Smux sr.sr_clientattr = fileattr; 935156230Smux sr.sr_serverattr = rcsattr; 936156230Smux 937156230Smux error = status_put(st, &sr); 938156230Smux fattr_free(fileattr); 939156230Smux if (error) { 940156230Smux up->errmsg = status_errmsg(st); 941156230Smux return (UPDATER_ERR_MSG); 942156230Smux } 943156230Smux return (0); 944156230Smux} 945156230Smux 946156230Smuxstatic int 947156701Smuxupdater_updatefile(struct updater *up, struct file_update *fup, 948156701Smux const char *md5, int isfixup) 949156230Smux{ 950156230Smux struct coll *coll; 951156230Smux struct status *st; 952156230Smux struct statusrec *sr; 953156230Smux struct fattr *fileattr; 954156230Smux int error, rv; 955156230Smux 956156230Smux coll = fup->coll; 957156230Smux sr = &fup->srbuf; 958156230Smux st = fup->st; 959156230Smux 960156701Smux if (strcmp(fup->wantmd5, md5) != 0) { 961156701Smux if (isfixup) { 962156701Smux lprintf(-1, "%s: Checksum mismatch -- " 963156701Smux "file not updated\n", fup->destpath); 964156701Smux } else { 965156701Smux lprintf(-1, "%s: Checksum mismatch -- " 966156701Smux "will transfer entire file\n", fup->destpath); 967156701Smux fixups_put(up->config->fixups, fup->coll, sr->sr_file); 968156701Smux } 969156701Smux if (coll->co_options & CO_KEEPBADFILES) 970156701Smux lprintf(-1, "Bad version saved in %s\n", fup->temppath); 971156701Smux else 972156701Smux updater_deletefile(fup->temppath); 973156701Smux return (0); 974156701Smux } 975156701Smux 976156230Smux fattr_umask(sr->sr_clientattr, coll->co_umask); 977156701Smux rv = fattr_install(sr->sr_clientattr, fup->destpath, fup->temppath); 978156230Smux if (rv == -1) { 979156701Smux xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s", 980156701Smux fup->temppath, fup->destpath, strerror(errno)); 981156230Smux return (UPDATER_ERR_MSG); 982156230Smux } 983156230Smux 984156230Smux /* XXX Executes */ 985156230Smux /* 986156230Smux * We weren't necessarily able to set all the file attributes to the 987156230Smux * desired values, and any executes may have altered the attributes. 988156230Smux * To make sure we record the actual attribute values, we fetch 989156230Smux * them from the file. 990156230Smux * 991156230Smux * However, we preserve the link count as received from the 992156230Smux * server. This is important for preserving hard links in mirror 993156230Smux * mode. 994156230Smux */ 995156701Smux fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); 996156230Smux if (fileattr == NULL) { 997156701Smux xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath, 998156230Smux strerror(errno)); 999156230Smux return (UPDATER_ERR_MSG); 1000156230Smux } 1001156230Smux fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT); 1002156230Smux fattr_free(sr->sr_clientattr); 1003156230Smux sr->sr_clientattr = fileattr; 1004156230Smux 1005156230Smux /* 1006156230Smux * To save space, don't write out the device and inode unless 1007156230Smux * the link count is greater than 1. These attributes are used 1008156230Smux * only for detecting hard links. If the link count is 1 then we 1009156230Smux * know there aren't any hard links. 1010156230Smux */ 1011156230Smux if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) || 1012156230Smux fattr_getlinkcount(sr->sr_clientattr) <= 1) 1013156230Smux fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE); 1014156230Smux 1015156230Smux if (coll->co_options & CO_CHECKOUTMODE) 1016156230Smux fattr_maskout(sr->sr_clientattr, FA_COIGNORE); 1017156230Smux 1018156230Smux error = status_put(st, sr); 1019156230Smux if (error) { 1020156230Smux up->errmsg = status_errmsg(st); 1021156230Smux return (UPDATER_ERR_MSG); 1022156230Smux } 1023156230Smux return (0); 1024156230Smux} 1025156230Smux 1026186781Slulf/* 1027186781Slulf * Update attributes of a directory. 1028186781Slulf */ 1029156230Smuxstatic int 1030186781Slulfupdater_setdirattrs(struct updater *up, struct coll *coll, 1031186781Slulf struct file_update *fup, char *name, char *attr) 1032186781Slulf{ 1033186781Slulf struct statusrec *sr; 1034186781Slulf struct fattr *fa; 1035186781Slulf int error, rv; 1036186781Slulf 1037186781Slulf sr = &fup->srbuf; 1038186781Slulf sr->sr_type = SR_DIRUP; 1039186781Slulf sr->sr_file = xstrdup(name); 1040186781Slulf sr->sr_clientattr = fattr_decode(attr); 1041186781Slulf sr->sr_serverattr = fattr_decode(attr); 1042186781Slulf if (sr->sr_clientattr == NULL || sr->sr_serverattr == NULL) 1043186781Slulf return (UPDATER_ERR_PROTO); 1044186781Slulf fattr_mergedefault(sr->sr_clientattr); 1045186781Slulf fattr_umask(sr->sr_clientattr, coll->co_umask); 1046186781Slulf rv = fattr_install(sr->sr_clientattr, fup->destpath, NULL); 1047186781Slulf lprintf(1, " SetAttrs %s\n", name); 1048186781Slulf if (rv == -1) { 1049186781Slulf xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s", 1050186781Slulf fup->temppath, fup->destpath, strerror(errno)); 1051186781Slulf return (UPDATER_ERR_MSG); 1052186781Slulf } 1053186781Slulf /* 1054186781Slulf * Now, make sure they were set and record what was set in the status 1055186781Slulf * file. 1056186781Slulf */ 1057186781Slulf fa = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); 1058186781Slulf if (fa == NULL) { 1059186781Slulf xasprintf(&up->errmsg, "Cannot open \%s\": %s", fup->destpath, 1060186781Slulf strerror(errno)); 1061186781Slulf return (UPDATER_ERR_MSG); 1062186781Slulf } 1063186781Slulf fattr_free(sr->sr_clientattr); 1064186781Slulf fattr_maskout(fa, FA_FLAGS); 1065186781Slulf sr->sr_clientattr = fa; 1066186781Slulf error = status_put(fup->st, sr); 1067186781Slulf if (error) { 1068186781Slulf up->errmsg = status_errmsg(fup->st); 1069186781Slulf return (UPDATER_ERR_MSG); 1070186781Slulf } 1071186781Slulf 1072186781Slulf return (0); 1073186781Slulf} 1074186781Slulf 1075186781Slulfstatic int 1076156230Smuxupdater_diff(struct updater *up, struct file_update *fup) 1077156230Smux{ 1078156230Smux char md5[MD5_DIGEST_SIZE]; 1079156230Smux struct coll *coll; 1080156230Smux struct statusrec *sr; 1081156230Smux struct fattr *fa, *tmp; 1082156230Smux char *author, *path, *revnum, *revdate; 1083156701Smux char *line, *cmd; 1084156230Smux int error; 1085156230Smux 1086156230Smux coll = fup->coll; 1087156230Smux sr = &fup->srbuf; 1088156230Smux path = fup->destpath; 1089156230Smux 1090156230Smux lprintf(1, " Edit %s\n", fup->coname); 1091156230Smux while ((line = stream_getln(up->rd, NULL)) != NULL) { 1092156230Smux if (strcmp(line, ".") == 0) 1093156230Smux break; 1094156230Smux cmd = proto_get_ascii(&line); 1095156701Smux if (cmd == NULL || strcmp(cmd, "D") != 0) 1096156701Smux return (UPDATER_ERR_PROTO); 1097156230Smux revnum = proto_get_ascii(&line); 1098156230Smux proto_get_ascii(&line); /* XXX - diffbase */ 1099156230Smux revdate = proto_get_ascii(&line); 1100156230Smux author = proto_get_ascii(&line); 1101156701Smux if (author == NULL || line != NULL) 1102156701Smux return (UPDATER_ERR_PROTO); 1103156230Smux if (sr->sr_revnum != NULL) 1104156230Smux free(sr->sr_revnum); 1105156230Smux if (sr->sr_revdate != NULL) 1106156230Smux free(sr->sr_revdate); 1107156230Smux if (fup->author != NULL) 1108156230Smux free(fup->author); 1109156230Smux sr->sr_revnum = xstrdup(revnum); 1110156230Smux sr->sr_revdate = xstrdup(revdate); 1111156230Smux fup->author = xstrdup(author); 1112156230Smux if (fup->orig == NULL) { 1113156230Smux /* First patch, the "origin" file is the one we have. */ 1114156230Smux fup->orig = stream_open_file(path, O_RDONLY); 1115156230Smux if (fup->orig == NULL) { 1116156230Smux xasprintf(&up->errmsg, "%s: Cannot open: %s", 1117156230Smux path, strerror(errno)); 1118156701Smux return (UPDATER_ERR_MSG); 1119156230Smux } 1120156230Smux } else { 1121156230Smux /* Subsequent patches. */ 1122156230Smux stream_close(fup->orig); 1123156230Smux fup->orig = fup->to; 1124156230Smux stream_rewind(fup->orig); 1125156701Smux unlink(fup->temppath); 1126156701Smux free(fup->temppath); 1127156701Smux fup->temppath = tempname(path); 1128156230Smux } 1129156701Smux fup->to = stream_open_file(fup->temppath, 1130156701Smux O_RDWR | O_CREAT | O_TRUNC, 0600); 1131156230Smux if (fup->to == NULL) { 1132156230Smux xasprintf(&up->errmsg, "%s: Cannot open: %s", 1133156701Smux fup->temppath, strerror(errno)); 1134156701Smux return (UPDATER_ERR_MSG); 1135156230Smux } 1136156230Smux lprintf(2, " Add delta %s %s %s\n", sr->sr_revnum, 1137156230Smux sr->sr_revdate, fup->author); 1138156230Smux error = updater_diff_batch(up, fup); 1139156230Smux if (error) 1140156701Smux return (error); 1141156230Smux } 1142156701Smux if (line == NULL) 1143156701Smux return (UPDATER_ERR_READ); 1144156230Smux 1145156230Smux fa = fattr_frompath(path, FATTR_FOLLOW); 1146156230Smux tmp = fattr_forcheckout(sr->sr_serverattr, coll->co_umask); 1147156230Smux fattr_override(fa, tmp, FA_MASK); 1148156230Smux fattr_free(tmp); 1149156230Smux fattr_maskout(fa, FA_MODTIME); 1150156230Smux sr->sr_clientattr = fa; 1151156230Smux 1152156701Smux if (MD5_File(fup->temppath, md5) == -1) { 1153156230Smux xasprintf(&up->errmsg, 1154156230Smux "Cannot calculate checksum for \"%s\": %s", 1155156230Smux path, strerror(errno)); 1156156701Smux return (UPDATER_ERR_MSG); 1157156230Smux } 1158156701Smux error = updater_updatefile(up, fup, md5, 0); 1159156230Smux return (error); 1160156230Smux} 1161156230Smux 1162186781Slulf/* 1163186781Slulf * Edit a file and add delta. 1164186781Slulf */ 1165156230Smuxstatic int 1166156230Smuxupdater_diff_batch(struct updater *up, struct file_update *fup) 1167156230Smux{ 1168156230Smux struct stream *rd; 1169156230Smux char *cmd, *line, *state, *tok; 1170156230Smux int error; 1171156230Smux 1172156230Smux state = NULL; 1173156230Smux rd = up->rd; 1174156230Smux while ((line = stream_getln(rd, NULL)) != NULL) { 1175156230Smux if (strcmp(line, ".") == 0) 1176156230Smux break; 1177156230Smux cmd = proto_get_ascii(&line); 1178156230Smux if (cmd == NULL || strlen(cmd) != 1) { 1179156230Smux error = UPDATER_ERR_PROTO; 1180156230Smux goto bad; 1181156230Smux } 1182156230Smux switch (cmd[0]) { 1183156230Smux case 'L': 1184156230Smux line = stream_getln(rd, NULL); 1185156230Smux /* XXX - We're just eating the log for now. */ 1186156230Smux while (line != NULL && strcmp(line, ".") != 0 && 1187156230Smux strcmp(line, ".+") != 0) 1188156230Smux line = stream_getln(rd, NULL); 1189156230Smux if (line == NULL) { 1190156230Smux error = UPDATER_ERR_READ; 1191156230Smux goto bad; 1192156230Smux } 1193156230Smux break; 1194156230Smux case 'S': 1195156230Smux tok = proto_get_ascii(&line); 1196156230Smux if (tok == NULL || line != NULL) { 1197156230Smux error = UPDATER_ERR_PROTO; 1198156230Smux goto bad; 1199156230Smux } 1200156230Smux if (state != NULL) 1201156230Smux free(state); 1202156230Smux state = xstrdup(tok); 1203156230Smux break; 1204156230Smux case 'T': 1205156230Smux error = updater_diff_apply(up, fup, state); 1206156230Smux if (error) 1207156230Smux goto bad; 1208156230Smux break; 1209156230Smux default: 1210156230Smux error = UPDATER_ERR_PROTO; 1211156230Smux goto bad; 1212156230Smux } 1213156230Smux } 1214156230Smux if (line == NULL) { 1215156230Smux error = UPDATER_ERR_READ; 1216156230Smux goto bad; 1217156230Smux } 1218156230Smux if (state != NULL) 1219156230Smux free(state); 1220156230Smux return (0); 1221156230Smuxbad: 1222156230Smux if (state != NULL) 1223156230Smux free(state); 1224156230Smux return (error); 1225156230Smux} 1226156230Smux 1227156230Smuxint 1228156230Smuxupdater_diff_apply(struct updater *up, struct file_update *fup, char *state) 1229156230Smux{ 1230156230Smux struct diffinfo dibuf, *di; 1231156230Smux struct coll *coll; 1232156230Smux struct statusrec *sr; 1233156230Smux int error; 1234156230Smux 1235156230Smux coll = fup->coll; 1236156230Smux sr = &fup->srbuf; 1237156230Smux di = &dibuf; 1238156230Smux 1239156230Smux di->di_rcsfile = sr->sr_file; 1240156230Smux di->di_cvsroot = coll->co_cvsroot; 1241156230Smux di->di_revnum = sr->sr_revnum; 1242156230Smux di->di_revdate = sr->sr_revdate; 1243156230Smux di->di_author = fup->author; 1244156230Smux di->di_tag = sr->sr_tag; 1245156230Smux di->di_state = state; 1246156230Smux di->di_expand = fup->expand; 1247156230Smux 1248186781Slulf error = diff_apply(up->rd, fup->orig, fup->to, coll->co_keyword, di, 1); 1249156230Smux if (error) { 1250156230Smux /* XXX Bad error message */ 1251156230Smux xasprintf(&up->errmsg, "Bad diff from server"); 1252156230Smux return (UPDATER_ERR_MSG); 1253156230Smux } 1254156230Smux return (0); 1255156230Smux} 1256156230Smux 1257186781Slulf/* Update or create a node. */ 1258156230Smuxstatic int 1259186781Slulfupdater_updatenode(struct updater *up, struct coll *coll, 1260186781Slulf struct file_update *fup, char *name, char *attr) 1261186781Slulf{ 1262186781Slulf struct fattr *fa, *fileattr; 1263186781Slulf struct status *st; 1264186781Slulf struct statusrec *sr; 1265186781Slulf int error, rv; 1266186781Slulf 1267186781Slulf sr = &fup->srbuf; 1268186781Slulf st = fup->st; 1269186781Slulf fa = fattr_decode(attr); 1270186781Slulf 1271186781Slulf if (fattr_type(fa) == FT_SYMLINK) { 1272186781Slulf lprintf(1, " Symlink %s -> %s\n", name, 1273186781Slulf fattr_getlinktarget(fa)); 1274186781Slulf } else { 1275186781Slulf lprintf(1, " Mknod %s\n", name); 1276186781Slulf } 1277186781Slulf 1278186781Slulf /* Create directory. */ 1279186781Slulf error = mkdirhier(fup->destpath, coll->co_umask); 1280186781Slulf if (error) 1281186781Slulf return (UPDATER_ERR_PROTO); 1282186781Slulf 1283186781Slulf /* If it does not exist, create it. */ 1284186781Slulf if (access(fup->destpath, F_OK) != 0) 1285186781Slulf fattr_makenode(fa, fup->destpath); 1286186781Slulf 1287186781Slulf /* 1288186781Slulf * Coming from attic? I don't think this is a problem since we have 1289186781Slulf * determined attic before we call this function (Look at UpdateNode in 1290186781Slulf * cvsup). 1291186781Slulf */ 1292186781Slulf fattr_umask(fa, coll->co_umask); 1293186781Slulf rv = fattr_install(fa, fup->destpath, fup->temppath); 1294186781Slulf if (rv == -1) { 1295186781Slulf xasprintf(&up->errmsg, "Cannot update attributes on " 1296186781Slulf "\"%s\": %s", fup->destpath, strerror(errno)); 1297186781Slulf return (UPDATER_ERR_MSG); 1298186781Slulf } 1299186781Slulf /* 1300186781Slulf * XXX: Executes not implemented. Have not encountered much use for it 1301186781Slulf * yet. 1302186781Slulf */ 1303186781Slulf /* 1304186781Slulf * We weren't necessarily able to set all the file attributes to the 1305186781Slulf * desired values, and any executes may have altered the attributes. 1306186781Slulf * To make sure we record the actual attribute values, we fetch 1307186781Slulf * them from the file. 1308186781Slulf * 1309186781Slulf * However, we preserve the link count as received from the 1310186781Slulf * server. This is important for preserving hard links in mirror 1311186781Slulf * mode. 1312186781Slulf */ 1313186781Slulf fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); 1314186781Slulf if (fileattr == NULL) { 1315186781Slulf xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath, 1316186781Slulf strerror(errno)); 1317186781Slulf return (UPDATER_ERR_MSG); 1318186781Slulf } 1319186781Slulf fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT); 1320186781Slulf fattr_free(sr->sr_clientattr); 1321186781Slulf sr->sr_clientattr = fileattr; 1322186781Slulf 1323186781Slulf /* 1324186781Slulf * To save space, don't write out the device and inode unless 1325186781Slulf * the link count is greater than 1. These attributes are used 1326186781Slulf * only for detecting hard links. If the link count is 1 then we 1327186781Slulf * know there aren't any hard links. 1328186781Slulf */ 1329186781Slulf if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) || 1330186781Slulf fattr_getlinkcount(sr->sr_clientattr) <= 1) 1331186781Slulf fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE); 1332186781Slulf 1333186781Slulf /* If it is a symlink, write only out it's path. */ 1334186781Slulf if (fattr_type(fa) == FT_SYMLINK) { 1335186781Slulf fattr_maskout(sr->sr_clientattr, ~(FA_FILETYPE | 1336186781Slulf FA_LINKTARGET)); 1337186781Slulf } 1338186781Slulf fattr_maskout(sr->sr_clientattr, FA_FLAGS); 1339186781Slulf error = status_put(st, sr); 1340186781Slulf if (error) { 1341186781Slulf up->errmsg = status_errmsg(st); 1342186781Slulf return (UPDATER_ERR_MSG); 1343186781Slulf } 1344186781Slulf fattr_free(fa); 1345186781Slulf 1346186781Slulf return (0); 1347186781Slulf} 1348186781Slulf 1349186781Slulf/* 1350186781Slulf * Fetches a new file in CVS mode. 1351186781Slulf */ 1352186781Slulfstatic int 1353186781Slulfupdater_addfile(struct updater *up, struct file_update *fup, char *attr, 1354186781Slulf int isfixup) 1355186781Slulf{ 1356186781Slulf struct coll *coll; 1357186781Slulf struct stream *to; 1358186781Slulf struct statusrec *sr; 1359186781Slulf struct fattr *fa; 1360186781Slulf char buf[BUFSIZE]; 1361186781Slulf char md5[MD5_DIGEST_SIZE]; 1362186781Slulf ssize_t nread; 1363186781Slulf off_t fsize, remains; 1364186781Slulf char *cmd, *line, *path; 1365186781Slulf int error; 1366186781Slulf 1367186781Slulf coll = fup->coll; 1368186781Slulf path = fup->destpath; 1369186781Slulf sr = &fup->srbuf; 1370186781Slulf fa = fattr_decode(attr); 1371186781Slulf fsize = fattr_filesize(fa); 1372186781Slulf 1373186781Slulf error = mkdirhier(path, coll->co_umask); 1374186781Slulf if (error) 1375186781Slulf return (UPDATER_ERR_PROTO); 1376186781Slulf to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, 0755); 1377186781Slulf if (to == NULL) { 1378186781Slulf xasprintf(&up->errmsg, "%s: Cannot create: %s", 1379186781Slulf fup->temppath, strerror(errno)); 1380186781Slulf return (UPDATER_ERR_MSG); 1381186781Slulf } 1382186781Slulf stream_filter_start(to, STREAM_FILTER_MD5, md5); 1383186781Slulf remains = fsize; 1384186781Slulf do { 1385186781Slulf nread = stream_read(up->rd, buf, (BUFSIZE > remains ? 1386186781Slulf remains : BUFSIZE)); 1387190422Slulf if (nread == -1) 1388190422Slulf return (UPDATER_ERR_PROTO); 1389186781Slulf remains -= nread; 1390190422Slulf if (stream_write(to, buf, nread) == -1) 1391190422Slulf goto bad; 1392186781Slulf } while (remains > 0); 1393186781Slulf stream_close(to); 1394186781Slulf line = stream_getln(up->rd, NULL); 1395186781Slulf if (line == NULL) 1396186781Slulf return (UPDATER_ERR_PROTO); 1397186781Slulf /* Check for EOF. */ 1398186781Slulf if (!(*line == '.' || (strncmp(line, ".<", 2) != 0))) 1399186781Slulf return (UPDATER_ERR_PROTO); 1400186781Slulf line = stream_getln(up->rd, NULL); 1401186781Slulf if (line == NULL) 1402186781Slulf return (UPDATER_ERR_PROTO); 1403186781Slulf 1404186781Slulf cmd = proto_get_ascii(&line); 1405186781Slulf fup->wantmd5 = proto_get_ascii(&line); 1406186781Slulf if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0) 1407186781Slulf return (UPDATER_ERR_PROTO); 1408186781Slulf 1409186781Slulf sr->sr_clientattr = fattr_frompath(fup->temppath, FATTR_NOFOLLOW); 1410186781Slulf if (sr->sr_clientattr == NULL) 1411186781Slulf return (UPDATER_ERR_PROTO); 1412186781Slulf fattr_override(sr->sr_clientattr, sr->sr_serverattr, 1413186781Slulf FA_MODTIME | FA_MASK); 1414186781Slulf error = updater_updatefile(up, fup, md5, isfixup); 1415186781Slulf fup->wantmd5 = NULL; /* So that it doesn't get freed. */ 1416190422Slulf return (error); 1417190422Slulfbad: 1418190422Slulf xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, 1419190422Slulf strerror(errno)); 1420190422Slulf return (UPDATER_ERR_MSG); 1421186781Slulf} 1422186781Slulf 1423186781Slulfstatic int 1424156230Smuxupdater_checkout(struct updater *up, struct file_update *fup, int isfixup) 1425156230Smux{ 1426156230Smux char md5[MD5_DIGEST_SIZE]; 1427156230Smux struct statusrec *sr; 1428156230Smux struct coll *coll; 1429156230Smux struct stream *to; 1430186781Slulf ssize_t nbytes; 1431186781Slulf size_t size; 1432156230Smux char *cmd, *path, *line; 1433156230Smux int error, first; 1434156230Smux 1435156230Smux coll = fup->coll; 1436156230Smux sr = &fup->srbuf; 1437156230Smux path = fup->destpath; 1438156230Smux 1439156230Smux if (isfixup) 1440156230Smux lprintf(1, " Fixup %s\n", fup->coname); 1441156230Smux else 1442156230Smux lprintf(1, " Checkout %s\n", fup->coname); 1443156230Smux error = mkdirhier(path, coll->co_umask); 1444156230Smux if (error) { 1445156230Smux xasprintf(&up->errmsg, 1446156230Smux "Cannot create directories leading to \"%s\": %s", 1447156230Smux path, strerror(errno)); 1448156230Smux return (UPDATER_ERR_MSG); 1449156230Smux } 1450156230Smux 1451156701Smux to = stream_open_file(fup->temppath, 1452156701Smux O_WRONLY | O_CREAT | O_TRUNC, 0600); 1453156230Smux if (to == NULL) { 1454156230Smux xasprintf(&up->errmsg, "%s: Cannot create: %s", 1455156701Smux fup->temppath, strerror(errno)); 1456156230Smux return (UPDATER_ERR_MSG); 1457156230Smux } 1458156230Smux stream_filter_start(to, STREAM_FILTER_MD5, md5); 1459156230Smux line = stream_getln(up->rd, &size); 1460156230Smux first = 1; 1461156230Smux while (line != NULL) { 1462156230Smux if (line[size - 1] == '\n') 1463156230Smux size--; 1464156230Smux if ((size == 1 && *line == '.') || 1465156230Smux (size == 2 && memcmp(line, ".+", 2) == 0)) 1466156230Smux break; 1467156230Smux if (size >= 2 && memcmp(line, "..", 2) == 0) { 1468156230Smux size--; 1469156230Smux line++; 1470156230Smux } 1471156230Smux if (!first) { 1472156230Smux nbytes = stream_write(to, "\n", 1); 1473156230Smux if (nbytes == -1) 1474156230Smux goto bad; 1475156230Smux } 1476190422Slulf nbytes = stream_write(to, line, size); 1477190422Slulf if (nbytes == -1) 1478190422Slulf goto bad; 1479156230Smux line = stream_getln(up->rd, &size); 1480156230Smux first = 0; 1481156230Smux } 1482156230Smux if (line == NULL) { 1483156230Smux stream_close(to); 1484156230Smux return (UPDATER_ERR_READ); 1485156230Smux } 1486156230Smux if (size == 1 && *line == '.') { 1487156230Smux nbytes = stream_write(to, "\n", 1); 1488156230Smux if (nbytes == -1) 1489156230Smux goto bad; 1490156230Smux } 1491156230Smux stream_close(to); 1492156230Smux /* Get the checksum line. */ 1493156230Smux line = stream_getln(up->rd, NULL); 1494156230Smux if (line == NULL) 1495156230Smux return (UPDATER_ERR_READ); 1496156230Smux cmd = proto_get_ascii(&line); 1497156230Smux fup->wantmd5 = proto_get_ascii(&line); 1498156230Smux if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0) 1499156230Smux return (UPDATER_ERR_PROTO); 1500156701Smux error = updater_updatefile(up, fup, md5, isfixup); 1501156230Smux fup->wantmd5 = NULL; /* So that it doesn't get freed. */ 1502156230Smux if (error) 1503156230Smux return (error); 1504156230Smux return (0); 1505156230Smuxbad: 1506156701Smux xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, 1507156701Smux strerror(errno)); 1508156230Smux return (UPDATER_ERR_MSG); 1509156230Smux} 1510156230Smux 1511156230Smux/* 1512156230Smux * Remove all empty directories below file. 1513156230Smux * This function will trash the path passed to it. 1514156230Smux */ 1515156230Smuxstatic void 1516156230Smuxupdater_prunedirs(char *base, char *file) 1517156230Smux{ 1518156230Smux char *cp; 1519156230Smux int error; 1520156230Smux 1521156230Smux while ((cp = strrchr(file, '/')) != NULL) { 1522156230Smux *cp = '\0'; 1523156230Smux if (strcmp(base, file) == 0) 1524156230Smux return; 1525156230Smux error = rmdir(file); 1526156230Smux if (error) 1527156230Smux return; 1528156230Smux } 1529156230Smux} 1530186781Slulf 1531186781Slulf/* 1532186781Slulf * Edit an RCS file. 1533186781Slulf */ 1534186781Slulfstatic int 1535186781Slulfupdater_rcsedit(struct updater *up, struct file_update *fup, char *name, 1536186781Slulf char *rcsopt) 1537186781Slulf{ 1538186781Slulf struct coll *coll; 1539186781Slulf struct stream *dest; 1540186781Slulf struct statusrec *sr; 1541186781Slulf struct status *st; 1542186781Slulf struct rcsfile *rf; 1543186781Slulf struct fattr *oldfattr; 1544186781Slulf char md5[MD5_DIGEST_SIZE]; 1545186781Slulf char *branch, *cmd, *expand, *line, *path, *revnum, *tag, *temppath; 1546186781Slulf int error; 1547186781Slulf 1548186781Slulf coll = fup->coll; 1549186781Slulf sr = &fup->srbuf; 1550186781Slulf st = fup->st; 1551186781Slulf temppath = fup->temppath; 1552186781Slulf path = fup->origpath != NULL ? fup->origpath : fup->destpath; 1553186781Slulf error = 0; 1554186781Slulf 1555186781Slulf /* If the path is new, we must create the Attic dir if needed. */ 1556186781Slulf if (fup->origpath != NULL) { 1557186781Slulf error = mkdirhier(fup->destpath, coll->co_umask); 1558186781Slulf if (error) { 1559186781Slulf xasprintf(&up->errmsg, "Unable to create Attic dir for " 1560186781Slulf "%s\n", fup->origpath); 1561186781Slulf return (UPDATER_ERR_MSG); 1562186781Slulf } 1563186781Slulf } 1564186781Slulf /* 1565186781Slulf * XXX: we could avoid parsing overhead if we're reading ahead before we 1566186781Slulf * parse the file. 1567186781Slulf */ 1568186781Slulf oldfattr = fattr_frompath(path, FATTR_NOFOLLOW); 1569186781Slulf if (oldfattr == NULL) { 1570186781Slulf xasprintf(&up->errmsg, "%s: Cannot get attributes: %s", path, 1571186781Slulf strerror(errno)); 1572186781Slulf return (UPDATER_ERR_MSG); 1573186781Slulf } 1574186781Slulf fattr_merge(sr->sr_serverattr, oldfattr); 1575186781Slulf rf = NULL; 1576186781Slulf 1577186781Slulf /* Macro for making touching an RCS file faster. */ 1578186781Slulf#define UPDATER_OPENRCS(rf, up, path, name, cvsroot, tag) do { \ 1579186781Slulf if ((rf) == NULL) { \ 1580186781Slulf lprintf(1, " Edit %s", fup->coname); \ 1581186781Slulf if (fup->attic) \ 1582186781Slulf lprintf(1, " -> Attic"); \ 1583186781Slulf lprintf(1, "\n"); \ 1584186781Slulf (rf) = rcsfile_frompath((path), (name), (cvsroot), \ 1585186781Slulf (tag), 0); \ 1586186781Slulf if ((rf) == NULL) { \ 1587186781Slulf xasprintf(&(up)->errmsg, \ 1588186781Slulf "Error reading rcsfile %s\n", (name)); \ 1589186781Slulf return (UPDATER_ERR_MSG); \ 1590186781Slulf } \ 1591186781Slulf } \ 1592186781Slulf} while (0) 1593186781Slulf 1594186781Slulf while ((line = stream_getln(up->rd, NULL)) != NULL) { 1595186781Slulf if (strcmp(line, ".") == 0) 1596186781Slulf break; 1597186781Slulf cmd = proto_get_ascii(&line); 1598186781Slulf if (cmd == NULL) { 1599186781Slulf lprintf(-1, "Error editing %s\n", name); 1600186781Slulf return (UPDATER_ERR_PROTO); 1601186781Slulf } 1602186781Slulf switch(cmd[0]) { 1603186781Slulf case 'B': 1604186781Slulf branch = proto_get_ascii(&line); 1605186781Slulf if (branch == NULL || line != NULL) 1606186781Slulf return (UPDATER_ERR_PROTO); 1607186781Slulf UPDATER_OPENRCS(rf, up, path, name, 1608186781Slulf coll->co_cvsroot, coll->co_tag); 1609186781Slulf break; 1610186781Slulf case 'b': 1611186781Slulf UPDATER_OPENRCS(rf, up, path, name, 1612186781Slulf coll->co_cvsroot, coll->co_tag); 1613186781Slulf rcsfile_setval(rf, RCSFILE_BRANCH, NULL); 1614186781Slulf break; 1615186781Slulf case 'D': 1616186781Slulf UPDATER_OPENRCS(rf, up, path, name, 1617186781Slulf coll->co_cvsroot, coll->co_tag); 1618186781Slulf error = updater_addelta(rf, up->rd, line); 1619186781Slulf if (error) 1620186781Slulf return (error); 1621186781Slulf break; 1622186781Slulf case 'd': 1623186781Slulf revnum = proto_get_ascii(&line); 1624186781Slulf if (revnum == NULL || line != NULL) 1625186781Slulf return (UPDATER_ERR_PROTO); 1626186781Slulf UPDATER_OPENRCS(rf, up, path, name, 1627186781Slulf coll->co_cvsroot, coll->co_tag); 1628186781Slulf rcsfile_deleterev(rf, revnum); 1629186781Slulf break; 1630186781Slulf case 'E': 1631186781Slulf expand = proto_get_ascii(&line); 1632186781Slulf if (expand == NULL || line != NULL) 1633186781Slulf return (UPDATER_ERR_PROTO); 1634186781Slulf UPDATER_OPENRCS(rf, up, path, name, 1635186781Slulf coll->co_cvsroot, coll->co_tag); 1636186781Slulf rcsfile_setval(rf, RCSFILE_EXPAND, expand); 1637186781Slulf break; 1638186781Slulf case 'T': 1639186781Slulf tag = proto_get_ascii(&line); 1640186781Slulf revnum = proto_get_ascii(&line); 1641186781Slulf if (tag == NULL || revnum == NULL || 1642186781Slulf line != NULL) 1643186781Slulf return (UPDATER_ERR_PROTO); 1644186781Slulf UPDATER_OPENRCS(rf, up, path, name, 1645186781Slulf coll->co_cvsroot, coll->co_tag); 1646186781Slulf rcsfile_addtag(rf, tag, revnum); 1647186781Slulf break; 1648186781Slulf case 't': 1649186781Slulf tag = proto_get_ascii(&line); 1650186781Slulf revnum = proto_get_ascii(&line); 1651186781Slulf if (tag == NULL || revnum == NULL || 1652186781Slulf line != NULL) 1653186781Slulf return (UPDATER_ERR_PROTO); 1654186781Slulf UPDATER_OPENRCS(rf, up, path, name, 1655186781Slulf coll->co_cvsroot, coll->co_tag); 1656186781Slulf rcsfile_deletetag(rf, tag, revnum); 1657186781Slulf break; 1658186781Slulf default: 1659186781Slulf return (UPDATER_ERR_PROTO); 1660186781Slulf } 1661186781Slulf } 1662186781Slulf 1663186781Slulf if (rf == NULL) { 1664186781Slulf fattr_maskout(oldfattr, ~FA_MODTIME); 1665188405Slulf if (fattr_equal(oldfattr, sr->sr_serverattr)) 1666186781Slulf lprintf(1, " SetAttrs %s", fup->coname); 1667186781Slulf else 1668186781Slulf lprintf(1, " Touch %s", fup->coname); 1669188405Slulf /* Install new attributes. */ 1670190406Slulf fattr_umask(sr->sr_serverattr, coll->co_umask); 1671188405Slulf fattr_install(sr->sr_serverattr, fup->destpath, NULL); 1672186781Slulf if (fup->attic) 1673186781Slulf lprintf(1, " -> Attic"); 1674186781Slulf lprintf(1, "\n"); 1675186781Slulf fattr_free(oldfattr); 1676186781Slulf goto finish; 1677186781Slulf } 1678186781Slulf 1679186781Slulf /* Write and rename temp file. */ 1680186781Slulf dest = stream_open_file(fup->temppath, 1681186781Slulf O_RDWR | O_CREAT | O_TRUNC, 0600); 1682186781Slulf if (dest == NULL) { 1683186781Slulf xasprintf(&up->errmsg, "Error opening file %s for writing: %s\n", 1684186781Slulf fup->temppath, strerror(errno)); 1685186781Slulf return (UPDATER_ERR_MSG); 1686186781Slulf } 1687186781Slulf stream_filter_start(dest, STREAM_FILTER_MD5RCS, md5); 1688186781Slulf error = rcsfile_write(rf, dest); 1689186781Slulf stream_close(dest); 1690186781Slulf rcsfile_free(rf); 1691190422Slulf if (error) { 1692190422Slulf xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, 1693190422Slulf strerror(errno)); 1694190422Slulf return (UPDATER_ERR_MSG); 1695190422Slulf } 1696186781Slulf 1697186781Slulffinish: 1698186781Slulf sr->sr_clientattr = fattr_frompath(path, FATTR_NOFOLLOW); 1699186781Slulf if (sr->sr_clientattr == NULL) { 1700186781Slulf xasprintf(&up->errmsg, "%s: Cannot get attributes: %s", 1701186781Slulf fup->destpath, strerror(errno)); 1702186781Slulf return (UPDATER_ERR_MSG); 1703186781Slulf } 1704186781Slulf fattr_override(sr->sr_clientattr, sr->sr_serverattr, 1705186781Slulf FA_MODTIME | FA_MASK); 1706186781Slulf if (rf != NULL) { 1707186781Slulf error = updater_updatefile(up, fup, md5, 0); 1708186781Slulf fup->wantmd5 = NULL; /* So that it doesn't get freed. */ 1709186781Slulf if (error) 1710186781Slulf return (error); 1711186781Slulf } else { 1712186781Slulf /* Record its attributes since we touched it. */ 1713186781Slulf if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) || 1714186781Slulf fattr_getlinkcount(sr->sr_clientattr) <= 1) 1715186781Slulf fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE); 1716186781Slulf error = status_put(st, sr); 1717186781Slulf if (error) { 1718186781Slulf up->errmsg = status_errmsg(st); 1719186781Slulf return (UPDATER_ERR_MSG); 1720186781Slulf } 1721186781Slulf } 1722186781Slulf 1723186781Slulf /* In this case, we need to remove the old file afterwards. */ 1724186781Slulf /* XXX: Can we be sure that a file not edited is moved? I don't think 1725186781Slulf * this is a problem, since if a file is moved, it should be edited to 1726186781Slulf * show if it's dead or not. 1727186781Slulf */ 1728186781Slulf if (fup->origpath != NULL) 1729186781Slulf updater_deletefile(fup->origpath); 1730186781Slulf return (0); 1731186781Slulf} 1732186781Slulf 1733186781Slulf/* 1734186781Slulf * Add a delta to a RCS file. 1735186781Slulf */ 1736186781Slulfint 1737186781Slulfupdater_addelta(struct rcsfile *rf, struct stream *rd, char *cmdline) 1738186781Slulf{ 1739186781Slulf struct delta *d; 1740186781Slulf size_t size; 1741186781Slulf char *author, *cmd, *diffbase, *line, *logline; 1742186781Slulf char *revdate, *revnum, *state, *textline; 1743186781Slulf 1744186781Slulf revnum = proto_get_ascii(&cmdline); 1745186781Slulf diffbase = proto_get_ascii(&cmdline); 1746186781Slulf revdate = proto_get_ascii(&cmdline); 1747186781Slulf author = proto_get_ascii(&cmdline); 1748186781Slulf size = 0; 1749186781Slulf 1750186781Slulf if (revnum == NULL || revdate == NULL || author == NULL) 1751186781Slulf return (UPDATER_ERR_PROTO); 1752186781Slulf 1753186781Slulf /* First add the delta so we have it. */ 1754186781Slulf d = rcsfile_addelta(rf, revnum, revdate, author, diffbase); 1755186781Slulf if (d == NULL) { 1756186781Slulf lprintf(-1, "Error adding delta %s\n", revnum); 1757186781Slulf return (UPDATER_ERR_READ); 1758186781Slulf } 1759186781Slulf while ((line = stream_getln(rd, NULL)) != NULL) { 1760186781Slulf if (strcmp(line, ".") == 0) 1761186781Slulf break; 1762186781Slulf cmd = proto_get_ascii(&line); 1763186781Slulf switch (cmd[0]) { 1764186781Slulf case 'L': 1765186781Slulf /* Do the same as in 'C' command. */ 1766186781Slulf logline = stream_getln(rd, &size); 1767186781Slulf while (logline != NULL) { 1768186781Slulf if (size == 2 && *logline == '.') 1769186781Slulf break; 1770186781Slulf if (size == 3 && 1771186781Slulf memcmp(logline, ".+", 2) == 0) { 1772186781Slulf rcsdelta_truncatelog(d, -1); 1773186781Slulf break; 1774186781Slulf } 1775186781Slulf if (size >= 3 && 1776186781Slulf memcmp(logline, "..", 2) == 0) { 1777186781Slulf size--; 1778186781Slulf logline++; 1779186781Slulf } 1780190422Slulf if (rcsdelta_appendlog(d, logline, size) 1781190422Slulf < 0) 1782190422Slulf return (-1); 1783186781Slulf logline = stream_getln(rd, &size); 1784186781Slulf } 1785186781Slulf break; 1786186781Slulf case 'N': 1787186781Slulf case 'n': 1788186781Slulf /* XXX: Not supported. */ 1789186781Slulf break; 1790186781Slulf case 'S': 1791186781Slulf state = proto_get_ascii(&line); 1792186781Slulf if (state == NULL) 1793186781Slulf return (UPDATER_ERR_PROTO); 1794186781Slulf rcsdelta_setstate(d, state); 1795186781Slulf break; 1796186781Slulf case 'T': 1797186781Slulf /* Do the same as in 'C' command. */ 1798186781Slulf textline = stream_getln(rd, &size); 1799186781Slulf while (textline != NULL) { 1800186781Slulf if (size == 2 && *textline == '.') 1801186781Slulf break; 1802186781Slulf if (size == 3 && 1803186781Slulf memcmp(textline, ".+", 2) == 0) { 1804186781Slulf /* Truncate newline. */ 1805186781Slulf rcsdelta_truncatetext(d, -1); 1806186781Slulf break; 1807186781Slulf } 1808186781Slulf if (size >= 3 && 1809186781Slulf memcmp(textline, "..", 2) == 0) { 1810186781Slulf size--; 1811186781Slulf textline++; 1812186781Slulf } 1813190422Slulf if (rcsdelta_appendtext(d, textline, 1814190422Slulf size) < 0) 1815190422Slulf return (-1); 1816186781Slulf textline = stream_getln(rd, &size); 1817186781Slulf } 1818186781Slulf break; 1819186781Slulf } 1820186781Slulf } 1821186781Slulf 1822186781Slulf return (0); 1823186781Slulf} 1824186781Slulf 1825186781Slulfint 1826186781Slulfupdater_append_file(struct updater *up, struct file_update *fup, off_t pos) 1827186781Slulf{ 1828186781Slulf struct fattr *fa; 1829186781Slulf struct stream *to; 1830186781Slulf struct statusrec *sr; 1831186781Slulf ssize_t nread; 1832186781Slulf off_t bytes; 1833186781Slulf char buf[BUFSIZE], md5[MD5_DIGEST_SIZE]; 1834186781Slulf char *line, *cmd; 1835186781Slulf int error, fd; 1836186781Slulf 1837186781Slulf sr = &fup->srbuf; 1838186781Slulf fa = sr->sr_serverattr; 1839186781Slulf to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, 1840186781Slulf 0755); 1841186781Slulf if (to == NULL) { 1842186781Slulf xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->temppath, 1843186781Slulf strerror(errno)); 1844186781Slulf return (UPDATER_ERR_MSG); 1845186781Slulf } 1846186781Slulf fd = open(fup->destpath, O_RDONLY); 1847186781Slulf if (fd < 0) { 1848186781Slulf xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->destpath, 1849186781Slulf strerror(errno)); 1850186781Slulf return (UPDATER_ERR_MSG); 1851186781Slulf } 1852186781Slulf 1853186781Slulf stream_filter_start(to, STREAM_FILTER_MD5, md5); 1854186781Slulf /* First write the existing content. */ 1855190422Slulf while ((nread = read(fd, buf, BUFSIZE)) > 0) { 1856190422Slulf if (stream_write(to, buf, nread) == -1) 1857190422Slulf goto bad; 1858190422Slulf } 1859190422Slulf if (nread == -1) { 1860193213Slulf xasprintf(&up->errmsg, "%s: Error reading: %s", fup->destpath, 1861190422Slulf strerror(errno)); 1862190422Slulf return (UPDATER_ERR_MSG); 1863190422Slulf } 1864186781Slulf close(fd); 1865186781Slulf 1866186781Slulf bytes = fattr_filesize(fa) - pos; 1867186781Slulf /* Append the new data. */ 1868186781Slulf do { 1869186781Slulf nread = stream_read(up->rd, buf, 1870186781Slulf (BUFSIZE > bytes) ? bytes : BUFSIZE); 1871190422Slulf if (nread == -1) 1872190422Slulf return (UPDATER_ERR_PROTO); 1873186781Slulf bytes -= nread; 1874190422Slulf if (stream_write(to, buf, nread) == -1) 1875190422Slulf goto bad; 1876186781Slulf } while (bytes > 0); 1877186781Slulf stream_close(to); 1878186781Slulf 1879186781Slulf line = stream_getln(up->rd, NULL); 1880186781Slulf if (line == NULL) 1881186781Slulf return (UPDATER_ERR_PROTO); 1882186781Slulf /* Check for EOF. */ 1883186781Slulf if (!(*line == '.' || (strncmp(line, ".<", 2) != 0))) 1884186781Slulf return (UPDATER_ERR_PROTO); 1885186781Slulf line = stream_getln(up->rd, NULL); 1886186781Slulf if (line == NULL) 1887186781Slulf return (UPDATER_ERR_PROTO); 1888186781Slulf 1889186781Slulf cmd = proto_get_ascii(&line); 1890186781Slulf fup->wantmd5 = proto_get_ascii(&line); 1891186781Slulf if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0) 1892186781Slulf return (UPDATER_ERR_PROTO); 1893186781Slulf 1894186781Slulf sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); 1895186781Slulf if (sr->sr_clientattr == NULL) 1896186781Slulf return (UPDATER_ERR_PROTO); 1897186781Slulf fattr_override(sr->sr_clientattr, sr->sr_serverattr, 1898186781Slulf FA_MODTIME | FA_MASK); 1899186781Slulf error = updater_updatefile(up, fup, md5, 0); 1900186781Slulf fup->wantmd5 = NULL; /* So that it doesn't get freed. */ 1901190422Slulf return (error); 1902190422Slulfbad: 1903190422Slulf xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, 1904190422Slulf strerror(errno)); 1905190422Slulf return (UPDATER_ERR_MSG); 1906186781Slulf} 1907186781Slulf 1908186781Slulf/* 1909186781Slulf * Read file data from stream of checkout commands, and write it to the 1910186781Slulf * destination. 1911186781Slulf */ 1912186781Slulfstatic int 1913186781Slulfupdater_read_checkout(struct stream *src, struct stream *dest) 1914186781Slulf{ 1915186781Slulf ssize_t nbytes; 1916186781Slulf size_t size; 1917186781Slulf char *line; 1918186781Slulf int first; 1919186781Slulf 1920186781Slulf first = 1; 1921186781Slulf line = stream_getln(src, &size); 1922186781Slulf while (line != NULL) { 1923186781Slulf if (line[size - 1] == '\n') 1924186781Slulf size--; 1925186781Slulf if ((size == 1 && *line == '.') || 1926186781Slulf (size == 2 && strncmp(line, ".+", 2) == 0)) 1927186781Slulf break; 1928186781Slulf if (size >= 2 && strncmp(line, "..", 2) == 0) { 1929186781Slulf size--; 1930186781Slulf line++; 1931186781Slulf } 1932186781Slulf if (!first) { 1933186781Slulf nbytes = stream_write(dest, "\n", 1); 1934186781Slulf if (nbytes == -1) 1935186781Slulf return (UPDATER_ERR_MSG); 1936186781Slulf } 1937186781Slulf nbytes = stream_write(dest, line, size); 1938186781Slulf if (nbytes == -1) 1939186781Slulf return (UPDATER_ERR_MSG); 1940186781Slulf line = stream_getln(src, &size); 1941186781Slulf first = 0; 1942186781Slulf } 1943186781Slulf if (line == NULL) 1944186781Slulf return (UPDATER_ERR_READ); 1945186781Slulf if (size == 1 && *line == '.') { 1946186781Slulf nbytes = stream_write(dest, "\n", 1); 1947186781Slulf if (nbytes == -1) 1948186781Slulf return (UPDATER_ERR_MSG); 1949186781Slulf } 1950186781Slulf return (0); 1951186781Slulf} 1952186781Slulf 1953186781Slulf/* Update file using the rsync protocol. */ 1954186781Slulfstatic int 1955186781Slulfupdater_rsync(struct updater *up, struct file_update *fup, size_t blocksize) 1956186781Slulf{ 1957186781Slulf struct statusrec *sr; 1958186781Slulf struct stream *to; 1959186781Slulf char md5[MD5_DIGEST_SIZE]; 1960186781Slulf ssize_t nbytes; 1961186781Slulf size_t blocknum, blockstart, blockcount; 1962186781Slulf char *buf, *line; 1963186781Slulf int error, orig; 1964186781Slulf 1965186781Slulf sr = &fup->srbuf; 1966186781Slulf 1967186781Slulf lprintf(1, " Rsync %s\n", fup->coname); 1968186781Slulf /* First open all files that we are going to work on. */ 1969186781Slulf to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, 1970186781Slulf 0600); 1971186781Slulf if (to == NULL) { 1972186781Slulf xasprintf(&up->errmsg, "%s: Cannot create: %s", 1973186781Slulf fup->temppath, strerror(errno)); 1974186781Slulf return (UPDATER_ERR_MSG); 1975186781Slulf } 1976186781Slulf orig = open(fup->destpath, O_RDONLY); 1977186781Slulf if (orig < 0) { 1978186781Slulf xasprintf(&up->errmsg, "%s: Cannot open: %s", 1979186781Slulf fup->destpath, strerror(errno)); 1980186781Slulf return (UPDATER_ERR_MSG); 1981186781Slulf } 1982186781Slulf stream_filter_start(to, STREAM_FILTER_MD5, md5); 1983186781Slulf 1984186781Slulf error = updater_read_checkout(up->rd, to); 1985186781Slulf if (error) { 1986186781Slulf xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, 1987186781Slulf strerror(errno)); 1988186781Slulf return (error); 1989186781Slulf } 1990186781Slulf 1991186781Slulf /* Buffer must contain blocksize bytes. */ 1992186781Slulf buf = xmalloc(blocksize); 1993186781Slulf /* Done with the initial text, read and write chunks. */ 1994186781Slulf line = stream_getln(up->rd, NULL); 1995186781Slulf while (line != NULL) { 1996186781Slulf if (strcmp(line, ".") == 0) 1997186781Slulf break; 1998186781Slulf error = UPDATER_ERR_PROTO; 1999186781Slulf if (proto_get_sizet(&line, &blockstart, 10) != 0) 2000186781Slulf goto bad; 2001186781Slulf if (proto_get_sizet(&line, &blockcount, 10) != 0) 2002186781Slulf goto bad; 2003186781Slulf /* Read blocks from original file. */ 2004186781Slulf lseek(orig, (blocksize * blockstart), SEEK_SET); 2005186781Slulf error = UPDATER_ERR_MSG; 2006186781Slulf for (blocknum = 0; blocknum < blockcount; blocknum++) { 2007186781Slulf nbytes = read(orig, buf, blocksize); 2008186781Slulf if (nbytes < 0) { 2009186781Slulf xasprintf(&up->errmsg, "%s: Cannot read: %s", 2010186781Slulf fup->destpath, strerror(errno)); 2011186781Slulf goto bad; 2012186781Slulf } 2013186781Slulf nbytes = stream_write(to, buf, nbytes); 2014186781Slulf if (nbytes == -1) { 2015186781Slulf xasprintf(&up->errmsg, "%s: Cannot write: %s", 2016186781Slulf fup->temppath, strerror(errno)); 2017186781Slulf goto bad; 2018186781Slulf } 2019186781Slulf } 2020186781Slulf /* Get the remaining text from the server. */ 2021186781Slulf error = updater_read_checkout(up->rd, to); 2022186781Slulf if (error) { 2023186781Slulf xasprintf(&up->errmsg, "%s: Cannot write: %s", 2024186781Slulf fup->temppath, strerror(errno)); 2025186781Slulf goto bad; 2026186781Slulf } 2027186781Slulf line = stream_getln(up->rd, NULL); 2028186781Slulf } 2029186781Slulf stream_close(to); 2030186781Slulf close(orig); 2031186781Slulf 2032186781Slulf sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); 2033186781Slulf if (sr->sr_clientattr == NULL) 2034186781Slulf return (UPDATER_ERR_PROTO); 2035186781Slulf fattr_override(sr->sr_clientattr, sr->sr_serverattr, 2036186781Slulf FA_MODTIME | FA_MASK); 2037186781Slulf 2038186781Slulf error = updater_updatefile(up, fup, md5, 0); 2039186781Slulf fup->wantmd5 = NULL; /* So that it doesn't get freed. */ 2040186781Slulfbad: 2041186781Slulf free(buf); 2042186781Slulf return (error); 2043186781Slulf} 2044