1156230Smux/*- 2156230Smux * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> 3156230Smux * All rights reserved. 4156230Smux * 5156230Smux * Redistribution and use in source and binary forms, with or without 6156230Smux * modification, are permitted provided that the following conditions 7156230Smux * are met: 8156230Smux * 1. Redistributions of source code must retain the above copyright 9156230Smux * notice, this list of conditions and the following disclaimer. 10156230Smux * 2. Redistributions in binary form must reproduce the above copyright 11156230Smux * notice, this list of conditions and the following disclaimer in the 12156230Smux * documentation and/or other materials provided with the distribution. 13156230Smux * 14156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17156230Smux * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24156230Smux * SUCH DAMAGE. 25156230Smux * 26156230Smux * $FreeBSD$ 27156230Smux */ 28156230Smux 29156230Smux#include <assert.h> 30156230Smux#include <errno.h> 31156230Smux#include <fcntl.h> 32156230Smux#include <stdio.h> 33156230Smux#include <stdlib.h> 34156230Smux#include <string.h> 35156230Smux#include <unistd.h> 36156230Smux 37156230Smux#include "config.h" 38156230Smux#include "fattr.h" 39156230Smux#include "misc.h" 40156230Smux#include "pathcomp.h" 41156230Smux#include "proto.h" 42156230Smux#include "queue.h" 43156230Smux#include "status.h" 44156230Smux#include "stream.h" 45156230Smux 46156230Smux#define STATUS_VERSION 5 47156230Smux 48156230Smux/* Internal error codes. */ 49156230Smux#define STATUS_ERR_READ (-1) 50156230Smux#define STATUS_ERR_WRITE (-2) 51156230Smux#define STATUS_ERR_PARSE (-3) 52156230Smux#define STATUS_ERR_UNSORTED (-4) 53156230Smux#define STATUS_ERR_TRUNC (-5) 54156230Smux#define STATUS_ERR_BOGUS_DIRUP (-6) 55156230Smux#define STATUS_ERR_BAD_TYPE (-7) 56156230Smux#define STATUS_ERR_RENAME (-8) 57156230Smux 58156230Smuxstatic struct status *status_new(char *, time_t, struct stream *); 59156230Smuxstatic struct statusrec *status_rd(struct status *); 60156230Smuxstatic struct statusrec *status_rdraw(struct status *, char **); 61156230Smuxstatic int status_wr(struct status *, struct statusrec *); 62156230Smuxstatic int status_wrraw(struct status *, struct statusrec *, 63156230Smux char *); 64156230Smuxstatic struct status *status_fromrd(char *, struct stream *); 65156230Smuxstatic struct status *status_fromnull(char *); 66156230Smuxstatic void status_free(struct status *); 67156230Smux 68156230Smuxstatic void statusrec_init(struct statusrec *); 69156230Smuxstatic void statusrec_fini(struct statusrec *); 70156230Smuxstatic int statusrec_cook(struct statusrec *, char *); 71156230Smuxstatic int statusrec_cmp(struct statusrec *, struct statusrec *); 72156230Smux 73156230Smuxstruct status { 74156230Smux char *path; 75156230Smux char *tempfile; 76156230Smux int error; 77156230Smux int suberror; 78156230Smux struct pathcomp *pc; 79156230Smux struct statusrec buf; 80156230Smux struct statusrec *previous; 81156230Smux struct statusrec *current; 82156230Smux struct stream *rd; 83156230Smux struct stream *wr; 84156230Smux time_t scantime; 85156230Smux int eof; 86156230Smux int linenum; 87156230Smux int depth; 88156230Smux int dirty; 89156230Smux}; 90156230Smux 91156230Smuxstatic void 92156230Smuxstatusrec_init(struct statusrec *sr) 93156230Smux{ 94156230Smux 95156230Smux memset(sr, 0, sizeof(*sr)); 96156230Smux} 97156230Smux 98156230Smuxstatic int 99156230Smuxstatusrec_cook(struct statusrec *sr, char *line) 100156230Smux{ 101156230Smux char *clientattr, *serverattr; 102156230Smux 103156230Smux switch (sr->sr_type) { 104186781Slulf case SR_FILEDEAD: 105186781Slulf case SR_FILELIVE: 106186781Slulf clientattr = proto_get_ascii(&line); 107186781Slulf if (clientattr == NULL || line != NULL) 108186781Slulf return (-1); 109186781Slulf sr->sr_clientattr = fattr_decode(clientattr); 110186781Slulf if (sr->sr_clientattr == NULL) 111186781Slulf return (-1); 112186781Slulf break; 113156230Smux case SR_DIRDOWN: 114156230Smux /* Nothing to do. */ 115156230Smux if (line != NULL) 116156230Smux return (-1); 117156230Smux break; 118156230Smux case SR_CHECKOUTLIVE: 119156230Smux sr->sr_tag = proto_get_ascii(&line); 120156230Smux sr->sr_date = proto_get_ascii(&line); 121156230Smux serverattr = proto_get_ascii(&line); 122156230Smux sr->sr_revnum = proto_get_ascii(&line); 123156230Smux sr->sr_revdate = proto_get_ascii(&line); 124156230Smux clientattr = proto_get_ascii(&line); 125156230Smux if (clientattr == NULL || line != NULL) 126156230Smux return (-1); 127156230Smux sr->sr_serverattr = fattr_decode(serverattr); 128156230Smux if (sr->sr_serverattr == NULL) 129156230Smux return (-1); 130156230Smux sr->sr_clientattr = fattr_decode(clientattr); 131156230Smux if (sr->sr_clientattr == NULL) { 132156230Smux fattr_free(sr->sr_serverattr); 133156230Smux return (-1); 134156230Smux } 135156230Smux break; 136156230Smux case SR_CHECKOUTDEAD: 137156230Smux sr->sr_tag = proto_get_ascii(&line); 138156230Smux sr->sr_date = proto_get_ascii(&line); 139156230Smux serverattr = proto_get_ascii(&line); 140156230Smux if (serverattr == NULL || line != NULL) 141156230Smux return (-1); 142156230Smux sr->sr_serverattr = fattr_decode(serverattr); 143156230Smux if (sr->sr_serverattr == NULL) 144156230Smux return (-1); 145156230Smux break; 146156230Smux case SR_DIRUP: 147156230Smux clientattr = proto_get_ascii(&line); 148156230Smux if (clientattr == NULL || line != NULL) 149156230Smux return (-1); 150156230Smux sr->sr_clientattr = fattr_decode(clientattr); 151156230Smux if (sr->sr_clientattr == NULL) 152156230Smux return (-1); 153156230Smux break; 154156230Smux default: 155156230Smux return (-1); 156156230Smux } 157156230Smux return (0); 158156230Smux} 159156230Smux 160156230Smuxstatic struct statusrec * 161156230Smuxstatus_rd(struct status *st) 162156230Smux{ 163156230Smux struct statusrec *sr; 164156230Smux char *line; 165156230Smux int error; 166156230Smux 167156230Smux sr = status_rdraw(st, &line); 168156230Smux if (sr == NULL) 169156230Smux return (NULL); 170156230Smux error = statusrec_cook(sr, line); 171156230Smux if (error) { 172156230Smux st->error = STATUS_ERR_PARSE; 173156230Smux return (NULL); 174156230Smux } 175156230Smux return (sr); 176156230Smux} 177156230Smux 178156230Smuxstatic struct statusrec * 179156230Smuxstatus_rdraw(struct status *st, char **linep) 180156230Smux{ 181156230Smux struct statusrec sr; 182156230Smux char *cmd, *line, *file; 183156230Smux 184156230Smux if (st->rd == NULL || st->eof) 185156230Smux return (NULL); 186156230Smux line = stream_getln(st->rd, NULL); 187156230Smux if (line == NULL) { 188156230Smux if (stream_eof(st->rd)) { 189156230Smux if (st->depth != 0) { 190156230Smux st->error = STATUS_ERR_TRUNC; 191156230Smux return (NULL); 192156230Smux } 193156230Smux st->eof = 1; 194156230Smux return (NULL); 195156230Smux } 196156230Smux st->error = STATUS_ERR_READ; 197156230Smux st->suberror = errno; 198156230Smux return (NULL); 199156230Smux } 200156230Smux st->linenum++; 201156230Smux cmd = proto_get_ascii(&line); 202156230Smux file = proto_get_ascii(&line); 203156230Smux if (file == NULL || strlen(cmd) != 1) { 204156230Smux st->error = STATUS_ERR_PARSE; 205156230Smux return (NULL); 206156230Smux } 207156230Smux 208156230Smux switch (cmd[0]) { 209186781Slulf case 'A': 210186781Slulf sr.sr_type = SR_FILELIVE; 211186781Slulf break; 212156230Smux case 'D': 213156230Smux sr.sr_type = SR_DIRDOWN; 214156230Smux st->depth++; 215156230Smux break; 216156230Smux case 'C': 217156230Smux sr.sr_type = SR_CHECKOUTLIVE; 218156230Smux break; 219156230Smux case 'c': 220156230Smux sr.sr_type = SR_CHECKOUTDEAD; 221156230Smux break; 222156230Smux case 'U': 223156230Smux sr.sr_type = SR_DIRUP; 224156230Smux if (st->depth <= 0) { 225156230Smux st->error = STATUS_ERR_BOGUS_DIRUP; 226156230Smux return (NULL); 227156230Smux } 228156230Smux st->depth--; 229156230Smux break; 230186781Slulf case 'V': 231186781Slulf sr.sr_type = SR_FILELIVE; 232186781Slulf break; 233186781Slulf case 'v': 234186781Slulf sr.sr_type = SR_FILEDEAD; 235186781Slulf break; 236156230Smux default: 237156230Smux st->error = STATUS_ERR_BAD_TYPE; 238156230Smux st->suberror = cmd[0]; 239156230Smux return (NULL); 240156230Smux } 241156230Smux 242156230Smux sr.sr_file = xstrdup(file); 243156230Smux if (st->previous != NULL && 244156230Smux statusrec_cmp(st->previous, &sr) >= 0) { 245156230Smux st->error = STATUS_ERR_UNSORTED; 246156230Smux free(sr.sr_file); 247156230Smux return (NULL); 248156230Smux } 249156230Smux 250156230Smux if (st->previous == NULL) { 251156230Smux st->previous = &st->buf; 252156230Smux } else { 253156230Smux statusrec_fini(st->previous); 254156230Smux statusrec_init(st->previous); 255156230Smux } 256156230Smux st->previous->sr_type = sr.sr_type; 257156230Smux st->previous->sr_file = sr.sr_file; 258156230Smux *linep = line; 259156230Smux return (st->previous); 260156230Smux} 261156230Smux 262156230Smuxstatic int 263156230Smuxstatus_wr(struct status *st, struct statusrec *sr) 264156230Smux{ 265156230Smux struct pathcomp *pc; 266156230Smux const struct fattr *fa; 267156230Smux char *name; 268156230Smux int error, type, usedirupattr; 269156230Smux 270156230Smux pc = st->pc; 271156230Smux error = 0; 272156230Smux usedirupattr = 0; 273156230Smux if (sr->sr_type == SR_DIRDOWN) { 274156230Smux pathcomp_put(pc, PC_DIRDOWN, sr->sr_file); 275156230Smux } else if (sr->sr_type == SR_DIRUP) { 276156230Smux pathcomp_put(pc, PC_DIRUP, sr->sr_file); 277156230Smux usedirupattr = 1; 278156230Smux } else { 279156230Smux pathcomp_put(pc, PC_FILE, sr->sr_file); 280156230Smux } 281156230Smux 282156230Smux while (pathcomp_get(pc, &type, &name)) { 283156230Smux if (type == PC_DIRDOWN) { 284156230Smux error = proto_printf(st->wr, "D %s\n", name); 285156230Smux } else if (type == PC_DIRUP) { 286156230Smux if (usedirupattr) 287156230Smux fa = sr->sr_clientattr; 288156230Smux else 289156230Smux fa = fattr_bogus; 290156230Smux usedirupattr = 0; 291156230Smux error = proto_printf(st->wr, "U %s %f\n", name, fa); 292156230Smux } 293156230Smux if (error) 294156230Smux goto bad; 295156230Smux } 296156230Smux 297156230Smux switch (sr->sr_type) { 298156230Smux case SR_DIRDOWN: 299156230Smux case SR_DIRUP: 300156230Smux /* Already emitted above. */ 301156230Smux break; 302156230Smux case SR_CHECKOUTLIVE: 303156230Smux error = proto_printf(st->wr, "C %s %s %s %f %s %s %f\n", 304156230Smux sr->sr_file, sr->sr_tag, sr->sr_date, sr->sr_serverattr, 305156230Smux sr->sr_revnum, sr->sr_revdate, sr->sr_clientattr); 306156230Smux break; 307156230Smux case SR_CHECKOUTDEAD: 308156230Smux error = proto_printf(st->wr, "c %s %s %s %f\n", sr->sr_file, 309156230Smux sr->sr_tag, sr->sr_date, sr->sr_serverattr); 310156230Smux break; 311186781Slulf case SR_FILELIVE: 312186781Slulf error = proto_printf(st->wr, "V %s %f\n", sr->sr_file, 313186781Slulf sr->sr_clientattr); 314186781Slulf break; 315186781Slulf case SR_FILEDEAD: 316186781Slulf error = proto_printf(st->wr, "v %s %f\n", sr->sr_file, 317186781Slulf sr->sr_clientattr); 318186781Slulf break; 319156230Smux } 320156230Smux if (error) 321156230Smux goto bad; 322156230Smux return (0); 323156230Smuxbad: 324156230Smux st->error = STATUS_ERR_WRITE; 325156230Smux st->suberror = errno; 326156230Smux return (-1); 327156230Smux} 328156230Smux 329156230Smuxstatic int 330156230Smuxstatus_wrraw(struct status *st, struct statusrec *sr, char *line) 331156230Smux{ 332156230Smux char *name; 333156230Smux char cmd; 334156230Smux int error, ret, type; 335156230Smux 336156230Smux if (st->wr == NULL) 337156230Smux return (0); 338156230Smux 339156230Smux /* 340156230Smux * Keep the compressor in sync. At this point, the necessary 341156230Smux * DirDowns and DirUps should have already been emitted, so the 342156230Smux * compressor should return exactly one value in the PC_DIRDOWN 343156230Smux * and PC_DIRUP case and none in the PC_FILE case. 344156230Smux */ 345156230Smux if (sr->sr_type == SR_DIRDOWN) 346156230Smux pathcomp_put(st->pc, PC_DIRDOWN, sr->sr_file); 347156230Smux else if (sr->sr_type == SR_DIRUP) 348156230Smux pathcomp_put(st->pc, PC_DIRUP, sr->sr_file); 349156230Smux else 350156230Smux pathcomp_put(st->pc, PC_FILE, sr->sr_file); 351156230Smux if (sr->sr_type == SR_DIRDOWN || sr->sr_type == SR_DIRUP) { 352156230Smux ret = pathcomp_get(st->pc, &type, &name); 353156230Smux assert(ret); 354156230Smux if (sr->sr_type == SR_DIRDOWN) 355156230Smux assert(type == PC_DIRDOWN); 356156230Smux else 357156230Smux assert(type == PC_DIRUP); 358156230Smux } 359156230Smux ret = pathcomp_get(st->pc, &type, &name); 360156230Smux assert(!ret); 361156230Smux 362156230Smux switch (sr->sr_type) { 363156230Smux case SR_DIRDOWN: 364156230Smux cmd = 'D'; 365156230Smux break; 366156230Smux case SR_DIRUP: 367156230Smux cmd = 'U'; 368156230Smux break; 369156230Smux case SR_CHECKOUTLIVE: 370156230Smux cmd = 'C'; 371156230Smux break; 372156230Smux case SR_CHECKOUTDEAD: 373156230Smux cmd = 'c'; 374156230Smux break; 375186781Slulf case SR_FILELIVE: 376186781Slulf cmd = 'V'; 377186781Slulf break; 378186781Slulf case SR_FILEDEAD: 379186781Slulf cmd = 'v'; 380186781Slulf break; 381156230Smux default: 382156230Smux assert(0); 383156230Smux return (-1); 384156230Smux } 385156230Smux if (sr->sr_type == SR_DIRDOWN) 386156230Smux error = proto_printf(st->wr, "%c %S\n", cmd, sr->sr_file); 387156230Smux else 388156230Smux error = proto_printf(st->wr, "%c %s %S\n", cmd, sr->sr_file, 389156230Smux line); 390156230Smux if (error) { 391156230Smux st->error = STATUS_ERR_WRITE; 392156230Smux st->suberror = errno; 393156230Smux return (-1); 394156230Smux } 395156230Smux return (0); 396156230Smux} 397156230Smux 398156230Smuxstatic void 399156230Smuxstatusrec_fini(struct statusrec *sr) 400156230Smux{ 401156230Smux 402156230Smux fattr_free(sr->sr_serverattr); 403156230Smux fattr_free(sr->sr_clientattr); 404156230Smux free(sr->sr_file); 405156230Smux} 406156230Smux 407156230Smuxstatic int 408156230Smuxstatusrec_cmp(struct statusrec *a, struct statusrec *b) 409156230Smux{ 410156230Smux size_t lena, lenb; 411156230Smux 412156230Smux if (a->sr_type == SR_DIRUP || b->sr_type == SR_DIRUP) { 413156230Smux lena = strlen(a->sr_file); 414156230Smux lenb = strlen(b->sr_file); 415156230Smux if (a->sr_type == SR_DIRUP && 416156230Smux ((lena < lenb && b->sr_file[lena] == '/') || lena == lenb) 417156230Smux && strncmp(a->sr_file, b->sr_file, lena) == 0) 418156230Smux return (1); 419156230Smux if (b->sr_type == SR_DIRUP && 420156230Smux ((lenb < lena && a->sr_file[lenb] == '/') || lenb == lena) 421156230Smux && strncmp(a->sr_file, b->sr_file, lenb) == 0) 422156230Smux return (-1); 423156230Smux } 424156230Smux return (pathcmp(a->sr_file, b->sr_file)); 425156230Smux} 426156230Smux 427156230Smuxstatic struct status * 428156230Smuxstatus_new(char *path, time_t scantime, struct stream *file) 429156230Smux{ 430156230Smux struct status *st; 431156230Smux 432156230Smux st = xmalloc(sizeof(struct status)); 433156230Smux st->path = path; 434156230Smux st->error = 0; 435156230Smux st->suberror = 0; 436156230Smux st->tempfile = NULL; 437156230Smux st->scantime = scantime; 438156230Smux st->rd = file; 439156230Smux st->wr = NULL; 440156230Smux st->previous = NULL; 441156230Smux st->current = NULL; 442156230Smux st->dirty = 0; 443156230Smux st->eof = 0; 444156230Smux st->linenum = 0; 445156230Smux st->depth = 0; 446156230Smux st->pc = pathcomp_new(); 447156230Smux statusrec_init(&st->buf); 448156230Smux return (st); 449156230Smux} 450156230Smux 451156230Smuxstatic void 452156230Smuxstatus_free(struct status *st) 453156230Smux{ 454156230Smux 455156230Smux if (st->previous != NULL) 456156230Smux statusrec_fini(st->previous); 457156230Smux if (st->rd != NULL) 458156230Smux stream_close(st->rd); 459156230Smux if (st->wr != NULL) 460156230Smux stream_close(st->wr); 461156230Smux if (st->tempfile != NULL) 462156230Smux free(st->tempfile); 463156230Smux free(st->path); 464156230Smux pathcomp_free(st->pc); 465156230Smux free(st); 466156230Smux} 467156230Smux 468156230Smuxstatic struct status * 469156230Smuxstatus_fromrd(char *path, struct stream *file) 470156230Smux{ 471156230Smux struct status *st; 472156230Smux char *id, *line; 473156230Smux time_t scantime; 474156230Smux int error, ver; 475156230Smux 476156230Smux /* Get the first line of the file and validate it. */ 477156230Smux line = stream_getln(file, NULL); 478156230Smux if (line == NULL) { 479156230Smux stream_close(file); 480156230Smux return (NULL); 481156230Smux } 482156230Smux id = proto_get_ascii(&line); 483156230Smux error = proto_get_int(&line, &ver, 10); 484156230Smux if (error) { 485156230Smux stream_close(file); 486156230Smux return (NULL); 487156230Smux } 488156230Smux error = proto_get_time(&line, &scantime); 489156230Smux if (error || line != NULL) { 490156230Smux stream_close(file); 491156230Smux return (NULL); 492156230Smux } 493156230Smux 494156230Smux if (strcmp(id, "F") != 0 || ver != STATUS_VERSION) { 495156230Smux stream_close(file); 496156230Smux return (NULL); 497156230Smux } 498156230Smux 499156230Smux st = status_new(path, scantime, file); 500156230Smux st->linenum = 1; 501156230Smux return (st); 502156230Smux} 503156230Smux 504156230Smuxstatic struct status * 505156230Smuxstatus_fromnull(char *path) 506156230Smux{ 507156230Smux struct status *st; 508156230Smux 509156230Smux st = status_new(path, -1, NULL); 510156230Smux st->eof = 1; 511156230Smux return (st); 512156230Smux} 513156230Smux 514156230Smux/* 515156230Smux * Open the status file. If scantime is not -1, the file is opened 516156230Smux * for updating, otherwise, it is opened read-only. If the status file 517156230Smux * couldn't be opened, NULL is returned and errmsg is set to the error 518156230Smux * message. 519156230Smux */ 520156230Smuxstruct status * 521156230Smuxstatus_open(struct coll *coll, time_t scantime, char **errmsg) 522156230Smux{ 523156230Smux struct status *st; 524156230Smux struct stream *file; 525156230Smux struct fattr *fa; 526156230Smux char *destpath, *path; 527156230Smux int error, rv; 528156230Smux 529156230Smux path = coll_statuspath(coll); 530156230Smux file = stream_open_file(path, O_RDONLY); 531156230Smux if (file == NULL) { 532156230Smux if (errno != ENOENT) { 533156230Smux xasprintf(errmsg, "Could not open \"%s\": %s\n", 534156230Smux path, strerror(errno)); 535156230Smux free(path); 536156230Smux return (NULL); 537156230Smux } 538156230Smux st = status_fromnull(path); 539156230Smux } else { 540156230Smux st = status_fromrd(path, file); 541156230Smux if (st == NULL) { 542156230Smux xasprintf(errmsg, "Error in \"%s\": Bad header line", 543156230Smux path); 544156230Smux free(path); 545156230Smux return (NULL); 546156230Smux } 547156230Smux } 548156230Smux 549156230Smux if (scantime != -1) { 550156230Smux /* Open for writing too. */ 551156230Smux xasprintf(&destpath, "%s/%s/%s/", coll->co_base, 552156230Smux coll->co_colldir, coll->co_name); 553156230Smux st->tempfile = tempname(destpath); 554156230Smux if (mkdirhier(destpath, coll->co_umask) != 0) { 555156230Smux xasprintf(errmsg, "Cannot create directories leading " 556156230Smux "to \"%s\": %s", destpath, strerror(errno)); 557156230Smux free(destpath); 558156230Smux status_free(st); 559156230Smux return (NULL); 560156230Smux } 561156230Smux free(destpath); 562156230Smux st->wr = stream_open_file(st->tempfile, 563156230Smux O_CREAT | O_TRUNC | O_WRONLY, 0644); 564156230Smux if (st->wr == NULL) { 565156230Smux xasprintf(errmsg, "Cannot create \"%s\": %s", 566156230Smux st->tempfile, strerror(errno)); 567156230Smux status_free(st); 568156230Smux return (NULL); 569156230Smux } 570156230Smux fa = fattr_new(FT_FILE, -1); 571156230Smux fattr_mergedefault(fa); 572156230Smux fattr_umask(fa, coll->co_umask); 573156230Smux rv = fattr_install(fa, st->tempfile, NULL); 574156230Smux fattr_free(fa); 575156230Smux if (rv == -1) { 576156230Smux xasprintf(errmsg, 577156230Smux "Cannot set attributes for \"%s\": %s", 578156230Smux st->tempfile, strerror(errno)); 579156230Smux status_free(st); 580156230Smux return (NULL); 581156230Smux } 582156230Smux if (scantime != st->scantime) 583156230Smux st->dirty = 1; 584156230Smux error = proto_printf(st->wr, "F %d %t\n", STATUS_VERSION, 585156230Smux scantime); 586156230Smux if (error) { 587156230Smux st->error = STATUS_ERR_WRITE; 588156230Smux st->suberror = errno; 589156230Smux *errmsg = status_errmsg(st); 590156230Smux status_free(st); 591156230Smux return (NULL); 592156230Smux } 593156230Smux } 594156230Smux return (st); 595156230Smux} 596156230Smux 597156230Smux/* 598156230Smux * Get an entry from the status file. If name is NULL, the next entry 599156230Smux * is returned. If name is not NULL, the entry matching this name is 600156230Smux * returned, or NULL if it couldn't be found. If deleteto is set to 1, 601156230Smux * all the entries read from the status file while looking for the 602156230Smux * given name are deleted. 603156230Smux */ 604156230Smuxint 605156230Smuxstatus_get(struct status *st, char *name, int isdirup, int deleteto, 606156230Smux struct statusrec **psr) 607156230Smux{ 608156230Smux struct statusrec key; 609156230Smux struct statusrec *sr; 610156230Smux char *line; 611156230Smux int c, error; 612156230Smux 613156230Smux if (st->eof) 614156230Smux return (0); 615156230Smux 616156230Smux if (st->error) 617156230Smux return (-1); 618156230Smux 619156230Smux if (name == NULL) { 620156230Smux sr = status_rd(st); 621156230Smux if (sr == NULL) { 622156230Smux if (st->error) 623156230Smux return (-1); 624156230Smux return (0); 625156230Smux } 626156230Smux *psr = sr; 627156230Smux return (1); 628156230Smux } 629156230Smux 630156230Smux if (st->current != NULL) { 631156230Smux sr = st->current; 632156230Smux st->current = NULL; 633156230Smux } else { 634156230Smux sr = status_rd(st); 635156230Smux if (sr == NULL) { 636156230Smux if (st->error) 637156230Smux return (-1); 638156230Smux return (0); 639156230Smux } 640156230Smux } 641156230Smux 642156230Smux key.sr_file = name; 643156230Smux if (isdirup) 644156230Smux key.sr_type = SR_DIRUP; 645156230Smux else 646156230Smux key.sr_type = SR_CHECKOUTLIVE; 647156230Smux 648156230Smux c = statusrec_cmp(sr, &key); 649156230Smux if (c < 0) { 650156230Smux if (st->wr != NULL && !deleteto) { 651156230Smux error = status_wr(st, sr); 652156230Smux if (error) 653156230Smux return (-1); 654156230Smux } 655156230Smux /* Loop until we find the good entry. */ 656156230Smux for (;;) { 657156230Smux sr = status_rdraw(st, &line); 658156230Smux if (sr == NULL) { 659156230Smux if (st->error) 660156230Smux return (-1); 661156230Smux return (0); 662156230Smux } 663156230Smux c = statusrec_cmp(sr, &key); 664156230Smux if (c >= 0) 665156230Smux break; 666156230Smux if (st->wr != NULL && !deleteto) { 667156230Smux error = status_wrraw(st, sr, line); 668156230Smux if (error) 669156230Smux return (-1); 670156230Smux } 671156230Smux } 672156230Smux error = statusrec_cook(sr, line); 673156230Smux if (error) { 674156230Smux st->error = STATUS_ERR_PARSE; 675156230Smux return (-1); 676156230Smux } 677156230Smux } 678156230Smux st->current = sr; 679156230Smux if (c != 0) 680156230Smux return (0); 681156230Smux *psr = sr; 682156230Smux return (1); 683156230Smux} 684156230Smux 685156230Smux/* 686156230Smux * Put this entry into the status file. If an entry with the same name 687156230Smux * existed in the status file, it is replaced by this one, otherwise, 688156230Smux * the entry is added to the status file. 689156230Smux */ 690156230Smuxint 691156230Smuxstatus_put(struct status *st, struct statusrec *sr) 692156230Smux{ 693156230Smux struct statusrec *old; 694156230Smux int error, ret; 695156230Smux 696156230Smux ret = status_get(st, sr->sr_file, sr->sr_type == SR_DIRUP, 0, &old); 697156230Smux if (ret == -1) 698156230Smux return (-1); 699156230Smux if (ret) { 700156230Smux if (old->sr_type == SR_DIRDOWN) { 701156230Smux /* DirUp should never match DirDown */ 702156230Smux assert(old->sr_type != SR_DIRUP); 703156230Smux if (sr->sr_type == SR_CHECKOUTLIVE || 704156230Smux sr->sr_type == SR_CHECKOUTDEAD) { 705156230Smux /* We are replacing a directory with a file. 706156230Smux Delete all entries inside the directory we 707156230Smux are replacing. */ 708156230Smux ret = status_get(st, sr->sr_file, 1, 1, &old); 709156230Smux if (ret == -1) 710156230Smux return (-1); 711156230Smux assert(ret); 712156230Smux } 713156230Smux } else 714156230Smux st->current = NULL; 715156230Smux } 716156230Smux st->dirty = 1; 717156230Smux error = status_wr(st, sr); 718156230Smux if (error) 719156230Smux return (-1); 720156230Smux return (0); 721156230Smux} 722156230Smux 723156230Smux/* 724156230Smux * Delete the specified entry from the status file. 725156230Smux */ 726156230Smuxint 727156230Smuxstatus_delete(struct status *st, char *name, int isdirup) 728156230Smux{ 729156230Smux struct statusrec *sr; 730156230Smux int ret; 731156230Smux 732156230Smux ret = status_get(st, name, isdirup, 0, &sr); 733156230Smux if (ret == -1) 734156230Smux return (-1); 735156230Smux if (ret) { 736156230Smux st->current = NULL; 737156230Smux st->dirty = 1; 738156230Smux } 739156230Smux return (0); 740156230Smux} 741156230Smux 742156230Smux/* 743156230Smux * Check whether we hit the end of file. 744156230Smux */ 745156230Smuxint 746156230Smuxstatus_eof(struct status *st) 747156230Smux{ 748156230Smux 749156230Smux return (st->eof); 750156230Smux} 751156230Smux 752156230Smux/* 753156230Smux * Returns the error message if there was an error, otherwise returns 754156230Smux * NULL. The error message is allocated dynamically and needs to be 755156230Smux * freed by the caller after use. 756156230Smux */ 757156230Smuxchar * 758156230Smuxstatus_errmsg(struct status *st) 759156230Smux{ 760156230Smux char *errmsg; 761156230Smux 762156230Smux if (!st->error) 763156230Smux return (NULL); 764156230Smux switch (st->error) { 765156230Smux case STATUS_ERR_READ: 766156230Smux xasprintf(&errmsg, "Read failure on \"%s\": %s", 767156230Smux st->path, strerror(st->suberror)); 768156230Smux break; 769156230Smux case STATUS_ERR_WRITE: 770156230Smux xasprintf(&errmsg, "Write failure on \"%s\": %s", 771156230Smux st->tempfile, strerror(st->suberror)); 772156230Smux break; 773156230Smux case STATUS_ERR_PARSE: 774156230Smux xasprintf(&errmsg, "Error in \"%s\": %d: " 775156230Smux "Could not parse status record", st->path, st->linenum); 776156230Smux break; 777156230Smux case STATUS_ERR_UNSORTED: 778156230Smux xasprintf(&errmsg, "Error in \"%s\": %d: " 779156230Smux "File is not sorted properly", st->path, st->linenum); 780156230Smux break; 781156230Smux case STATUS_ERR_TRUNC: 782156230Smux xasprintf(&errmsg, "Error in \"%s\": " 783156230Smux "File is truncated", st->path); 784156230Smux break; 785156230Smux case STATUS_ERR_BOGUS_DIRUP: 786156230Smux xasprintf(&errmsg, "Error in \"%s\": %d: " 787156230Smux "\"U\" entry has no matching \"D\"", st->path, st->linenum); 788156230Smux break; 789156230Smux case STATUS_ERR_BAD_TYPE: 790156230Smux xasprintf(&errmsg, "Error in \"%s\": %d: " 791156230Smux "Invalid file type \"%c\"", st->path, st->linenum, 792156230Smux st->suberror); 793156230Smux break; 794156230Smux case STATUS_ERR_RENAME: 795156230Smux xasprintf(&errmsg, "Cannot rename \"%s\" to \"%s\": %s", 796156230Smux st->tempfile, st->path, strerror(st->suberror)); 797156230Smux break; 798156230Smux default: 799156230Smux assert(0); 800156230Smux return (NULL); 801156230Smux } 802156230Smux return (errmsg); 803156230Smux} 804156230Smux 805156230Smux/* 806156230Smux * Close the status file and free any resource associated with it. 807156230Smux * It is OK to pass NULL for errmsg only if the status file was 808156230Smux * opened read-only. If it wasn't opened read-only, status_close() 809156230Smux * can produce an error and errmsg is not allowed to be NULL. If 810156230Smux * there was no errors, errmsg is set to NULL. 811156230Smux */ 812156230Smuxvoid 813156230Smuxstatus_close(struct status *st, char **errmsg) 814156230Smux{ 815156230Smux struct statusrec *sr; 816156230Smux char *line, *name; 817156230Smux int error, type; 818156230Smux 819156230Smux if (st->wr != NULL) { 820156230Smux if (st->dirty) { 821156230Smux if (st->current != NULL) { 822156230Smux error = status_wr(st, st->current); 823156230Smux if (error) { 824156230Smux *errmsg = status_errmsg(st); 825156230Smux goto bad; 826156230Smux } 827156230Smux st->current = NULL; 828156230Smux } 829156230Smux /* Copy the rest of the file. */ 830156230Smux while ((sr = status_rdraw(st, &line)) != NULL) { 831156230Smux error = status_wrraw(st, sr, line); 832156230Smux if (error) { 833156230Smux *errmsg = status_errmsg(st); 834156230Smux goto bad; 835156230Smux } 836156230Smux } 837156230Smux if (st->error) { 838156230Smux *errmsg = status_errmsg(st); 839156230Smux goto bad; 840156230Smux } 841156230Smux 842156230Smux /* Close off all the open directories. */ 843156230Smux pathcomp_finish(st->pc); 844156230Smux while (pathcomp_get(st->pc, &type, &name)) { 845156230Smux assert(type == PC_DIRUP); 846156230Smux error = proto_printf(st->wr, "U %s %f\n", 847156230Smux name, fattr_bogus); 848156230Smux if (error) { 849156230Smux st->error = STATUS_ERR_WRITE; 850156230Smux st->suberror = errno; 851156230Smux *errmsg = status_errmsg(st); 852156230Smux goto bad; 853156230Smux } 854156230Smux } 855156230Smux 856156230Smux /* Rename tempfile. */ 857156230Smux error = rename(st->tempfile, st->path); 858156230Smux if (error) { 859156230Smux st->error = STATUS_ERR_RENAME; 860156230Smux st->suberror = errno; 861156230Smux *errmsg = status_errmsg(st); 862156230Smux goto bad; 863156230Smux } 864156230Smux } else { 865156230Smux /* Just discard the tempfile. */ 866156230Smux unlink(st->tempfile); 867156230Smux } 868156230Smux *errmsg = NULL; 869156230Smux } 870156230Smux status_free(st); 871156230Smux return; 872156230Smuxbad: 873156230Smux status_free(st); 874156230Smux} 875