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