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