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