1 /* $OpenBSD: file.c,v 1.181 2007/01/28 03:03:35 joris Exp $ */ 2 /* 3 * Copyright (c) 2006 Joris Vink <joris@openbsd.org> 4 * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 17 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 18 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 19 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "includes.h" 29 30 #include <sys/mman.h> 31 32 #include "cvs.h" 33 #include "file.h" 34 #include "log.h" 35 36 #define CVS_IGN_STATIC 0x01 /* pattern is static, no need to glob */ 37 38 #define CVS_CHAR_ISMETA(c) ((c == '*') || (c == '?') || (c == '[')) 39 40 /* 41 * Standard patterns to ignore. 42 */ 43 static const char *cvs_ign_std[] = { 44 ".", 45 "..", 46 "*.o", 47 "*.a", 48 "*.bak", 49 "*.orig", 50 "*.rej", 51 "*.old", 52 "*.exe", 53 "*.depend", 54 "*.obj", 55 "*.elc", 56 "*.ln", 57 "*.olb", 58 "CVS", 59 "core", 60 "cvslog*", 61 "*.core", 62 ".#*", 63 "*~", 64 "_$*", 65 "*$", 66 }; 67 68 struct ignore_head cvs_ign_pats; 69 struct ignore_head dir_ign_pats; 70 71 void 72 cvs_file_init(void) 73 { 74 int i, l; 75 FILE *ifp; 76 size_t len; 77 char path[MAXPATHLEN], buf[MAXNAMLEN]; 78 79 TAILQ_INIT(&cvs_ign_pats); 80 TAILQ_INIT(&dir_ign_pats); 81 82 /* standard patterns to ignore */ 83 for (i = 0; i < (int)(sizeof(cvs_ign_std)/sizeof(char *)); i++) 84 cvs_file_ignore(cvs_ign_std[i], &cvs_ign_pats); 85 86 /* read the cvsignore file in the user's home directory, if any */ 87 l = snprintf(path, MAXPATHLEN, "%s/.cvsignore", cvs_homedir); 88 if (l == -1 || l >= MAXPATHLEN) 89 fatal("overflow in cvs_file_init"); 90 91 ifp = fopen(path, "r"); 92 if (ifp == NULL) { 93 if (errno != ENOENT) 94 cvs_log(LP_ERRNO, 95 "failed to open user's cvsignore file `%s'", path); 96 } else { 97 while (fgets(buf, MAXNAMLEN, ifp) != NULL) { 98 len = strlen(buf); 99 if (len == 0) 100 continue; 101 if (buf[len - 1] == '\n') 102 buf[len - 1] = '\0'; 103 104 cvs_file_ignore(buf, &cvs_ign_pats); 105 } 106 107 (void)fclose(ifp); 108 } 109 } 110 111 void 112 cvs_file_ignore(const char *pat, struct ignore_head *list) 113 { 114 char *cp; 115 size_t len; 116 struct cvs_ignpat *ip; 117 118 ip = xmalloc(sizeof(*ip)); 119 len = strlcpy(ip->ip_pat, pat, sizeof(ip->ip_pat)); 120 if (len >= sizeof(ip->ip_pat)) 121 fatal("cvs_file_ignore: truncation of pattern '%s'", pat); 122 123 /* check if we will need globbing for that pattern */ 124 ip->ip_flags = CVS_IGN_STATIC; 125 for (cp = ip->ip_pat; *cp != '\0'; cp++) { 126 if (CVS_CHAR_ISMETA(*cp)) { 127 ip->ip_flags &= ~CVS_IGN_STATIC; 128 break; 129 } 130 } 131 132 TAILQ_INSERT_TAIL(list, ip, ip_list); 133 } 134 135 int 136 cvs_file_chkign(const char *file) 137 { 138 int flags; 139 struct cvs_ignpat *ip; 140 141 flags = FNM_PERIOD; 142 if (cvs_nocase) 143 flags |= FNM_CASEFOLD; 144 145 TAILQ_FOREACH(ip, &cvs_ign_pats, ip_list) { 146 if (ip->ip_flags & CVS_IGN_STATIC) { 147 if (cvs_file_cmpname(file, ip->ip_pat) == 0) 148 return (1); 149 } else if (fnmatch(ip->ip_pat, file, flags) == 0) 150 return (1); 151 } 152 153 TAILQ_FOREACH(ip, &dir_ign_pats, ip_list) { 154 if (ip->ip_flags & CVS_IGN_STATIC) { 155 if (cvs_file_cmpname(file, ip->ip_pat) == 0) 156 return (1); 157 } else if (fnmatch(ip->ip_pat, file, flags) == 0) 158 return (1); 159 } 160 161 return (0); 162 } 163 164 void 165 cvs_file_run(int argc, char **argv, struct cvs_recursion *cr) 166 { 167 int i; 168 struct cvs_flisthead fl; 169 170 TAILQ_INIT(&fl); 171 172 for (i = 0; i < argc; i++) 173 cvs_file_get(argv[i], &fl); 174 175 cvs_file_walklist(&fl, cr); 176 cvs_file_freelist(&fl); 177 } 178 179 struct cvs_filelist * 180 cvs_file_get(const char *name, struct cvs_flisthead *fl) 181 { 182 const char *p; 183 struct cvs_filelist *l; 184 185 for (p = name; p[0] == '.' && p[1] == '/';) 186 p += 2; 187 188 TAILQ_FOREACH(l, fl, flist) 189 if (!strcmp(l->file_path, p)) 190 return (l); 191 192 l = (struct cvs_filelist *)xmalloc(sizeof(*l)); 193 l->file_path = xstrdup(p); 194 195 TAILQ_INSERT_TAIL(fl, l, flist); 196 return (l); 197 } 198 199 struct cvs_file * 200 cvs_file_get_cf(const char *d, const char *f, int fd, int type) 201 { 202 int l; 203 struct cvs_file *cf; 204 char *p, rpath[MAXPATHLEN]; 205 206 l = snprintf(rpath, MAXPATHLEN, "%s/%s", d, f); 207 if (l == -1 || l >= MAXPATHLEN) 208 fatal("cvs_file_get_cf: overflow"); 209 210 for (p = rpath; p[0] == '.' && p[1] == '/';) 211 p += 2; 212 213 cf = (struct cvs_file *)xmalloc(sizeof(*cf)); 214 memset(cf, 0, sizeof(*cf)); 215 216 cf->file_name = xstrdup(f); 217 cf->file_wd = xstrdup(d); 218 cf->file_path = xstrdup(p); 219 cf->fd = fd; 220 cf->repo_fd = -1; 221 cf->file_type = type; 222 cf->file_status = cf->file_flags = 0; 223 cf->file_ent = NULL; 224 225 return (cf); 226 } 227 228 void 229 cvs_file_walklist(struct cvs_flisthead *fl, struct cvs_recursion *cr) 230 { 231 int len, fd, type; 232 struct stat st; 233 struct cvs_file *cf; 234 struct cvs_filelist *l, *nxt; 235 char *d, *f, repo[MAXPATHLEN], fpath[MAXPATHLEN]; 236 237 for (l = TAILQ_FIRST(fl); l != NULL; l = nxt) { 238 if (cvs_quit) 239 fatal("received signal %d", sig_received); 240 241 cvs_log(LP_TRACE, "cvs_file_walklist: element '%s'", 242 l->file_path); 243 244 if ((f = basename(l->file_path)) == NULL) 245 fatal("cvs_file_walklist: basename failed"); 246 if ((d = dirname(l->file_path)) == NULL) 247 fatal("cvs_file_walklist: dirname failed"); 248 249 type = CVS_FILE; 250 if ((fd = open(l->file_path, O_RDONLY)) != -1) { 251 if (fstat(fd, &st) == -1) { 252 cvs_log(LP_ERRNO, "%s", l->file_path); 253 (void)close(fd); 254 goto next; 255 } 256 257 if (S_ISDIR(st.st_mode)) 258 type = CVS_DIR; 259 else if (S_ISREG(st.st_mode)) 260 type = CVS_FILE; 261 else { 262 cvs_log(LP_ERR, 263 "ignoring bad file type for %s", 264 l->file_path); 265 (void)close(fd); 266 goto next; 267 } 268 } else if (current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 269 if (stat(d, &st) == -1) { 270 cvs_log(LP_ERRNO, "%s", d); 271 goto next; 272 } 273 274 cvs_get_repository_path(d, repo, MAXPATHLEN); 275 len = snprintf(fpath, MAXPATHLEN, "%s/%s", 276 repo, f); 277 if (len == -1 || len >= MAXPATHLEN) 278 fatal("cvs_file_walklist: overflow"); 279 280 if ((fd = open(fpath, O_RDONLY)) == -1) { 281 strlcat(fpath, RCS_FILE_EXT, MAXPATHLEN); 282 fd = open(fpath, O_RDONLY); 283 } 284 285 if (fd != -1) { 286 if (fstat(fd, &st) == -1) 287 fatal("cvs_file_walklist: %s: %s", 288 fpath, strerror(errno)); 289 290 if (S_ISDIR(st.st_mode)) 291 type = CVS_DIR; 292 else if (S_ISREG(st.st_mode)) 293 type = CVS_FILE; 294 else { 295 cvs_log(LP_ERR, 296 "ignoring bad file type for %s", 297 l->file_path); 298 (void)close(fd); 299 goto next; 300 } 301 302 /* this file is not in our working copy yet */ 303 (void)close(fd); 304 fd = -1; 305 } 306 } 307 308 cf = cvs_file_get_cf(d, f, fd, type); 309 if (cf->file_type == CVS_DIR) { 310 cvs_file_walkdir(cf, cr); 311 } else { 312 if (cr->fileproc != NULL) 313 cr->fileproc(cf); 314 } 315 316 cvs_file_free(cf); 317 318 next: 319 nxt = TAILQ_NEXT(l, flist); 320 TAILQ_REMOVE(fl, l, flist); 321 322 xfree(l->file_path); 323 xfree(l); 324 } 325 } 326 327 void 328 cvs_file_walkdir(struct cvs_file *cf, struct cvs_recursion *cr) 329 { 330 int l, type; 331 FILE *fp; 332 int nbytes; 333 size_t len; 334 long base; 335 size_t bufsize; 336 struct stat st; 337 struct dirent *dp; 338 struct cvs_ent *ent; 339 struct cvs_ignpat *ip; 340 struct cvs_ent_line *line; 341 struct cvs_flisthead fl, dl; 342 CVSENTRIES *entlist; 343 char *buf, *ebuf, *cp, repo[MAXPATHLEN], fpath[MAXPATHLEN]; 344 345 cvs_log(LP_TRACE, "cvs_file_walkdir(%s)", cf->file_path); 346 347 if (cr->enterdir != NULL) 348 cr->enterdir(cf); 349 350 if (cr->fileproc != NULL) 351 cr->fileproc(cf); 352 353 if (cf->file_status == FILE_SKIP) 354 return; 355 356 /* 357 * If we do not have a admin directory inside here, dont bother, 358 * unless we are running import. 359 */ 360 l = snprintf(fpath, MAXPATHLEN, "%s/%s", cf->file_path, 361 CVS_PATH_CVSDIR); 362 if (l == -1 || l >= MAXPATHLEN) 363 fatal("cvs_file_walkdir: overflow"); 364 365 l = stat(fpath, &st); 366 if (cvs_cmdop != CVS_OP_IMPORT && 367 (l == -1 || (l == 0 && !S_ISDIR(st.st_mode)))) { 368 return; 369 } 370 371 /* 372 * check for a local .cvsignore file 373 */ 374 l = snprintf(fpath, MAXPATHLEN, "%s/.cvsignore", cf->file_path); 375 if (l == -1 || l >= MAXPATHLEN) 376 fatal("cvs_file_walkdir: overflow"); 377 378 if ((fp = fopen(fpath, "r")) != NULL) { 379 while (fgets(fpath, MAXPATHLEN, fp) != NULL) { 380 len = strlen(fpath); 381 if (len == 0) 382 continue; 383 if (fpath[len - 1] == '\n') 384 fpath[len - 1] = '\0'; 385 386 cvs_file_ignore(fpath, &dir_ign_pats); 387 } 388 389 (void)fclose(fp); 390 } 391 392 if (fstat(cf->fd, &st) == -1) 393 fatal("cvs_file_walkdir: %s %s", cf->file_path, 394 strerror(errno)); 395 396 bufsize = st.st_size; 397 if (bufsize < st.st_blksize) 398 bufsize = st.st_blksize; 399 400 buf = xmalloc(bufsize); 401 TAILQ_INIT(&fl); 402 TAILQ_INIT(&dl); 403 404 while ((nbytes = getdirentries(cf->fd, buf, bufsize, &base)) > 0) { 405 ebuf = buf + nbytes; 406 cp = buf; 407 408 while (cp < ebuf) { 409 dp = (struct dirent *)cp; 410 if (!strcmp(dp->d_name, ".") || 411 !strcmp(dp->d_name, "..") || 412 !strcmp(dp->d_name, CVS_PATH_CVSDIR) || 413 dp->d_reclen == 0) { 414 cp += dp->d_reclen; 415 continue; 416 } 417 418 if (cvs_file_chkign(dp->d_name)) { 419 cp += dp->d_reclen; 420 continue; 421 } 422 423 l = snprintf(fpath, MAXPATHLEN, "%s/%s", 424 cf->file_path, dp->d_name); 425 if (l == -1 || l >= MAXPATHLEN) 426 fatal("cvs_file_walkdir: overflow"); 427 428 /* 429 * nfs and afs will show d_type as DT_UNKNOWN 430 * for files and/or directories so when we encounter 431 * this we call stat() on the path to be sure. 432 */ 433 if (dp->d_type == DT_UNKNOWN) { 434 if (stat(fpath, &st) == -1) 435 fatal("'%s': %s", fpath, 436 strerror(errno)); 437 438 switch (st.st_mode & S_IFMT) { 439 case S_IFDIR: 440 type = CVS_DIR; 441 break; 442 case S_IFREG: 443 type = CVS_FILE; 444 break; 445 default: 446 type = FILE_SKIP; 447 break; 448 } 449 } else { 450 switch (dp->d_type) { 451 case DT_DIR: 452 type = CVS_DIR; 453 break; 454 case DT_REG: 455 type = CVS_FILE; 456 break; 457 default: 458 type = FILE_SKIP; 459 break; 460 } 461 } 462 463 if (type == FILE_SKIP) { 464 if (verbosity > 1) { 465 cvs_log(LP_NOTICE, "ignoring `%s'", 466 dp->d_name); 467 } 468 cp += dp->d_reclen; 469 continue; 470 } 471 472 if (!(cr->flags & CR_RECURSE_DIRS) && 473 type == CVS_DIR) { 474 cp += dp->d_reclen; 475 continue; 476 } 477 478 switch (type) { 479 case CVS_DIR: 480 cvs_file_get(fpath, &dl); 481 break; 482 case CVS_FILE: 483 cvs_file_get(fpath, &fl); 484 break; 485 default: 486 fatal("type %d unknown, shouldn't happen", 487 type); 488 } 489 490 cp += dp->d_reclen; 491 } 492 } 493 494 if (nbytes == -1) 495 fatal("cvs_file_walkdir: %s %s", cf->file_path, 496 strerror(errno)); 497 498 xfree(buf); 499 500 while ((ip = TAILQ_FIRST(&dir_ign_pats)) != NULL) { 501 TAILQ_REMOVE(&dir_ign_pats, ip, ip_list); 502 xfree(ip); 503 } 504 505 entlist = cvs_ent_open(cf->file_path); 506 TAILQ_FOREACH(line, &(entlist->cef_ent), entries_list) { 507 ent = cvs_ent_parse(line->buf); 508 509 l = snprintf(fpath, MAXPATHLEN, "%s/%s", cf->file_path, 510 ent->ce_name); 511 if (l == -1 || l >= MAXPATHLEN) 512 fatal("cvs_file_walkdir: overflow"); 513 514 if (!(cr->flags & CR_RECURSE_DIRS) && 515 ent->ce_type == CVS_ENT_DIR) 516 continue; 517 518 if (ent->ce_type == CVS_ENT_DIR) 519 cvs_file_get(fpath, &dl); 520 else if (ent->ce_type == CVS_ENT_FILE) 521 cvs_file_get(fpath, &fl); 522 523 cvs_ent_free(ent); 524 } 525 526 cvs_ent_close(entlist, ENT_NOSYNC); 527 528 if (cr->flags & CR_REPO) { 529 cvs_get_repository_path(cf->file_path, repo, MAXPATHLEN); 530 cvs_repository_lock(repo); 531 532 cvs_repository_getdir(repo, cf->file_path, &fl, &dl, 533 (cr->flags & CR_RECURSE_DIRS)); 534 } 535 536 cvs_file_walklist(&fl, cr); 537 cvs_file_freelist(&fl); 538 539 if (cr->flags & CR_REPO) 540 cvs_repository_unlock(repo); 541 542 cvs_file_walklist(&dl, cr); 543 cvs_file_freelist(&dl); 544 545 if (cr->leavedir != NULL) 546 cr->leavedir(cf); 547 } 548 549 void 550 cvs_file_freelist(struct cvs_flisthead *fl) 551 { 552 struct cvs_filelist *f; 553 554 while ((f = TAILQ_FIRST(fl)) != NULL) { 555 TAILQ_REMOVE(fl, f, flist); 556 xfree(f->file_path); 557 xfree(f); 558 } 559 } 560 561 void 562 cvs_file_classify(struct cvs_file *cf, const char *tag, int loud) 563 { 564 size_t len; 565 struct stat st; 566 BUF *b1, *b2; 567 int rflags, l, ismodified, rcsdead; 568 CVSENTRIES *entlist = NULL; 569 const char *state; 570 char repo[MAXPATHLEN], rcsfile[MAXPATHLEN], r1[16], r2[16]; 571 572 cvs_log(LP_TRACE, "cvs_file_classify(%s)", cf->file_path); 573 574 if (!strcmp(cf->file_path, ".")) { 575 cf->file_status = FILE_UPTODATE; 576 return; 577 } 578 579 cvs_get_repository_path(cf->file_wd, repo, MAXPATHLEN); 580 l = snprintf(rcsfile, MAXPATHLEN, "%s/%s", 581 repo, cf->file_name); 582 if (l == -1 || l >= MAXPATHLEN) 583 fatal("cvs_file_classify: overflow"); 584 585 if (cf->file_type == CVS_FILE) { 586 len = strlcat(rcsfile, RCS_FILE_EXT, MAXPATHLEN); 587 if (len >= MAXPATHLEN) 588 fatal("cvs_file_classify: truncation"); 589 } 590 591 cf->file_rpath = xstrdup(rcsfile); 592 593 entlist = cvs_ent_open(cf->file_wd); 594 cf->file_ent = cvs_ent_get(entlist, cf->file_name); 595 596 if (cf->file_ent != NULL) { 597 if (cf->file_ent->ce_type == CVS_ENT_DIR && 598 cf->file_type != CVS_DIR) 599 fatal("%s is supposed to be a directory, but it is not", 600 cf->file_path); 601 if (cf->file_ent->ce_type == CVS_ENT_FILE && 602 cf->file_type != CVS_FILE) 603 fatal("%s is supposed to be a file, but it is not", 604 cf->file_path); 605 } 606 607 if (cf->file_type == CVS_DIR) { 608 if (cf->fd == -1 && stat(rcsfile, &st) != -1) 609 cf->file_status = DIR_CREATE; 610 else if (cf->file_ent != NULL) 611 cf->file_status = FILE_UPTODATE; 612 else 613 cf->file_status = FILE_UNKNOWN; 614 615 cvs_ent_close(entlist, ENT_NOSYNC); 616 return; 617 } 618 619 rflags = RCS_READ; 620 switch (cvs_cmdop) { 621 case CVS_OP_COMMIT: 622 rflags = RCS_WRITE; 623 break; 624 case CVS_OP_IMPORT: 625 case CVS_OP_LOG: 626 rflags |= RCS_PARSE_FULLY; 627 break; 628 } 629 630 cf->repo_fd = open(cf->file_rpath, O_RDONLY); 631 if (cf->repo_fd != -1) { 632 cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, rflags); 633 if (cf->file_rcs == NULL) 634 fatal("cvs_file_classify: failed to parse RCS"); 635 cf->file_rcs->rf_inattic = 0; 636 } else if (cvs_cmdop != CVS_OP_CHECKOUT) { 637 l = snprintf(rcsfile, MAXPATHLEN, "%s/%s/%s%s", 638 repo, CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT); 639 if (l == -1 || l >= MAXPATHLEN) 640 fatal("cvs_file_classify: overflow"); 641 642 cf->repo_fd = open(rcsfile, O_RDONLY); 643 if (cf->repo_fd != -1) { 644 xfree(cf->file_rpath); 645 cf->file_rpath = xstrdup(rcsfile); 646 cf->file_rcs = rcs_open(cf->file_rpath, 647 cf->repo_fd, rflags); 648 if (cf->file_rcs == NULL) 649 fatal("cvs_file_classify: failed to parse RCS"); 650 cf->file_rcs->rf_inattic = 1; 651 } else { 652 cf->file_rcs = NULL; 653 } 654 } else 655 cf->file_rcs = NULL; 656 657 if (tag != NULL && cf->file_rcs != NULL) { 658 if ((cf->file_rcsrev = rcs_translate_tag(tag, cf->file_rcs)) == NULL) 659 fatal("cvs_file_classify: could not translate tag `%s'", tag); 660 } else if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL) { 661 cf->file_rcsrev = rcsnum_alloc(); 662 rcsnum_cpy(cf->file_ent->ce_rev, cf->file_rcsrev, 0); 663 } else if (cf->file_rcs != NULL) 664 cf->file_rcsrev = rcs_head_get(cf->file_rcs); 665 else 666 cf->file_rcsrev = NULL; 667 668 if (cf->file_ent != NULL) 669 rcsnum_tostr(cf->file_ent->ce_rev, r1, sizeof(r1)); 670 if (cf->file_rcsrev != NULL) 671 rcsnum_tostr(cf->file_rcsrev, r2, sizeof(r2)); 672 673 ismodified = rcsdead = 0; 674 if (cf->fd != -1 && cf->file_ent != NULL) { 675 if (fstat(cf->fd, &st) == -1) 676 fatal("cvs_file_classify: %s", strerror(errno)); 677 678 if (st.st_mtime != cf->file_ent->ce_mtime) 679 ismodified = 1; 680 } 681 682 if (ismodified == 1 && cf->fd != -1 && cf->file_rcs != NULL) { 683 b1 = rcs_rev_getbuf(cf->file_rcs, cf->file_rcsrev, 0); 684 if (b1 == NULL) 685 fatal("failed to get HEAD revision for comparison"); 686 687 b2 = cvs_buf_load_fd(cf->fd, BUF_AUTOEXT); 688 if (b2 == NULL) 689 fatal("failed to get file content for comparison"); 690 691 /* b1 and b2 get released in cvs_buf_differ */ 692 if (cvs_buf_differ(b1, b2)) 693 ismodified = 1; 694 else 695 ismodified = 0; 696 } 697 698 if (cf->file_rcs != NULL && cf->file_rcsrev != NULL) { 699 state = rcs_state_get(cf->file_rcs, cf->file_rcsrev); 700 if (state == NULL) 701 fatal("failed to get state for HEAD for %s", 702 cf->file_path); 703 if (!strcmp(state, RCS_STATE_DEAD)) 704 rcsdead = 1; 705 706 cf->file_rcs->rf_dead = rcsdead; 707 } 708 709 /* 710 * 10 Sin 711 * 20 Goto hell 712 * (I welcome you if-else hell) 713 */ 714 if (cf->file_ent == NULL) { 715 if (cf->file_rcs == NULL) { 716 if (cf->fd == -1) { 717 cvs_log(LP_NOTICE, 718 "nothing known about '%s'", 719 cf->file_path); 720 } else if (cvs_cmdop != CVS_OP_ADD) { 721 cvs_log(LP_NOTICE, 722 "use add to create an entry for %s", 723 cf->file_path); 724 } 725 726 cf->file_status = FILE_UNKNOWN; 727 } else if (rcsdead == 1) { 728 if (cf->fd == -1) { 729 cf->file_status = FILE_UPTODATE; 730 } else if (cvs_cmdop != CVS_OP_ADD) { 731 cvs_log(LP_NOTICE, 732 "use add to create an entry for %s", 733 cf->file_path); 734 cf->file_status = FILE_UNKNOWN; 735 } 736 } else { 737 cf->file_status = FILE_CHECKOUT; 738 } 739 } else if (cf->file_ent->ce_status == CVS_ENT_ADDED) { 740 if (cf->fd == -1) { 741 if (cvs_cmdop != CVS_OP_REMOVE) { 742 cvs_log(LP_NOTICE, 743 "warning: new-born %s has disappeared", 744 cf->file_path); 745 } 746 cf->file_status = FILE_REMOVE_ENTRY; 747 } else if (cf->file_rcs == NULL || rcsdead == 1) { 748 cf->file_status = FILE_ADDED; 749 } else { 750 cvs_log(LP_NOTICE, 751 "conflict: %s already created by others", 752 cf->file_path); 753 cf->file_status = FILE_CONFLICT; 754 } 755 } else if (cf->file_ent->ce_status == CVS_ENT_REMOVED) { 756 if (cf->fd != -1) { 757 cvs_log(LP_NOTICE, 758 "%s should be removed but is still there", 759 cf->file_path); 760 cf->file_status = FILE_REMOVED; 761 } else if (cf->file_rcs == NULL || rcsdead == 1) { 762 cf->file_status = FILE_REMOVE_ENTRY; 763 } else { 764 if (strcmp(r1, r2)) { 765 cvs_log(LP_NOTICE, 766 "conflict: removed %s was modified" 767 " by a second party", 768 cf->file_path); 769 cf->file_status = FILE_CONFLICT; 770 } else { 771 cf->file_status = FILE_REMOVED; 772 } 773 } 774 } else if (cf->file_ent->ce_status == CVS_ENT_REG) { 775 if (cf->file_rcs == NULL || rcsdead == 1) { 776 if (cf->fd == -1) { 777 cvs_log(LP_NOTICE, 778 "warning: %s's entry exists but" 779 " there is no longer a file" 780 " in the repository," 781 " removing entry", 782 cf->file_path); 783 cf->file_status = FILE_REMOVE_ENTRY; 784 } else { 785 if (ismodified) { 786 cvs_log(LP_NOTICE, 787 "conflict: %s is no longer " 788 "in the repository but is " 789 "locally modified", 790 cf->file_path); 791 cf->file_status = FILE_CONFLICT; 792 } else { 793 cvs_log(LP_NOTICE, 794 "%s is no longer in the " 795 "repository", 796 cf->file_path); 797 798 cf->file_status = FILE_UNLINK; 799 } 800 } 801 } else { 802 if (cf->fd == -1) { 803 if (cvs_cmdop != CVS_OP_REMOVE) { 804 cvs_log(LP_NOTICE, 805 "warning: %s was lost", 806 cf->file_path); 807 } 808 cf->file_status = FILE_LOST; 809 } else { 810 if (ismodified == 1) 811 cf->file_status = FILE_MODIFIED; 812 else 813 cf->file_status = FILE_UPTODATE; 814 815 if (strcmp(r1, r2)) { 816 if (cf->file_status == FILE_MODIFIED) 817 cf->file_status = FILE_MERGE; 818 else 819 cf->file_status = FILE_PATCH; 820 } 821 } 822 } 823 } 824 825 cvs_ent_close(entlist, ENT_NOSYNC); 826 } 827 828 void 829 cvs_file_free(struct cvs_file *cf) 830 { 831 xfree(cf->file_name); 832 xfree(cf->file_wd); 833 xfree(cf->file_path); 834 835 if (cf->file_rcsrev != NULL) 836 rcsnum_free(cf->file_rcsrev); 837 if (cf->file_rpath != NULL) 838 xfree(cf->file_rpath); 839 if (cf->file_ent != NULL) 840 cvs_ent_free(cf->file_ent); 841 if (cf->file_rcs != NULL) 842 rcs_close(cf->file_rcs); 843 if (cf->fd != -1) 844 (void)close(cf->fd); 845 if (cf->repo_fd != -1) 846 (void)close(cf->repo_fd); 847 xfree(cf); 848 } 849 850 int 851 cvs_file_cmpname(const char *name1, const char *name2) 852 { 853 return (cvs_nocase == 0) ? (strcmp(name1, name2)) : 854 (strcasecmp(name1, name2)); 855 } 856 857 int 858 cvs_file_cmp(const char *file1, const char *file2) 859 { 860 struct stat stb1, stb2; 861 int fd1, fd2, ret; 862 863 ret = 0; 864 865 if ((fd1 = open(file1, O_RDONLY|O_NOFOLLOW, 0)) == -1) 866 fatal("cvs_file_cmp: open: `%s': %s", file1, strerror(errno)); 867 if ((fd2 = open(file2, O_RDONLY|O_NOFOLLOW, 0)) == -1) 868 fatal("cvs_file_cmp: open: `%s': %s", file2, strerror(errno)); 869 870 if (fstat(fd1, &stb1) == -1) 871 fatal("cvs_file_cmp: `%s': %s", file1, strerror(errno)); 872 if (fstat(fd2, &stb2) == -1) 873 fatal("cvs_file_cmp: `%s': %s", file2, strerror(errno)); 874 875 if (stb1.st_size != stb2.st_size || 876 (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)) { 877 ret = 1; 878 goto out; 879 } 880 881 if (S_ISBLK(stb1.st_mode) || S_ISCHR(stb1.st_mode)) { 882 if (stb1.st_rdev != stb2.st_rdev) 883 ret = 1; 884 goto out; 885 } 886 887 if (S_ISREG(stb1.st_mode)) { 888 void *p1, *p2; 889 890 if (stb1.st_size > SIZE_MAX) { 891 ret = 1; 892 goto out; 893 } 894 895 if ((p1 = mmap(NULL, stb1.st_size, PROT_READ, 896 MAP_FILE, fd1, (off_t)0)) == MAP_FAILED) 897 fatal("cvs_file_cmp: mmap failed"); 898 899 if ((p2 = mmap(NULL, stb1.st_size, PROT_READ, 900 MAP_FILE, fd2, (off_t)0)) == MAP_FAILED) 901 fatal("cvs_file_cmp: mmap failed"); 902 903 madvise(p1, stb1.st_size, MADV_SEQUENTIAL); 904 madvise(p2, stb1.st_size, MADV_SEQUENTIAL); 905 906 ret = memcmp(p1, p2, stb1.st_size); 907 908 (void)munmap(p1, stb1.st_size); 909 (void)munmap(p2, stb1.st_size); 910 } 911 912 out: 913 (void)close(fd1); 914 (void)close(fd2); 915 916 return (ret); 917 } 918 919 int 920 cvs_file_copy(const char *from, const char *to) 921 { 922 struct stat st; 923 struct timeval tv[2]; 924 time_t atime, mtime; 925 int src, dst, ret; 926 927 ret = 0; 928 929 cvs_log(LP_TRACE, "cvs_file_copy(%s,%s)", from, to); 930 931 if (cvs_noexec == 1) 932 return (0); 933 934 if ((src = open(from, O_RDONLY, 0)) == -1) 935 fatal("cvs_file_copy: open: `%s': %s", from, strerror(errno)); 936 937 if (fstat(src, &st) == -1) 938 fatal("cvs_file_copy: `%s': %s", from, strerror(errno)); 939 940 atime = st.st_atimespec.tv_sec; 941 mtime = st.st_mtimespec.tv_sec; 942 943 if (S_ISREG(st.st_mode)) { 944 size_t sz; 945 ssize_t nw; 946 char *p, *buf; 947 int saved_errno; 948 949 if (st.st_size > SIZE_MAX) { 950 ret = -1; 951 goto out; 952 } 953 954 if ((dst = open(to, O_CREAT|O_TRUNC|O_WRONLY, 955 st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))) == -1) 956 fatal("cvs_file_copy: open `%s': %s", 957 to, strerror(errno)); 958 959 if ((p = mmap(NULL, st.st_size, PROT_READ, 960 MAP_FILE, src, (off_t)0)) == MAP_FAILED) { 961 saved_errno = errno; 962 (void)unlink(to); 963 fatal("cvs_file_copy: mmap: %s", strerror(saved_errno)); 964 } 965 966 madvise(p, st.st_size, MADV_SEQUENTIAL); 967 968 sz = st.st_size; 969 buf = p; 970 971 while (sz > 0) { 972 if ((nw = write(dst, p, sz)) == -1) { 973 saved_errno = errno; 974 (void)unlink(to); 975 fatal("cvs_file_copy: `%s': %s", 976 from, strerror(saved_errno)); 977 } 978 buf += nw; 979 sz -= nw; 980 } 981 982 (void)munmap(p, st.st_size); 983 984 tv[0].tv_sec = atime; 985 tv[1].tv_sec = mtime; 986 987 if (futimes(dst, tv) == -1) { 988 saved_errno = errno; 989 (void)unlink(to); 990 fatal("cvs_file_copy: futimes: %s", 991 strerror(saved_errno)); 992 } 993 (void)close(dst); 994 } 995 out: 996 (void)close(src); 997 998 return (ret); 999 } 1000