1/*- 2 * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29#include <assert.h> 30#include <errno.h> 31#include <fcntl.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <string.h> 35#include <unistd.h> 36 37#include "config.h" 38#include "fattr.h" 39#include "misc.h" 40#include "pathcomp.h" 41#include "proto.h" 42#include "queue.h" 43#include "status.h" 44#include "stream.h" 45 46#define STATUS_VERSION 5 47 48/* Internal error codes. */ 49#define STATUS_ERR_READ (-1) 50#define STATUS_ERR_WRITE (-2) 51#define STATUS_ERR_PARSE (-3) 52#define STATUS_ERR_UNSORTED (-4) 53#define STATUS_ERR_TRUNC (-5) 54#define STATUS_ERR_BOGUS_DIRUP (-6) 55#define STATUS_ERR_BAD_TYPE (-7) 56#define STATUS_ERR_RENAME (-8) 57 58static struct status *status_new(char *, time_t, struct stream *); 59static struct statusrec *status_rd(struct status *); 60static struct statusrec *status_rdraw(struct status *, char **); 61static int status_wr(struct status *, struct statusrec *); 62static int status_wrraw(struct status *, struct statusrec *, 63 char *); 64static struct status *status_fromrd(char *, struct stream *); 65static struct status *status_fromnull(char *); 66static void status_free(struct status *); 67 68static void statusrec_init(struct statusrec *); 69static void statusrec_fini(struct statusrec *); 70static int statusrec_cook(struct statusrec *, char *); 71static int statusrec_cmp(struct statusrec *, struct statusrec *); 72 73struct status { 74 char *path; 75 char *tempfile; 76 int error; 77 int suberror; 78 struct pathcomp *pc; 79 struct statusrec buf; 80 struct statusrec *previous; 81 struct statusrec *current; 82 struct stream *rd; 83 struct stream *wr; 84 time_t scantime; 85 int eof; 86 int linenum; 87 int depth; 88 int dirty; 89}; 90 91static void 92statusrec_init(struct statusrec *sr) 93{ 94 95 memset(sr, 0, sizeof(*sr)); 96} 97 98static int 99statusrec_cook(struct statusrec *sr, char *line) 100{ 101 char *clientattr, *serverattr; 102 103 switch (sr->sr_type) { 104 case SR_FILEDEAD: 105 case SR_FILELIVE: 106 clientattr = proto_get_ascii(&line); 107 if (clientattr == NULL || line != NULL) 108 return (-1); 109 sr->sr_clientattr = fattr_decode(clientattr); 110 if (sr->sr_clientattr == NULL) 111 return (-1); 112 break; 113 case SR_DIRDOWN: 114 /* Nothing to do. */ 115 if (line != NULL) 116 return (-1); 117 break; 118 case SR_CHECKOUTLIVE: 119 sr->sr_tag = proto_get_ascii(&line); 120 sr->sr_date = proto_get_ascii(&line); 121 serverattr = proto_get_ascii(&line); 122 sr->sr_revnum = proto_get_ascii(&line); 123 sr->sr_revdate = proto_get_ascii(&line); 124 clientattr = proto_get_ascii(&line); 125 if (clientattr == NULL || line != NULL) 126 return (-1); 127 sr->sr_serverattr = fattr_decode(serverattr); 128 if (sr->sr_serverattr == NULL) 129 return (-1); 130 sr->sr_clientattr = fattr_decode(clientattr); 131 if (sr->sr_clientattr == NULL) { 132 fattr_free(sr->sr_serverattr); 133 return (-1); 134 } 135 break; 136 case SR_CHECKOUTDEAD: 137 sr->sr_tag = proto_get_ascii(&line); 138 sr->sr_date = proto_get_ascii(&line); 139 serverattr = proto_get_ascii(&line); 140 if (serverattr == NULL || line != NULL) 141 return (-1); 142 sr->sr_serverattr = fattr_decode(serverattr); 143 if (sr->sr_serverattr == NULL) 144 return (-1); 145 break; 146 case SR_DIRUP: 147 clientattr = proto_get_ascii(&line); 148 if (clientattr == NULL || line != NULL) 149 return (-1); 150 sr->sr_clientattr = fattr_decode(clientattr); 151 if (sr->sr_clientattr == NULL) 152 return (-1); 153 break; 154 default: 155 return (-1); 156 } 157 return (0); 158} 159 160static struct statusrec * 161status_rd(struct status *st) 162{ 163 struct statusrec *sr; 164 char *line; 165 int error; 166 167 sr = status_rdraw(st, &line); 168 if (sr == NULL) 169 return (NULL); 170 error = statusrec_cook(sr, line); 171 if (error) { 172 st->error = STATUS_ERR_PARSE; 173 return (NULL); 174 } 175 return (sr); 176} 177 178static struct statusrec * 179status_rdraw(struct status *st, char **linep) 180{ 181 struct statusrec sr; 182 char *cmd, *line, *file; 183 184 if (st->rd == NULL || st->eof) 185 return (NULL); 186 line = stream_getln(st->rd, NULL); 187 if (line == NULL) { 188 if (stream_eof(st->rd)) { 189 if (st->depth != 0) { 190 st->error = STATUS_ERR_TRUNC; 191 return (NULL); 192 } 193 st->eof = 1; 194 return (NULL); 195 } 196 st->error = STATUS_ERR_READ; 197 st->suberror = errno; 198 return (NULL); 199 } 200 st->linenum++; 201 cmd = proto_get_ascii(&line); 202 file = proto_get_ascii(&line); 203 if (file == NULL || strlen(cmd) != 1) { 204 st->error = STATUS_ERR_PARSE; 205 return (NULL); 206 } 207 208 switch (cmd[0]) { 209 case 'A': 210 sr.sr_type = SR_FILELIVE; 211 break; 212 case 'D': 213 sr.sr_type = SR_DIRDOWN; 214 st->depth++; 215 break; 216 case 'C': 217 sr.sr_type = SR_CHECKOUTLIVE; 218 break; 219 case 'c': 220 sr.sr_type = SR_CHECKOUTDEAD; 221 break; 222 case 'U': 223 sr.sr_type = SR_DIRUP; 224 if (st->depth <= 0) { 225 st->error = STATUS_ERR_BOGUS_DIRUP; 226 return (NULL); 227 } 228 st->depth--; 229 break; 230 case 'V': 231 sr.sr_type = SR_FILELIVE; 232 break; 233 case 'v': 234 sr.sr_type = SR_FILEDEAD; 235 break; 236 default: 237 st->error = STATUS_ERR_BAD_TYPE; 238 st->suberror = cmd[0]; 239 return (NULL); 240 } 241 242 sr.sr_file = xstrdup(file); 243 if (st->previous != NULL && 244 statusrec_cmp(st->previous, &sr) >= 0) { 245 st->error = STATUS_ERR_UNSORTED; 246 free(sr.sr_file); 247 return (NULL); 248 } 249 250 if (st->previous == NULL) { 251 st->previous = &st->buf; 252 } else { 253 statusrec_fini(st->previous); 254 statusrec_init(st->previous); 255 } 256 st->previous->sr_type = sr.sr_type; 257 st->previous->sr_file = sr.sr_file; 258 *linep = line; 259 return (st->previous); 260} 261 262static int 263status_wr(struct status *st, struct statusrec *sr) 264{ 265 struct pathcomp *pc; 266 const struct fattr *fa; 267 char *name; 268 int error, type, usedirupattr; 269 270 pc = st->pc; 271 error = 0; 272 usedirupattr = 0; 273 if (sr->sr_type == SR_DIRDOWN) { 274 pathcomp_put(pc, PC_DIRDOWN, sr->sr_file); 275 } else if (sr->sr_type == SR_DIRUP) { 276 pathcomp_put(pc, PC_DIRUP, sr->sr_file); 277 usedirupattr = 1; 278 } else { 279 pathcomp_put(pc, PC_FILE, sr->sr_file); 280 } 281 282 while (pathcomp_get(pc, &type, &name)) { 283 if (type == PC_DIRDOWN) { 284 error = proto_printf(st->wr, "D %s\n", name); 285 } else if (type == PC_DIRUP) { 286 if (usedirupattr) 287 fa = sr->sr_clientattr; 288 else 289 fa = fattr_bogus; 290 usedirupattr = 0; 291 error = proto_printf(st->wr, "U %s %f\n", name, fa); 292 } 293 if (error) 294 goto bad; 295 } 296 297 switch (sr->sr_type) { 298 case SR_DIRDOWN: 299 case SR_DIRUP: 300 /* Already emitted above. */ 301 break; 302 case SR_CHECKOUTLIVE: 303 error = proto_printf(st->wr, "C %s %s %s %f %s %s %f\n", 304 sr->sr_file, sr->sr_tag, sr->sr_date, sr->sr_serverattr, 305 sr->sr_revnum, sr->sr_revdate, sr->sr_clientattr); 306 break; 307 case SR_CHECKOUTDEAD: 308 error = proto_printf(st->wr, "c %s %s %s %f\n", sr->sr_file, 309 sr->sr_tag, sr->sr_date, sr->sr_serverattr); 310 break; 311 case SR_FILELIVE: 312 error = proto_printf(st->wr, "V %s %f\n", sr->sr_file, 313 sr->sr_clientattr); 314 break; 315 case SR_FILEDEAD: 316 error = proto_printf(st->wr, "v %s %f\n", sr->sr_file, 317 sr->sr_clientattr); 318 break; 319 } 320 if (error) 321 goto bad; 322 return (0); 323bad: 324 st->error = STATUS_ERR_WRITE; 325 st->suberror = errno; 326 return (-1); 327} 328 329static int 330status_wrraw(struct status *st, struct statusrec *sr, char *line) 331{ 332 char *name; 333 char cmd; 334 int error, ret, type; 335 336 if (st->wr == NULL) 337 return (0); 338 339 /* 340 * Keep the compressor in sync. At this point, the necessary 341 * DirDowns and DirUps should have already been emitted, so the 342 * compressor should return exactly one value in the PC_DIRDOWN 343 * and PC_DIRUP case and none in the PC_FILE case. 344 */ 345 if (sr->sr_type == SR_DIRDOWN) 346 pathcomp_put(st->pc, PC_DIRDOWN, sr->sr_file); 347 else if (sr->sr_type == SR_DIRUP) 348 pathcomp_put(st->pc, PC_DIRUP, sr->sr_file); 349 else 350 pathcomp_put(st->pc, PC_FILE, sr->sr_file); 351 if (sr->sr_type == SR_DIRDOWN || sr->sr_type == SR_DIRUP) { 352 ret = pathcomp_get(st->pc, &type, &name); 353 assert(ret); 354 if (sr->sr_type == SR_DIRDOWN) 355 assert(type == PC_DIRDOWN); 356 else 357 assert(type == PC_DIRUP); 358 } 359 ret = pathcomp_get(st->pc, &type, &name); 360 assert(!ret); 361 362 switch (sr->sr_type) { 363 case SR_DIRDOWN: 364 cmd = 'D'; 365 break; 366 case SR_DIRUP: 367 cmd = 'U'; 368 break; 369 case SR_CHECKOUTLIVE: 370 cmd = 'C'; 371 break; 372 case SR_CHECKOUTDEAD: 373 cmd = 'c'; 374 break; 375 case SR_FILELIVE: 376 cmd = 'V'; 377 break; 378 case SR_FILEDEAD: 379 cmd = 'v'; 380 break; 381 default: 382 assert(0); 383 return (-1); 384 } 385 if (sr->sr_type == SR_DIRDOWN) 386 error = proto_printf(st->wr, "%c %S\n", cmd, sr->sr_file); 387 else 388 error = proto_printf(st->wr, "%c %s %S\n", cmd, sr->sr_file, 389 line); 390 if (error) { 391 st->error = STATUS_ERR_WRITE; 392 st->suberror = errno; 393 return (-1); 394 } 395 return (0); 396} 397 398static void 399statusrec_fini(struct statusrec *sr) 400{ 401 402 fattr_free(sr->sr_serverattr); 403 fattr_free(sr->sr_clientattr); 404 free(sr->sr_file); 405} 406 407static int 408statusrec_cmp(struct statusrec *a, struct statusrec *b) 409{ 410 size_t lena, lenb; 411 412 if (a->sr_type == SR_DIRUP || b->sr_type == SR_DIRUP) { 413 lena = strlen(a->sr_file); 414 lenb = strlen(b->sr_file); 415 if (a->sr_type == SR_DIRUP && 416 ((lena < lenb && b->sr_file[lena] == '/') || lena == lenb) 417 && strncmp(a->sr_file, b->sr_file, lena) == 0) 418 return (1); 419 if (b->sr_type == SR_DIRUP && 420 ((lenb < lena && a->sr_file[lenb] == '/') || lenb == lena) 421 && strncmp(a->sr_file, b->sr_file, lenb) == 0) 422 return (-1); 423 } 424 return (pathcmp(a->sr_file, b->sr_file)); 425} 426 427static struct status * 428status_new(char *path, time_t scantime, struct stream *file) 429{ 430 struct status *st; 431 432 st = xmalloc(sizeof(struct status)); 433 st->path = path; 434 st->error = 0; 435 st->suberror = 0; 436 st->tempfile = NULL; 437 st->scantime = scantime; 438 st->rd = file; 439 st->wr = NULL; 440 st->previous = NULL; 441 st->current = NULL; 442 st->dirty = 0; 443 st->eof = 0; 444 st->linenum = 0; 445 st->depth = 0; 446 st->pc = pathcomp_new(); 447 statusrec_init(&st->buf); 448 return (st); 449} 450 451static void 452status_free(struct status *st) 453{ 454 455 if (st->previous != NULL) 456 statusrec_fini(st->previous); 457 if (st->rd != NULL) 458 stream_close(st->rd); 459 if (st->wr != NULL) 460 stream_close(st->wr); 461 if (st->tempfile != NULL) 462 free(st->tempfile); 463 free(st->path); 464 pathcomp_free(st->pc); 465 free(st); 466} 467 468static struct status * 469status_fromrd(char *path, struct stream *file) 470{ 471 struct status *st; 472 char *id, *line; 473 time_t scantime; 474 int error, ver; 475 476 /* Get the first line of the file and validate it. */ 477 line = stream_getln(file, NULL); 478 if (line == NULL) { 479 stream_close(file); 480 return (NULL); 481 } 482 id = proto_get_ascii(&line); 483 error = proto_get_int(&line, &ver, 10); 484 if (error) { 485 stream_close(file); 486 return (NULL); 487 } 488 error = proto_get_time(&line, &scantime); 489 if (error || line != NULL) { 490 stream_close(file); 491 return (NULL); 492 } 493 494 if (strcmp(id, "F") != 0 || ver != STATUS_VERSION) { 495 stream_close(file); 496 return (NULL); 497 } 498 499 st = status_new(path, scantime, file); 500 st->linenum = 1; 501 return (st); 502} 503 504static struct status * 505status_fromnull(char *path) 506{ 507 struct status *st; 508 509 st = status_new(path, -1, NULL); 510 st->eof = 1; 511 return (st); 512} 513 514/* 515 * Open the status file. If scantime is not -1, the file is opened 516 * for updating, otherwise, it is opened read-only. If the status file 517 * couldn't be opened, NULL is returned and errmsg is set to the error 518 * message. 519 */ 520struct status * 521status_open(struct coll *coll, time_t scantime, char **errmsg) 522{ 523 struct status *st; 524 struct stream *file; 525 struct fattr *fa; 526 char *destpath, *path; 527 int error, rv; 528 529 path = coll_statuspath(coll); 530 file = stream_open_file(path, O_RDONLY); 531 if (file == NULL) { 532 if (errno != ENOENT) { 533 xasprintf(errmsg, "Could not open \"%s\": %s\n", 534 path, strerror(errno)); 535 free(path); 536 return (NULL); 537 } 538 st = status_fromnull(path); 539 } else { 540 st = status_fromrd(path, file); 541 if (st == NULL) { 542 xasprintf(errmsg, "Error in \"%s\": Bad header line", 543 path); 544 free(path); 545 return (NULL); 546 } 547 } 548 549 if (scantime != -1) { 550 /* Open for writing too. */ 551 xasprintf(&destpath, "%s/%s/%s/", coll->co_base, 552 coll->co_colldir, coll->co_name); 553 st->tempfile = tempname(destpath); 554 if (mkdirhier(destpath, coll->co_umask) != 0) { 555 xasprintf(errmsg, "Cannot create directories leading " 556 "to \"%s\": %s", destpath, strerror(errno)); 557 free(destpath); 558 status_free(st); 559 return (NULL); 560 } 561 free(destpath); 562 st->wr = stream_open_file(st->tempfile, 563 O_CREAT | O_TRUNC | O_WRONLY, 0644); 564 if (st->wr == NULL) { 565 xasprintf(errmsg, "Cannot create \"%s\": %s", 566 st->tempfile, strerror(errno)); 567 status_free(st); 568 return (NULL); 569 } 570 fa = fattr_new(FT_FILE, -1); 571 fattr_mergedefault(fa); 572 fattr_umask(fa, coll->co_umask); 573 rv = fattr_install(fa, st->tempfile, NULL); 574 fattr_free(fa); 575 if (rv == -1) { 576 xasprintf(errmsg, 577 "Cannot set attributes for \"%s\": %s", 578 st->tempfile, strerror(errno)); 579 status_free(st); 580 return (NULL); 581 } 582 if (scantime != st->scantime) 583 st->dirty = 1; 584 error = proto_printf(st->wr, "F %d %t\n", STATUS_VERSION, 585 scantime); 586 if (error) { 587 st->error = STATUS_ERR_WRITE; 588 st->suberror = errno; 589 *errmsg = status_errmsg(st); 590 status_free(st); 591 return (NULL); 592 } 593 } 594 return (st); 595} 596 597/* 598 * Get an entry from the status file. If name is NULL, the next entry 599 * is returned. If name is not NULL, the entry matching this name is 600 * returned, or NULL if it couldn't be found. If deleteto is set to 1, 601 * all the entries read from the status file while looking for the 602 * given name are deleted. 603 */ 604int 605status_get(struct status *st, char *name, int isdirup, int deleteto, 606 struct statusrec **psr) 607{ 608 struct statusrec key; 609 struct statusrec *sr; 610 char *line; 611 int c, error; 612 613 if (st->eof) 614 return (0); 615 616 if (st->error) 617 return (-1); 618 619 if (name == NULL) { 620 sr = status_rd(st); 621 if (sr == NULL) { 622 if (st->error) 623 return (-1); 624 return (0); 625 } 626 *psr = sr; 627 return (1); 628 } 629 630 if (st->current != NULL) { 631 sr = st->current; 632 st->current = NULL; 633 } else { 634 sr = status_rd(st); 635 if (sr == NULL) { 636 if (st->error) 637 return (-1); 638 return (0); 639 } 640 } 641 642 key.sr_file = name; 643 if (isdirup) 644 key.sr_type = SR_DIRUP; 645 else 646 key.sr_type = SR_CHECKOUTLIVE; 647 648 c = statusrec_cmp(sr, &key); 649 if (c < 0) { 650 if (st->wr != NULL && !deleteto) { 651 error = status_wr(st, sr); 652 if (error) 653 return (-1); 654 } 655 /* Loop until we find the good entry. */ 656 for (;;) { 657 sr = status_rdraw(st, &line); 658 if (sr == NULL) { 659 if (st->error) 660 return (-1); 661 return (0); 662 } 663 c = statusrec_cmp(sr, &key); 664 if (c >= 0) 665 break; 666 if (st->wr != NULL && !deleteto) { 667 error = status_wrraw(st, sr, line); 668 if (error) 669 return (-1); 670 } 671 } 672 error = statusrec_cook(sr, line); 673 if (error) { 674 st->error = STATUS_ERR_PARSE; 675 return (-1); 676 } 677 } 678 st->current = sr; 679 if (c != 0) 680 return (0); 681 *psr = sr; 682 return (1); 683} 684 685/* 686 * Put this entry into the status file. If an entry with the same name 687 * existed in the status file, it is replaced by this one, otherwise, 688 * the entry is added to the status file. 689 */ 690int 691status_put(struct status *st, struct statusrec *sr) 692{ 693 struct statusrec *old; 694 int error, ret; 695 696 ret = status_get(st, sr->sr_file, sr->sr_type == SR_DIRUP, 0, &old); 697 if (ret == -1) 698 return (-1); 699 if (ret) { 700 if (old->sr_type == SR_DIRDOWN) { 701 /* DirUp should never match DirDown */ 702 assert(old->sr_type != SR_DIRUP); 703 if (sr->sr_type == SR_CHECKOUTLIVE || 704 sr->sr_type == SR_CHECKOUTDEAD) { 705 /* We are replacing a directory with a file. 706 Delete all entries inside the directory we 707 are replacing. */ 708 ret = status_get(st, sr->sr_file, 1, 1, &old); 709 if (ret == -1) 710 return (-1); 711 assert(ret); 712 } 713 } else 714 st->current = NULL; 715 } 716 st->dirty = 1; 717 error = status_wr(st, sr); 718 if (error) 719 return (-1); 720 return (0); 721} 722 723/* 724 * Delete the specified entry from the status file. 725 */ 726int 727status_delete(struct status *st, char *name, int isdirup) 728{ 729 struct statusrec *sr; 730 int ret; 731 732 ret = status_get(st, name, isdirup, 0, &sr); 733 if (ret == -1) 734 return (-1); 735 if (ret) { 736 st->current = NULL; 737 st->dirty = 1; 738 } 739 return (0); 740} 741 742/* 743 * Check whether we hit the end of file. 744 */ 745int 746status_eof(struct status *st) 747{ 748 749 return (st->eof); 750} 751 752/* 753 * Returns the error message if there was an error, otherwise returns 754 * NULL. The error message is allocated dynamically and needs to be 755 * freed by the caller after use. 756 */ 757char * 758status_errmsg(struct status *st) 759{ 760 char *errmsg; 761 762 if (!st->error) 763 return (NULL); 764 switch (st->error) { 765 case STATUS_ERR_READ: 766 xasprintf(&errmsg, "Read failure on \"%s\": %s", 767 st->path, strerror(st->suberror)); 768 break; 769 case STATUS_ERR_WRITE: 770 xasprintf(&errmsg, "Write failure on \"%s\": %s", 771 st->tempfile, strerror(st->suberror)); 772 break; 773 case STATUS_ERR_PARSE: 774 xasprintf(&errmsg, "Error in \"%s\": %d: " 775 "Could not parse status record", st->path, st->linenum); 776 break; 777 case STATUS_ERR_UNSORTED: 778 xasprintf(&errmsg, "Error in \"%s\": %d: " 779 "File is not sorted properly", st->path, st->linenum); 780 break; 781 case STATUS_ERR_TRUNC: 782 xasprintf(&errmsg, "Error in \"%s\": " 783 "File is truncated", st->path); 784 break; 785 case STATUS_ERR_BOGUS_DIRUP: 786 xasprintf(&errmsg, "Error in \"%s\": %d: " 787 "\"U\" entry has no matching \"D\"", st->path, st->linenum); 788 break; 789 case STATUS_ERR_BAD_TYPE: 790 xasprintf(&errmsg, "Error in \"%s\": %d: " 791 "Invalid file type \"%c\"", st->path, st->linenum, 792 st->suberror); 793 break; 794 case STATUS_ERR_RENAME: 795 xasprintf(&errmsg, "Cannot rename \"%s\" to \"%s\": %s", 796 st->tempfile, st->path, strerror(st->suberror)); 797 break; 798 default: 799 assert(0); 800 return (NULL); 801 } 802 return (errmsg); 803} 804 805/* 806 * Close the status file and free any resource associated with it. 807 * It is OK to pass NULL for errmsg only if the status file was 808 * opened read-only. If it wasn't opened read-only, status_close() 809 * can produce an error and errmsg is not allowed to be NULL. If 810 * there was no errors, errmsg is set to NULL. 811 */ 812void 813status_close(struct status *st, char **errmsg) 814{ 815 struct statusrec *sr; 816 char *line, *name; 817 int error, type; 818 819 if (st->wr != NULL) { 820 if (st->dirty) { 821 if (st->current != NULL) { 822 error = status_wr(st, st->current); 823 if (error) { 824 *errmsg = status_errmsg(st); 825 goto bad; 826 } 827 st->current = NULL; 828 } 829 /* Copy the rest of the file. */ 830 while ((sr = status_rdraw(st, &line)) != NULL) { 831 error = status_wrraw(st, sr, line); 832 if (error) { 833 *errmsg = status_errmsg(st); 834 goto bad; 835 } 836 } 837 if (st->error) { 838 *errmsg = status_errmsg(st); 839 goto bad; 840 } 841 842 /* Close off all the open directories. */ 843 pathcomp_finish(st->pc); 844 while (pathcomp_get(st->pc, &type, &name)) { 845 assert(type == PC_DIRUP); 846 error = proto_printf(st->wr, "U %s %f\n", 847 name, fattr_bogus); 848 if (error) { 849 st->error = STATUS_ERR_WRITE; 850 st->suberror = errno; 851 *errmsg = status_errmsg(st); 852 goto bad; 853 } 854 } 855 856 /* Rename tempfile. */ 857 error = rename(st->tempfile, st->path); 858 if (error) { 859 st->error = STATUS_ERR_RENAME; 860 st->suberror = errno; 861 *errmsg = status_errmsg(st); 862 goto bad; 863 } 864 } else { 865 /* Just discard the tempfile. */ 866 unlink(st->tempfile); 867 } 868 *errmsg = NULL; 869 } 870 status_free(st); 871 return; 872bad: 873 status_free(st); 874} 875