status.c revision 156230
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: vendor/csup/dist/contrib/csup/status.c 156230 2006-03-03 04:11:29Z mux $ 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) { 104156230Smux case SR_DIRDOWN: 105156230Smux /* Nothing to do. */ 106156230Smux if (line != NULL) 107156230Smux return (-1); 108156230Smux break; 109156230Smux case SR_CHECKOUTLIVE: 110156230Smux sr->sr_tag = proto_get_ascii(&line); 111156230Smux sr->sr_date = proto_get_ascii(&line); 112156230Smux serverattr = proto_get_ascii(&line); 113156230Smux sr->sr_revnum = proto_get_ascii(&line); 114156230Smux sr->sr_revdate = proto_get_ascii(&line); 115156230Smux clientattr = proto_get_ascii(&line); 116156230Smux if (clientattr == NULL || line != NULL) 117156230Smux return (-1); 118156230Smux sr->sr_serverattr = fattr_decode(serverattr); 119156230Smux if (sr->sr_serverattr == NULL) 120156230Smux return (-1); 121156230Smux sr->sr_clientattr = fattr_decode(clientattr); 122156230Smux if (sr->sr_clientattr == NULL) { 123156230Smux fattr_free(sr->sr_serverattr); 124156230Smux return (-1); 125156230Smux } 126156230Smux break; 127156230Smux case SR_CHECKOUTDEAD: 128156230Smux sr->sr_tag = proto_get_ascii(&line); 129156230Smux sr->sr_date = proto_get_ascii(&line); 130156230Smux serverattr = proto_get_ascii(&line); 131156230Smux if (serverattr == NULL || line != NULL) 132156230Smux return (-1); 133156230Smux sr->sr_serverattr = fattr_decode(serverattr); 134156230Smux if (sr->sr_serverattr == NULL) 135156230Smux return (-1); 136156230Smux break; 137156230Smux case SR_DIRUP: 138156230Smux clientattr = proto_get_ascii(&line); 139156230Smux if (clientattr == NULL || line != NULL) 140156230Smux return (-1); 141156230Smux sr->sr_clientattr = fattr_decode(clientattr); 142156230Smux if (sr->sr_clientattr == NULL) 143156230Smux return (-1); 144156230Smux break; 145156230Smux default: 146156230Smux return (-1); 147156230Smux } 148156230Smux return (0); 149156230Smux} 150156230Smux 151156230Smuxstatic struct statusrec * 152156230Smuxstatus_rd(struct status *st) 153156230Smux{ 154156230Smux struct statusrec *sr; 155156230Smux char *line; 156156230Smux int error; 157156230Smux 158156230Smux sr = status_rdraw(st, &line); 159156230Smux if (sr == NULL) 160156230Smux return (NULL); 161156230Smux error = statusrec_cook(sr, line); 162156230Smux if (error) { 163156230Smux st->error = STATUS_ERR_PARSE; 164156230Smux return (NULL); 165156230Smux } 166156230Smux return (sr); 167156230Smux} 168156230Smux 169156230Smuxstatic struct statusrec * 170156230Smuxstatus_rdraw(struct status *st, char **linep) 171156230Smux{ 172156230Smux struct statusrec sr; 173156230Smux char *cmd, *line, *file; 174156230Smux 175156230Smux if (st->rd == NULL || st->eof) 176156230Smux return (NULL); 177156230Smux line = stream_getln(st->rd, NULL); 178156230Smux if (line == NULL) { 179156230Smux if (stream_eof(st->rd)) { 180156230Smux if (st->depth != 0) { 181156230Smux st->error = STATUS_ERR_TRUNC; 182156230Smux return (NULL); 183156230Smux } 184156230Smux st->eof = 1; 185156230Smux return (NULL); 186156230Smux } 187156230Smux st->error = STATUS_ERR_READ; 188156230Smux st->suberror = errno; 189156230Smux return (NULL); 190156230Smux } 191156230Smux st->linenum++; 192156230Smux cmd = proto_get_ascii(&line); 193156230Smux file = proto_get_ascii(&line); 194156230Smux if (file == NULL || strlen(cmd) != 1) { 195156230Smux st->error = STATUS_ERR_PARSE; 196156230Smux return (NULL); 197156230Smux } 198156230Smux 199156230Smux switch (cmd[0]) { 200156230Smux case 'D': 201156230Smux sr.sr_type = SR_DIRDOWN; 202156230Smux st->depth++; 203156230Smux break; 204156230Smux case 'C': 205156230Smux sr.sr_type = SR_CHECKOUTLIVE; 206156230Smux break; 207156230Smux case 'c': 208156230Smux sr.sr_type = SR_CHECKOUTDEAD; 209156230Smux break; 210156230Smux case 'U': 211156230Smux sr.sr_type = SR_DIRUP; 212156230Smux if (st->depth <= 0) { 213156230Smux st->error = STATUS_ERR_BOGUS_DIRUP; 214156230Smux return (NULL); 215156230Smux } 216156230Smux st->depth--; 217156230Smux break; 218156230Smux default: 219156230Smux st->error = STATUS_ERR_BAD_TYPE; 220156230Smux st->suberror = cmd[0]; 221156230Smux return (NULL); 222156230Smux } 223156230Smux 224156230Smux sr.sr_file = xstrdup(file); 225156230Smux if (st->previous != NULL && 226156230Smux statusrec_cmp(st->previous, &sr) >= 0) { 227156230Smux st->error = STATUS_ERR_UNSORTED; 228156230Smux free(sr.sr_file); 229156230Smux return (NULL); 230156230Smux } 231156230Smux 232156230Smux if (st->previous == NULL) { 233156230Smux st->previous = &st->buf; 234156230Smux } else { 235156230Smux statusrec_fini(st->previous); 236156230Smux statusrec_init(st->previous); 237156230Smux } 238156230Smux st->previous->sr_type = sr.sr_type; 239156230Smux st->previous->sr_file = sr.sr_file; 240156230Smux *linep = line; 241156230Smux return (st->previous); 242156230Smux} 243156230Smux 244156230Smuxstatic int 245156230Smuxstatus_wr(struct status *st, struct statusrec *sr) 246156230Smux{ 247156230Smux struct pathcomp *pc; 248156230Smux const struct fattr *fa; 249156230Smux char *name; 250156230Smux int error, type, usedirupattr; 251156230Smux 252156230Smux pc = st->pc; 253156230Smux error = 0; 254156230Smux usedirupattr = 0; 255156230Smux if (sr->sr_type == SR_DIRDOWN) { 256156230Smux pathcomp_put(pc, PC_DIRDOWN, sr->sr_file); 257156230Smux } else if (sr->sr_type == SR_DIRUP) { 258156230Smux pathcomp_put(pc, PC_DIRUP, sr->sr_file); 259156230Smux usedirupattr = 1; 260156230Smux } else { 261156230Smux pathcomp_put(pc, PC_FILE, sr->sr_file); 262156230Smux } 263156230Smux 264156230Smux while (pathcomp_get(pc, &type, &name)) { 265156230Smux if (type == PC_DIRDOWN) { 266156230Smux error = proto_printf(st->wr, "D %s\n", name); 267156230Smux } else if (type == PC_DIRUP) { 268156230Smux if (usedirupattr) 269156230Smux fa = sr->sr_clientattr; 270156230Smux else 271156230Smux fa = fattr_bogus; 272156230Smux usedirupattr = 0; 273156230Smux error = proto_printf(st->wr, "U %s %f\n", name, fa); 274156230Smux } 275156230Smux if (error) 276156230Smux goto bad; 277156230Smux } 278156230Smux 279156230Smux switch (sr->sr_type) { 280156230Smux case SR_DIRDOWN: 281156230Smux case SR_DIRUP: 282156230Smux /* Already emitted above. */ 283156230Smux break; 284156230Smux case SR_CHECKOUTLIVE: 285156230Smux error = proto_printf(st->wr, "C %s %s %s %f %s %s %f\n", 286156230Smux sr->sr_file, sr->sr_tag, sr->sr_date, sr->sr_serverattr, 287156230Smux sr->sr_revnum, sr->sr_revdate, sr->sr_clientattr); 288156230Smux break; 289156230Smux case SR_CHECKOUTDEAD: 290156230Smux error = proto_printf(st->wr, "c %s %s %s %f\n", sr->sr_file, 291156230Smux sr->sr_tag, sr->sr_date, sr->sr_serverattr); 292156230Smux break; 293156230Smux } 294156230Smux if (error) 295156230Smux goto bad; 296156230Smux return (0); 297156230Smuxbad: 298156230Smux st->error = STATUS_ERR_WRITE; 299156230Smux st->suberror = errno; 300156230Smux return (-1); 301156230Smux} 302156230Smux 303156230Smuxstatic int 304156230Smuxstatus_wrraw(struct status *st, struct statusrec *sr, char *line) 305156230Smux{ 306156230Smux char *name; 307156230Smux char cmd; 308156230Smux int error, ret, type; 309156230Smux 310156230Smux if (st->wr == NULL) 311156230Smux return (0); 312156230Smux 313156230Smux /* 314156230Smux * Keep the compressor in sync. At this point, the necessary 315156230Smux * DirDowns and DirUps should have already been emitted, so the 316156230Smux * compressor should return exactly one value in the PC_DIRDOWN 317156230Smux * and PC_DIRUP case and none in the PC_FILE case. 318156230Smux */ 319156230Smux if (sr->sr_type == SR_DIRDOWN) 320156230Smux pathcomp_put(st->pc, PC_DIRDOWN, sr->sr_file); 321156230Smux else if (sr->sr_type == SR_DIRUP) 322156230Smux pathcomp_put(st->pc, PC_DIRUP, sr->sr_file); 323156230Smux else 324156230Smux pathcomp_put(st->pc, PC_FILE, sr->sr_file); 325156230Smux if (sr->sr_type == SR_DIRDOWN || sr->sr_type == SR_DIRUP) { 326156230Smux ret = pathcomp_get(st->pc, &type, &name); 327156230Smux assert(ret); 328156230Smux if (sr->sr_type == SR_DIRDOWN) 329156230Smux assert(type == PC_DIRDOWN); 330156230Smux else 331156230Smux assert(type == PC_DIRUP); 332156230Smux } 333156230Smux ret = pathcomp_get(st->pc, &type, &name); 334156230Smux assert(!ret); 335156230Smux 336156230Smux switch (sr->sr_type) { 337156230Smux case SR_DIRDOWN: 338156230Smux cmd = 'D'; 339156230Smux break; 340156230Smux case SR_DIRUP: 341156230Smux cmd = 'U'; 342156230Smux break; 343156230Smux case SR_CHECKOUTLIVE: 344156230Smux cmd = 'C'; 345156230Smux break; 346156230Smux case SR_CHECKOUTDEAD: 347156230Smux cmd = 'c'; 348156230Smux break; 349156230Smux default: 350156230Smux assert(0); 351156230Smux return (-1); 352156230Smux } 353156230Smux if (sr->sr_type == SR_DIRDOWN) 354156230Smux error = proto_printf(st->wr, "%c %S\n", cmd, sr->sr_file); 355156230Smux else 356156230Smux error = proto_printf(st->wr, "%c %s %S\n", cmd, sr->sr_file, 357156230Smux line); 358156230Smux if (error) { 359156230Smux st->error = STATUS_ERR_WRITE; 360156230Smux st->suberror = errno; 361156230Smux return (-1); 362156230Smux } 363156230Smux return (0); 364156230Smux} 365156230Smux 366156230Smuxstatic void 367156230Smuxstatusrec_fini(struct statusrec *sr) 368156230Smux{ 369156230Smux 370156230Smux fattr_free(sr->sr_serverattr); 371156230Smux fattr_free(sr->sr_clientattr); 372156230Smux free(sr->sr_file); 373156230Smux} 374156230Smux 375156230Smuxstatic int 376156230Smuxstatusrec_cmp(struct statusrec *a, struct statusrec *b) 377156230Smux{ 378156230Smux size_t lena, lenb; 379156230Smux 380156230Smux if (a->sr_type == SR_DIRUP || b->sr_type == SR_DIRUP) { 381156230Smux lena = strlen(a->sr_file); 382156230Smux lenb = strlen(b->sr_file); 383156230Smux if (a->sr_type == SR_DIRUP && 384156230Smux ((lena < lenb && b->sr_file[lena] == '/') || lena == lenb) 385156230Smux && strncmp(a->sr_file, b->sr_file, lena) == 0) 386156230Smux return (1); 387156230Smux if (b->sr_type == SR_DIRUP && 388156230Smux ((lenb < lena && a->sr_file[lenb] == '/') || lenb == lena) 389156230Smux && strncmp(a->sr_file, b->sr_file, lenb) == 0) 390156230Smux return (-1); 391156230Smux } 392156230Smux return (pathcmp(a->sr_file, b->sr_file)); 393156230Smux} 394156230Smux 395156230Smuxstatic struct status * 396156230Smuxstatus_new(char *path, time_t scantime, struct stream *file) 397156230Smux{ 398156230Smux struct status *st; 399156230Smux 400156230Smux st = xmalloc(sizeof(struct status)); 401156230Smux st->path = path; 402156230Smux st->error = 0; 403156230Smux st->suberror = 0; 404156230Smux st->tempfile = NULL; 405156230Smux st->scantime = scantime; 406156230Smux st->rd = file; 407156230Smux st->wr = NULL; 408156230Smux st->previous = NULL; 409156230Smux st->current = NULL; 410156230Smux st->dirty = 0; 411156230Smux st->eof = 0; 412156230Smux st->linenum = 0; 413156230Smux st->depth = 0; 414156230Smux st->pc = pathcomp_new(); 415156230Smux statusrec_init(&st->buf); 416156230Smux return (st); 417156230Smux} 418156230Smux 419156230Smuxstatic void 420156230Smuxstatus_free(struct status *st) 421156230Smux{ 422156230Smux 423156230Smux if (st->previous != NULL) 424156230Smux statusrec_fini(st->previous); 425156230Smux if (st->rd != NULL) 426156230Smux stream_close(st->rd); 427156230Smux if (st->wr != NULL) 428156230Smux stream_close(st->wr); 429156230Smux if (st->tempfile != NULL) 430156230Smux free(st->tempfile); 431156230Smux free(st->path); 432156230Smux pathcomp_free(st->pc); 433156230Smux free(st); 434156230Smux} 435156230Smux 436156230Smuxstatic struct status * 437156230Smuxstatus_fromrd(char *path, struct stream *file) 438156230Smux{ 439156230Smux struct status *st; 440156230Smux char *id, *line; 441156230Smux time_t scantime; 442156230Smux int error, ver; 443156230Smux 444156230Smux /* Get the first line of the file and validate it. */ 445156230Smux line = stream_getln(file, NULL); 446156230Smux if (line == NULL) { 447156230Smux stream_close(file); 448156230Smux return (NULL); 449156230Smux } 450156230Smux id = proto_get_ascii(&line); 451156230Smux error = proto_get_int(&line, &ver, 10); 452156230Smux if (error) { 453156230Smux stream_close(file); 454156230Smux return (NULL); 455156230Smux } 456156230Smux error = proto_get_time(&line, &scantime); 457156230Smux if (error || line != NULL) { 458156230Smux stream_close(file); 459156230Smux return (NULL); 460156230Smux } 461156230Smux 462156230Smux if (strcmp(id, "F") != 0 || ver != STATUS_VERSION) { 463156230Smux stream_close(file); 464156230Smux return (NULL); 465156230Smux } 466156230Smux 467156230Smux st = status_new(path, scantime, file); 468156230Smux st->linenum = 1; 469156230Smux return (st); 470156230Smux} 471156230Smux 472156230Smuxstatic struct status * 473156230Smuxstatus_fromnull(char *path) 474156230Smux{ 475156230Smux struct status *st; 476156230Smux 477156230Smux st = status_new(path, -1, NULL); 478156230Smux st->eof = 1; 479156230Smux return (st); 480156230Smux} 481156230Smux 482156230Smux/* 483156230Smux * Open the status file. If scantime is not -1, the file is opened 484156230Smux * for updating, otherwise, it is opened read-only. If the status file 485156230Smux * couldn't be opened, NULL is returned and errmsg is set to the error 486156230Smux * message. 487156230Smux */ 488156230Smuxstruct status * 489156230Smuxstatus_open(struct coll *coll, time_t scantime, char **errmsg) 490156230Smux{ 491156230Smux struct status *st; 492156230Smux struct stream *file; 493156230Smux struct fattr *fa; 494156230Smux char *destpath, *path; 495156230Smux int error, rv; 496156230Smux 497156230Smux path = coll_statuspath(coll); 498156230Smux file = stream_open_file(path, O_RDONLY); 499156230Smux if (file == NULL) { 500156230Smux if (errno != ENOENT) { 501156230Smux xasprintf(errmsg, "Could not open \"%s\": %s\n", 502156230Smux path, strerror(errno)); 503156230Smux free(path); 504156230Smux return (NULL); 505156230Smux } 506156230Smux st = status_fromnull(path); 507156230Smux } else { 508156230Smux st = status_fromrd(path, file); 509156230Smux if (st == NULL) { 510156230Smux xasprintf(errmsg, "Error in \"%s\": Bad header line", 511156230Smux path); 512156230Smux free(path); 513156230Smux return (NULL); 514156230Smux } 515156230Smux } 516156230Smux 517156230Smux if (scantime != -1) { 518156230Smux /* Open for writing too. */ 519156230Smux xasprintf(&destpath, "%s/%s/%s/", coll->co_base, 520156230Smux coll->co_colldir, coll->co_name); 521156230Smux st->tempfile = tempname(destpath); 522156230Smux if (mkdirhier(destpath, coll->co_umask) != 0) { 523156230Smux xasprintf(errmsg, "Cannot create directories leading " 524156230Smux "to \"%s\": %s", destpath, strerror(errno)); 525156230Smux free(destpath); 526156230Smux status_free(st); 527156230Smux return (NULL); 528156230Smux } 529156230Smux free(destpath); 530156230Smux st->wr = stream_open_file(st->tempfile, 531156230Smux O_CREAT | O_TRUNC | O_WRONLY, 0644); 532156230Smux if (st->wr == NULL) { 533156230Smux xasprintf(errmsg, "Cannot create \"%s\": %s", 534156230Smux st->tempfile, strerror(errno)); 535156230Smux status_free(st); 536156230Smux return (NULL); 537156230Smux } 538156230Smux fa = fattr_new(FT_FILE, -1); 539156230Smux fattr_mergedefault(fa); 540156230Smux fattr_umask(fa, coll->co_umask); 541156230Smux rv = fattr_install(fa, st->tempfile, NULL); 542156230Smux fattr_free(fa); 543156230Smux if (rv == -1) { 544156230Smux xasprintf(errmsg, 545156230Smux "Cannot set attributes for \"%s\": %s", 546156230Smux st->tempfile, strerror(errno)); 547156230Smux status_free(st); 548156230Smux return (NULL); 549156230Smux } 550156230Smux if (scantime != st->scantime) 551156230Smux st->dirty = 1; 552156230Smux error = proto_printf(st->wr, "F %d %t\n", STATUS_VERSION, 553156230Smux scantime); 554156230Smux if (error) { 555156230Smux st->error = STATUS_ERR_WRITE; 556156230Smux st->suberror = errno; 557156230Smux *errmsg = status_errmsg(st); 558156230Smux status_free(st); 559156230Smux return (NULL); 560156230Smux } 561156230Smux } 562156230Smux return (st); 563156230Smux} 564156230Smux 565156230Smux/* 566156230Smux * Get an entry from the status file. If name is NULL, the next entry 567156230Smux * is returned. If name is not NULL, the entry matching this name is 568156230Smux * returned, or NULL if it couldn't be found. If deleteto is set to 1, 569156230Smux * all the entries read from the status file while looking for the 570156230Smux * given name are deleted. 571156230Smux */ 572156230Smuxint 573156230Smuxstatus_get(struct status *st, char *name, int isdirup, int deleteto, 574156230Smux struct statusrec **psr) 575156230Smux{ 576156230Smux struct statusrec key; 577156230Smux struct statusrec *sr; 578156230Smux char *line; 579156230Smux int c, error; 580156230Smux 581156230Smux if (st->eof) 582156230Smux return (0); 583156230Smux 584156230Smux if (st->error) 585156230Smux return (-1); 586156230Smux 587156230Smux if (name == NULL) { 588156230Smux sr = status_rd(st); 589156230Smux if (sr == NULL) { 590156230Smux if (st->error) 591156230Smux return (-1); 592156230Smux return (0); 593156230Smux } 594156230Smux *psr = sr; 595156230Smux return (1); 596156230Smux } 597156230Smux 598156230Smux if (st->current != NULL) { 599156230Smux sr = st->current; 600156230Smux st->current = NULL; 601156230Smux } else { 602156230Smux sr = status_rd(st); 603156230Smux if (sr == NULL) { 604156230Smux if (st->error) 605156230Smux return (-1); 606156230Smux return (0); 607156230Smux } 608156230Smux } 609156230Smux 610156230Smux key.sr_file = name; 611156230Smux if (isdirup) 612156230Smux key.sr_type = SR_DIRUP; 613156230Smux else 614156230Smux key.sr_type = SR_CHECKOUTLIVE; 615156230Smux 616156230Smux c = statusrec_cmp(sr, &key); 617156230Smux if (c < 0) { 618156230Smux if (st->wr != NULL && !deleteto) { 619156230Smux error = status_wr(st, sr); 620156230Smux if (error) 621156230Smux return (-1); 622156230Smux } 623156230Smux /* Loop until we find the good entry. */ 624156230Smux for (;;) { 625156230Smux sr = status_rdraw(st, &line); 626156230Smux if (sr == NULL) { 627156230Smux if (st->error) 628156230Smux return (-1); 629156230Smux return (0); 630156230Smux } 631156230Smux c = statusrec_cmp(sr, &key); 632156230Smux if (c >= 0) 633156230Smux break; 634156230Smux if (st->wr != NULL && !deleteto) { 635156230Smux error = status_wrraw(st, sr, line); 636156230Smux if (error) 637156230Smux return (-1); 638156230Smux } 639156230Smux } 640156230Smux error = statusrec_cook(sr, line); 641156230Smux if (error) { 642156230Smux st->error = STATUS_ERR_PARSE; 643156230Smux return (-1); 644156230Smux } 645156230Smux } 646156230Smux st->current = sr; 647156230Smux if (c != 0) 648156230Smux return (0); 649156230Smux *psr = sr; 650156230Smux return (1); 651156230Smux} 652156230Smux 653156230Smux/* 654156230Smux * Put this entry into the status file. If an entry with the same name 655156230Smux * existed in the status file, it is replaced by this one, otherwise, 656156230Smux * the entry is added to the status file. 657156230Smux */ 658156230Smuxint 659156230Smuxstatus_put(struct status *st, struct statusrec *sr) 660156230Smux{ 661156230Smux struct statusrec *old; 662156230Smux int error, ret; 663156230Smux 664156230Smux ret = status_get(st, sr->sr_file, sr->sr_type == SR_DIRUP, 0, &old); 665156230Smux if (ret == -1) 666156230Smux return (-1); 667156230Smux if (ret) { 668156230Smux if (old->sr_type == SR_DIRDOWN) { 669156230Smux /* DirUp should never match DirDown */ 670156230Smux assert(old->sr_type != SR_DIRUP); 671156230Smux if (sr->sr_type == SR_CHECKOUTLIVE || 672156230Smux sr->sr_type == SR_CHECKOUTDEAD) { 673156230Smux /* We are replacing a directory with a file. 674156230Smux Delete all entries inside the directory we 675156230Smux are replacing. */ 676156230Smux ret = status_get(st, sr->sr_file, 1, 1, &old); 677156230Smux if (ret == -1) 678156230Smux return (-1); 679156230Smux assert(ret); 680156230Smux } 681156230Smux } else 682156230Smux st->current = NULL; 683156230Smux } 684156230Smux st->dirty = 1; 685156230Smux error = status_wr(st, sr); 686156230Smux if (error) 687156230Smux return (-1); 688156230Smux return (0); 689156230Smux} 690156230Smux 691156230Smux/* 692156230Smux * Delete the specified entry from the status file. 693156230Smux */ 694156230Smuxint 695156230Smuxstatus_delete(struct status *st, char *name, int isdirup) 696156230Smux{ 697156230Smux struct statusrec *sr; 698156230Smux int ret; 699156230Smux 700156230Smux ret = status_get(st, name, isdirup, 0, &sr); 701156230Smux if (ret == -1) 702156230Smux return (-1); 703156230Smux if (ret) { 704156230Smux st->current = NULL; 705156230Smux st->dirty = 1; 706156230Smux } 707156230Smux return (0); 708156230Smux} 709156230Smux 710156230Smux/* 711156230Smux * Check whether we hit the end of file. 712156230Smux */ 713156230Smuxint 714156230Smuxstatus_eof(struct status *st) 715156230Smux{ 716156230Smux 717156230Smux return (st->eof); 718156230Smux} 719156230Smux 720156230Smux/* 721156230Smux * Returns the error message if there was an error, otherwise returns 722156230Smux * NULL. The error message is allocated dynamically and needs to be 723156230Smux * freed by the caller after use. 724156230Smux */ 725156230Smuxchar * 726156230Smuxstatus_errmsg(struct status *st) 727156230Smux{ 728156230Smux char *errmsg; 729156230Smux 730156230Smux if (!st->error) 731156230Smux return (NULL); 732156230Smux switch (st->error) { 733156230Smux case STATUS_ERR_READ: 734156230Smux xasprintf(&errmsg, "Read failure on \"%s\": %s", 735156230Smux st->path, strerror(st->suberror)); 736156230Smux break; 737156230Smux case STATUS_ERR_WRITE: 738156230Smux xasprintf(&errmsg, "Write failure on \"%s\": %s", 739156230Smux st->tempfile, strerror(st->suberror)); 740156230Smux break; 741156230Smux case STATUS_ERR_PARSE: 742156230Smux xasprintf(&errmsg, "Error in \"%s\": %d: " 743156230Smux "Could not parse status record", st->path, st->linenum); 744156230Smux break; 745156230Smux case STATUS_ERR_UNSORTED: 746156230Smux xasprintf(&errmsg, "Error in \"%s\": %d: " 747156230Smux "File is not sorted properly", st->path, st->linenum); 748156230Smux break; 749156230Smux case STATUS_ERR_TRUNC: 750156230Smux xasprintf(&errmsg, "Error in \"%s\": " 751156230Smux "File is truncated", st->path); 752156230Smux break; 753156230Smux case STATUS_ERR_BOGUS_DIRUP: 754156230Smux xasprintf(&errmsg, "Error in \"%s\": %d: " 755156230Smux "\"U\" entry has no matching \"D\"", st->path, st->linenum); 756156230Smux break; 757156230Smux case STATUS_ERR_BAD_TYPE: 758156230Smux xasprintf(&errmsg, "Error in \"%s\": %d: " 759156230Smux "Invalid file type \"%c\"", st->path, st->linenum, 760156230Smux st->suberror); 761156230Smux break; 762156230Smux case STATUS_ERR_RENAME: 763156230Smux xasprintf(&errmsg, "Cannot rename \"%s\" to \"%s\": %s", 764156230Smux st->tempfile, st->path, strerror(st->suberror)); 765156230Smux break; 766156230Smux default: 767156230Smux assert(0); 768156230Smux return (NULL); 769156230Smux } 770156230Smux return (errmsg); 771156230Smux} 772156230Smux 773156230Smux/* 774156230Smux * Close the status file and free any resource associated with it. 775156230Smux * It is OK to pass NULL for errmsg only if the status file was 776156230Smux * opened read-only. If it wasn't opened read-only, status_close() 777156230Smux * can produce an error and errmsg is not allowed to be NULL. If 778156230Smux * there was no errors, errmsg is set to NULL. 779156230Smux */ 780156230Smuxvoid 781156230Smuxstatus_close(struct status *st, char **errmsg) 782156230Smux{ 783156230Smux struct statusrec *sr; 784156230Smux char *line, *name; 785156230Smux int error, type; 786156230Smux 787156230Smux if (st->wr != NULL) { 788156230Smux if (st->dirty) { 789156230Smux if (st->current != NULL) { 790156230Smux error = status_wr(st, st->current); 791156230Smux if (error) { 792156230Smux *errmsg = status_errmsg(st); 793156230Smux goto bad; 794156230Smux } 795156230Smux st->current = NULL; 796156230Smux } 797156230Smux /* Copy the rest of the file. */ 798156230Smux while ((sr = status_rdraw(st, &line)) != NULL) { 799156230Smux error = status_wrraw(st, sr, line); 800156230Smux if (error) { 801156230Smux *errmsg = status_errmsg(st); 802156230Smux goto bad; 803156230Smux } 804156230Smux } 805156230Smux if (st->error) { 806156230Smux *errmsg = status_errmsg(st); 807156230Smux goto bad; 808156230Smux } 809156230Smux 810156230Smux /* Close off all the open directories. */ 811156230Smux pathcomp_finish(st->pc); 812156230Smux while (pathcomp_get(st->pc, &type, &name)) { 813156230Smux assert(type == PC_DIRUP); 814156230Smux error = proto_printf(st->wr, "U %s %f\n", 815156230Smux name, fattr_bogus); 816156230Smux if (error) { 817156230Smux st->error = STATUS_ERR_WRITE; 818156230Smux st->suberror = errno; 819156230Smux *errmsg = status_errmsg(st); 820156230Smux goto bad; 821156230Smux } 822156230Smux } 823156230Smux 824156230Smux /* Rename tempfile. */ 825156230Smux error = rename(st->tempfile, st->path); 826156230Smux if (error) { 827156230Smux st->error = STATUS_ERR_RENAME; 828156230Smux st->suberror = errno; 829156230Smux *errmsg = status_errmsg(st); 830156230Smux goto bad; 831156230Smux } 832156230Smux } else { 833156230Smux /* Just discard the tempfile. */ 834156230Smux unlink(st->tempfile); 835156230Smux } 836156230Smux *errmsg = NULL; 837156230Smux } 838156230Smux status_free(st); 839156230Smux return; 840156230Smuxbad: 841156230Smux status_free(st); 842156230Smux} 843