1 /* $OpenBSD: file.c,v 1.273 2017/06/01 08:38:56 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 <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, *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 (cvsroot_is_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 (cvsroot_is_local()) { 364 cvs_get_repository_path(cf->file_wd, 365 repo, PATH_MAX); 366 cvs_repository_lock(repo, 367 (cmdp->cmd_flags & CVS_LOCK_REPO)); 368 } 369 } 370 371 if (cr->fileproc != NULL) 372 cr->fileproc(cf); 373 374 if (l->flags & FILE_USER_SUPPLIED) { 375 if (cvsroot_is_local() && 376 (cmdp->cmd_flags & CVS_LOCK_REPO)) { 377 cvs_repository_unlock(repo); 378 } 379 free(cvs_directory_tag); 380 cvs_directory_tag = NULL; 381 } 382 } 383 384 cvs_file_free(cf); 385 386 next: 387 nxt = RB_NEXT(cvs_flisthead, fl, l); 388 } 389 } 390 391 void 392 cvs_file_walkdir(struct cvs_file *cf, struct cvs_recursion *cr) 393 { 394 int l, type; 395 FILE *fp; 396 int nbytes; 397 size_t bufsize; 398 struct stat st; 399 struct dirent *dp; 400 struct cvs_ent *ent; 401 struct cvs_ignpat *ip; 402 struct cvs_ent_line *line; 403 struct cvs_flisthead fl, dl; 404 CVSENTRIES *entlist; 405 char *buf, *ebuf, *cp, repo[PATH_MAX], fpath[PATH_MAX]; 406 407 cvs_log(LP_TRACE, "cvs_file_walkdir(%s)", cf->file_path); 408 409 if (cr->enterdir != NULL) 410 cr->enterdir(cf); 411 412 if (cr->fileproc != NULL) 413 cr->fileproc(cf); 414 415 if (cf->file_status == FILE_SKIP) 416 return; 417 418 /* 419 * If this is a repository-only command, do not touch any 420 * locally available directories or try to create them. 421 */ 422 if (!(cmdp->cmd_flags & CVS_USE_WDIR)) { 423 RB_INIT(&fl); 424 RB_INIT(&dl); 425 goto walkrepo; 426 } 427 428 /* 429 * If we do not have an admin directory inside here, dont bother, 430 * unless we are running export or import. 431 */ 432 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", cf->file_path, 433 CVS_PATH_CVSDIR); 434 435 l = stat(fpath, &st); 436 if (cvs_cmdop != CVS_OP_EXPORT && cvs_cmdop != CVS_OP_IMPORT && 437 (l == -1 || (l == 0 && !S_ISDIR(st.st_mode)))) { 438 return; 439 } 440 441 cvs_parse_tagfile(cf->file_path, &cvs_directory_tag, NULL, NULL); 442 443 /* 444 * check for a local .cvsignore file 445 */ 446 (void)xsnprintf(fpath, PATH_MAX, "%s/.cvsignore", cf->file_path); 447 448 if ((fp = fopen(fpath, "r")) != NULL) { 449 while (fgets(fpath, PATH_MAX, fp) != NULL) { 450 fpath[strcspn(fpath, "\n")] = '\0'; 451 if (fpath[0] == '\0') 452 continue; 453 454 cvs_file_ignore(fpath, &dir_ign_pats); 455 } 456 457 (void)fclose(fp); 458 } 459 460 if (fstat(cf->fd, &st) == -1) 461 fatal("cvs_file_walkdir: %s %s", cf->file_path, 462 strerror(errno)); 463 464 if ((uintmax_t)st.st_size > SIZE_MAX) 465 fatal("cvs_file_walkdir: %s: file size too big", cf->file_name); 466 467 bufsize = (st.st_size > st.st_blksize) ? st.st_size : st.st_blksize; 468 469 buf = xmalloc(bufsize); 470 RB_INIT(&fl); 471 RB_INIT(&dl); 472 473 while ((nbytes = getdents(cf->fd, buf, bufsize)) > 0) { 474 ebuf = buf + nbytes; 475 cp = buf; 476 477 while (cp < ebuf) { 478 dp = (struct dirent *)cp; 479 if (!strcmp(dp->d_name, ".") || 480 !strcmp(dp->d_name, "..") || 481 !strcmp(dp->d_name, CVS_PATH_CVSDIR) || 482 dp->d_fileno == 0) { 483 cp += dp->d_reclen; 484 continue; 485 } 486 487 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", 488 cf->file_path, dp->d_name); 489 490 if (cvs_file_chkign(dp->d_name) && 491 cvs_cmdop != CVS_OP_RLOG && 492 cvs_cmdop != CVS_OP_RTAG) { 493 if (cvs_cmdop == CVS_OP_IMPORT) 494 cvs_import_ignored(fpath); 495 cp += dp->d_reclen; 496 continue; 497 } 498 499 /* 500 * nfs and afs will show d_type as DT_UNKNOWN 501 * for files and/or directories so when we encounter 502 * this we call lstat() on the path to be sure. 503 */ 504 if (dp->d_type == DT_UNKNOWN) { 505 if (lstat(fpath, &st) == -1) 506 fatal("'%s': %s", fpath, 507 strerror(errno)); 508 509 switch (st.st_mode & S_IFMT) { 510 case S_IFDIR: 511 type = CVS_DIR; 512 break; 513 case S_IFREG: 514 type = CVS_FILE; 515 break; 516 default: 517 type = FILE_SKIP; 518 break; 519 } 520 } else { 521 switch (dp->d_type) { 522 case DT_DIR: 523 type = CVS_DIR; 524 break; 525 case DT_REG: 526 type = CVS_FILE; 527 break; 528 default: 529 type = FILE_SKIP; 530 break; 531 } 532 } 533 534 if (type == FILE_SKIP) { 535 if (verbosity > 1) { 536 cvs_log(LP_NOTICE, "ignoring `%s'", 537 dp->d_name); 538 } 539 cp += dp->d_reclen; 540 continue; 541 } 542 543 switch (type) { 544 case CVS_DIR: 545 if (cr->flags & CR_RECURSE_DIRS) 546 cvs_file_get(fpath, 0, &dl, CVS_DIR); 547 break; 548 case CVS_FILE: 549 cvs_file_get(fpath, 0, &fl, CVS_FILE); 550 break; 551 default: 552 fatal("type %d unknown, shouldn't happen", 553 type); 554 } 555 556 cp += dp->d_reclen; 557 } 558 } 559 560 if (nbytes == -1) 561 fatal("cvs_file_walkdir: %s %s", cf->file_path, 562 strerror(errno)); 563 564 free(buf); 565 566 while ((ip = TAILQ_FIRST(&dir_ign_pats)) != NULL) { 567 TAILQ_REMOVE(&dir_ign_pats, ip, ip_list); 568 free(ip); 569 } 570 571 entlist = cvs_ent_open(cf->file_path); 572 TAILQ_FOREACH(line, &(entlist->cef_ent), entries_list) { 573 ent = cvs_ent_parse(line->buf); 574 575 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", cf->file_path, 576 ent->ce_name); 577 578 if (!(cr->flags & CR_RECURSE_DIRS) && 579 ent->ce_type == CVS_ENT_DIR) 580 continue; 581 if (ent->ce_type == CVS_ENT_DIR) 582 cvs_file_get(fpath, 0, &dl, CVS_DIR); 583 else if (ent->ce_type == CVS_ENT_FILE) 584 cvs_file_get(fpath, 0, &fl, CVS_FILE); 585 586 cvs_ent_free(ent); 587 } 588 589 walkrepo: 590 if (cvsroot_is_local()) { 591 cvs_get_repository_path(cf->file_path, repo, PATH_MAX); 592 cvs_repository_lock(repo, (cmdp->cmd_flags & CVS_LOCK_REPO)); 593 } 594 595 if (cr->flags & CR_REPO) { 596 xsnprintf(fpath, sizeof(fpath), "%s/%s", cf->file_path, 597 CVS_PATH_STATICENTRIES); 598 599 if (!(cmdp->cmd_flags & CVS_USE_WDIR) || 600 stat(fpath, &st) == -1 || build_dirs == 1) 601 cvs_repository_getdir(repo, cf->file_path, &fl, &dl, 602 (cr->flags & CR_RECURSE_DIRS) ? 603 REPOSITORY_DODIRS : 0); 604 } 605 606 cvs_file_walklist(&fl, cr); 607 cvs_file_freelist(&fl); 608 609 if (cvsroot_is_local() && (cmdp->cmd_flags & CVS_LOCK_REPO)) 610 cvs_repository_unlock(repo); 611 612 if (cvs_directory_tag != NULL && cmdp->cmd_flags & CVS_USE_WDIR) { 613 cvs_write_tagfile(cf->file_path, cvs_directory_tag, NULL); 614 free(cvs_directory_tag); 615 cvs_directory_tag = NULL; 616 } 617 618 cvs_file_walklist(&dl, cr); 619 cvs_file_freelist(&dl); 620 621 if (cr->leavedir != NULL) 622 cr->leavedir(cf); 623 } 624 625 void 626 cvs_file_freelist(struct cvs_flisthead *fl) 627 { 628 struct cvs_filelist *f, *nxt; 629 630 for (f = RB_MIN(cvs_flisthead, fl); f != NULL; f = nxt) { 631 nxt = RB_NEXT(cvs_flisthead, fl, f); 632 RB_REMOVE(cvs_flisthead, fl, f); 633 free(f->file_path); 634 free(f); 635 } 636 } 637 638 void 639 cvs_file_classify(struct cvs_file *cf, const char *tag) 640 { 641 size_t len; 642 struct stat st; 643 BUF *b1, *b2; 644 int server_has_file, notag; 645 int rflags, ismodified, rcsdead; 646 CVSENTRIES *entlist = NULL; 647 const char *state; 648 char repo[PATH_MAX], rcsfile[PATH_MAX]; 649 650 cvs_log(LP_TRACE, "cvs_file_classify(%s, %s)", cf->file_path, 651 (tag != NULL) ? tag : "none"); 652 653 if (!strcmp(cf->file_path, ".")) { 654 cf->file_status = FILE_UPTODATE; 655 return; 656 } 657 658 cvs_get_repository_path(cf->file_wd, repo, PATH_MAX); 659 (void)xsnprintf(rcsfile, PATH_MAX, "%s/%s", 660 repo, cf->file_name); 661 662 if (cf->file_type == CVS_FILE) { 663 len = strlcat(rcsfile, RCS_FILE_EXT, PATH_MAX); 664 if (len >= PATH_MAX) 665 fatal("cvs_file_classify: truncation"); 666 } 667 668 cf->file_rpath = xstrdup(rcsfile); 669 670 if (cmdp->cmd_flags & CVS_USE_WDIR) { 671 entlist = cvs_ent_open(cf->file_wd); 672 cf->file_ent = cvs_ent_get(entlist, cf->file_name); 673 } else 674 cf->file_ent = NULL; 675 676 if (cf->file_ent != NULL) { 677 if (cvs_specified_tag == NULL) 678 tag = cf->file_ent->ce_tag; 679 680 if (cf->file_flags & FILE_ON_DISK && 681 cf->file_ent->ce_type == CVS_ENT_FILE && 682 cf->file_type == CVS_DIR && tag != NULL) { 683 cf->file_status = FILE_SKIP; 684 return; 685 } 686 687 if (cf->file_flags & FILE_ON_DISK && 688 cf->file_ent->ce_type == CVS_ENT_DIR && 689 cf->file_type == CVS_FILE && tag != NULL) { 690 cf->file_status = FILE_SKIP; 691 return; 692 } 693 694 if (cf->file_flags & FILE_INSIDE_ATTIC && 695 cf->file_ent->ce_type == CVS_ENT_DIR && 696 cf->file_type != CVS_DIR) { 697 cf->file_status = FILE_SKIP; 698 return; 699 } 700 701 if (cf->file_flags & FILE_ON_DISK && 702 cf->file_ent->ce_type == CVS_ENT_DIR && 703 cf->file_type != CVS_DIR) 704 fatal("%s is supposed to be a directory, but it is not", 705 cf->file_path); 706 if (cf->file_flags & FILE_ON_DISK && 707 cf->file_ent->ce_type == CVS_ENT_FILE && 708 cf->file_type != CVS_FILE) 709 fatal("%s is supposed to be a file, but it is not", 710 cf->file_path); 711 } 712 713 if (cf->file_type == CVS_DIR) { 714 if (!(cmdp->cmd_flags & CVS_USE_WDIR)) 715 cf->file_status = FILE_UPTODATE; 716 else if (cf->fd == -1 && stat(rcsfile, &st) != -1) 717 cf->file_status = DIR_CREATE; 718 else if (cf->file_ent != NULL || cvs_cmdop == CVS_OP_RLOG || 719 cvs_cmdop == CVS_OP_RTAG) 720 cf->file_status = FILE_UPTODATE; 721 else 722 cf->file_status = FILE_UNKNOWN; 723 724 return; 725 } 726 727 rflags = RCS_READ; 728 switch (cvs_cmdop) { 729 case CVS_OP_COMMIT: 730 case CVS_OP_TAG: 731 case CVS_OP_RTAG: 732 rflags = RCS_WRITE; 733 break; 734 case CVS_OP_ADMIN: 735 case CVS_OP_IMPORT: 736 case CVS_OP_LOG: 737 case CVS_OP_RLOG: 738 rflags |= RCS_PARSE_FULLY; 739 break; 740 default: 741 break; 742 } 743 744 cf->repo_fd = open(cf->file_rpath, O_RDONLY); 745 if (cf->repo_fd != -1) { 746 cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, rflags); 747 if (cf->file_rcs == NULL) 748 fatal("cvs_file_classify: failed to parse RCS"); 749 } else { 750 (void)xsnprintf(rcsfile, PATH_MAX, "%s/%s/%s%s", 751 repo, CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT); 752 753 cf->repo_fd = open(rcsfile, O_RDONLY); 754 if (cf->repo_fd != -1) { 755 free(cf->file_rpath); 756 cf->file_rpath = xstrdup(rcsfile); 757 cf->file_rcs = rcs_open(cf->file_rpath, 758 cf->repo_fd, rflags); 759 if (cf->file_rcs == NULL) 760 fatal("cvs_file_classify: failed to parse RCS"); 761 cf->in_attic = 1; 762 } else { 763 cf->file_rcs = NULL; 764 } 765 } 766 767 notag = 0; 768 cf->file_flags |= FILE_HAS_TAG; 769 if (tag != NULL && cf->file_rcs != NULL) { 770 if ((cf->file_rcsrev = rcs_translate_tag(tag, cf->file_rcs)) 771 == NULL) { 772 cf->file_rcsrev = rcs_translate_tag(NULL, cf->file_rcs); 773 if (cf->file_rcsrev != NULL) { 774 notag = 1; 775 cf->file_flags &= ~FILE_HAS_TAG; 776 } 777 } 778 } else if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL) { 779 cf->file_rcsrev = rcsnum_alloc(); 780 rcsnum_cpy(cf->file_ent->ce_rev, cf->file_rcsrev, 0); 781 } else if (cf->file_rcs != NULL) { 782 cf->file_rcsrev = rcs_translate_tag(NULL, cf->file_rcs); 783 } else { 784 cf->file_rcsrev = NULL; 785 } 786 787 ismodified = rcsdead = 0; 788 if ((cf->file_flags & FILE_ON_DISK) && cf->file_ent != NULL) { 789 if (fstat(cf->fd, &st) == -1) 790 fatal("cvs_file_classify: %s", strerror(errno)); 791 792 if (st.st_mtime != cf->file_ent->ce_mtime) 793 ismodified = 1; 794 } 795 796 server_has_file = 0; 797 if (cvs_server_active == 1 && cf->file_ent != NULL && 798 cf->file_ent->ce_mtime == CVS_SERVER_UPTODATE) { 799 server_has_file = 1; 800 ismodified = 0; 801 } 802 803 if ((server_has_file == 1) || (cf->fd != -1)) 804 cf->file_flags |= FILE_ON_DISK; 805 806 if (ismodified == 1 && 807 (cf->file_flags & FILE_ON_DISK) && cf->file_rcs != NULL && 808 cf->file_ent != NULL && !RCSNUM_ISBRANCH(cf->file_ent->ce_rev) && 809 cf->file_ent->ce_status != CVS_ENT_ADDED) { 810 b1 = rcs_rev_getbuf(cf->file_rcs, cf->file_ent->ce_rev, 0); 811 b2 = buf_load_fd(cf->fd); 812 813 if (buf_differ(b1, b2)) 814 ismodified = 1; 815 else 816 ismodified = 0; 817 buf_free(b1); 818 buf_free(b2); 819 } 820 821 if (cf->file_rcs != NULL && cf->file_rcsrev != NULL && 822 !RCSNUM_ISBRANCH(cf->file_rcsrev)) { 823 state = rcs_state_get(cf->file_rcs, cf->file_rcsrev); 824 if (state == NULL) 825 fatal("failed to get state for HEAD for %s", 826 cf->file_path); 827 if (!strcmp(state, RCS_STATE_DEAD)) 828 rcsdead = 1; 829 830 if (cvs_specified_date == -1 && cvs_directory_date == -1 && 831 tag == NULL && cf->in_attic && 832 !RCSNUM_ISBRANCHREV(cf->file_rcsrev)) 833 rcsdead = 1; 834 835 cf->file_rcs->rf_dead = rcsdead; 836 } 837 838 /* 839 * 10 Sin 840 * 20 Goto hell 841 * (I welcome you if-else hell) 842 */ 843 if (cf->file_ent == NULL) { 844 if (cf->file_rcs == NULL) { 845 if (!(cf->file_flags & FILE_ON_DISK)) { 846 cvs_log(LP_NOTICE, 847 "nothing known about '%s'", 848 cf->file_path); 849 } 850 851 cf->file_status = FILE_UNKNOWN; 852 } else if (rcsdead == 1 || !(cf->file_flags & FILE_HAS_TAG)) { 853 if (!(cf->file_flags & FILE_ON_DISK)) { 854 cf->file_status = FILE_UPTODATE; 855 } else if (cvs_cmdop != CVS_OP_ADD) { 856 cf->file_status = FILE_UNKNOWN; 857 } 858 } else if (notag == 0 && cf->file_rcsrev != NULL) { 859 cf->file_status = FILE_CHECKOUT; 860 } else { 861 cf->file_status = FILE_UPTODATE; 862 } 863 864 return; 865 } 866 867 switch (cf->file_ent->ce_status) { 868 case CVS_ENT_ADDED: 869 if (!(cf->file_flags & FILE_ON_DISK)) { 870 if (cvs_cmdop != CVS_OP_REMOVE) { 871 cvs_log(LP_NOTICE, 872 "warning: new-born %s has disappeared", 873 cf->file_path); 874 } 875 cf->file_status = FILE_REMOVE_ENTRY; 876 } else if (cf->file_rcs == NULL || rcsdead == 1 || 877 !(cf->file_flags & FILE_HAS_TAG)) { 878 cf->file_status = FILE_ADDED; 879 } else { 880 cvs_log(LP_NOTICE, 881 "conflict: %s already created by others", 882 cf->file_path); 883 cf->file_status = FILE_CONFLICT; 884 } 885 break; 886 case CVS_ENT_REMOVED: 887 if (cf->file_flags & FILE_ON_DISK) { 888 cvs_log(LP_NOTICE, 889 "%s should be removed but is still there", 890 cf->file_path); 891 cf->file_status = FILE_REMOVED; 892 } else if (cf->file_rcs == NULL || rcsdead == 1) { 893 cf->file_status = FILE_REMOVE_ENTRY; 894 } else { 895 if (rcsnum_differ(cf->file_ent->ce_rev, 896 cf->file_rcsrev) && cvs_cmdop != CVS_OP_ADD) { 897 cvs_log(LP_NOTICE, 898 "conflict: removed %s was modified" 899 " by a second party", 900 cf->file_path); 901 cf->file_status = FILE_CONFLICT; 902 } else { 903 cf->file_status = FILE_REMOVED; 904 } 905 } 906 break; 907 case CVS_ENT_REG: 908 if (cf->file_rcs == NULL || cf->file_rcsrev == NULL || 909 rcsdead == 1 || (reset_tag == 1 && cf->in_attic == 1) || 910 (notag == 1 && tag != NULL)) { 911 if (!(cf->file_flags & FILE_ON_DISK)) { 912 cvs_log(LP_NOTICE, 913 "warning: %s's entry exists but" 914 " is no longer in the repository," 915 " removing entry", 916 cf->file_path); 917 cf->file_status = FILE_REMOVE_ENTRY; 918 } else { 919 if (ismodified) { 920 cvs_log(LP_NOTICE, 921 "conflict: %s is no longer " 922 "in the repository but is " 923 "locally modified", 924 cf->file_path); 925 if (cvs_cmdop == CVS_OP_COMMIT) 926 cf->file_status = FILE_UNLINK; 927 else 928 cf->file_status = FILE_CONFLICT; 929 } else if (cvs_cmdop != CVS_OP_IMPORT) { 930 cvs_log(LP_NOTICE, 931 "%s is no longer in the " 932 "repository", 933 cf->file_path); 934 935 cf->file_status = FILE_UNLINK; 936 } 937 } 938 } else if (cf->file_rcsrev == NULL) { 939 cf->file_status = FILE_UNLINK; 940 } else { 941 if (!(cf->file_flags & FILE_ON_DISK)) { 942 if (cvs_cmdop != CVS_OP_REMOVE) { 943 cvs_log(LP_NOTICE, 944 "warning: %s was lost", 945 cf->file_path); 946 } 947 cf->file_status = FILE_LOST; 948 } else { 949 if (ismodified == 1) 950 cf->file_status = FILE_MODIFIED; 951 else 952 cf->file_status = FILE_UPTODATE; 953 if (rcsnum_differ(cf->file_ent->ce_rev, 954 cf->file_rcsrev)) { 955 if (cf->file_status == FILE_MODIFIED) 956 cf->file_status = FILE_MERGE; 957 else 958 cf->file_status = FILE_PATCH; 959 } 960 } 961 } 962 break; 963 case CVS_ENT_UNKNOWN: 964 if (cvs_server_active != 1) 965 fatal("server-side questionable in local mode?"); 966 cf->file_status = FILE_UNKNOWN; 967 break; 968 default: 969 break; 970 } 971 } 972 973 void 974 cvs_file_free(struct cvs_file *cf) 975 { 976 free(cf->file_name); 977 free(cf->file_wd); 978 free(cf->file_path); 979 free(cf->file_rcsrev); 980 free(cf->file_rpath); 981 if (cf->file_ent != NULL) 982 cvs_ent_free(cf->file_ent); 983 if (cf->file_rcs != NULL) 984 rcs_close(cf->file_rcs); 985 if (cf->fd != -1) 986 (void)close(cf->fd); 987 if (cf->repo_fd != -1) 988 (void)close(cf->repo_fd); 989 free(cf); 990 } 991 992 int 993 cvs_file_cmpname(const char *name1, const char *name2) 994 { 995 return (cvs_nocase == 0) ? (strcmp(name1, name2)) : 996 (strcasecmp(name1, name2)); 997 } 998 999 int 1000 cvs_file_cmp(const char *file1, const char *file2) 1001 { 1002 struct stat stb1, stb2; 1003 int fd1, fd2, ret; 1004 1005 ret = 0; 1006 1007 if ((fd1 = open(file1, O_RDONLY|O_NOFOLLOW, 0)) == -1) 1008 fatal("cvs_file_cmp: open: `%s': %s", file1, strerror(errno)); 1009 if ((fd2 = open(file2, O_RDONLY|O_NOFOLLOW, 0)) == -1) 1010 fatal("cvs_file_cmp: open: `%s': %s", file2, strerror(errno)); 1011 1012 if (fstat(fd1, &stb1) == -1) 1013 fatal("cvs_file_cmp: `%s': %s", file1, strerror(errno)); 1014 if (fstat(fd2, &stb2) == -1) 1015 fatal("cvs_file_cmp: `%s': %s", file2, strerror(errno)); 1016 1017 if (stb1.st_size != stb2.st_size || 1018 (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)) { 1019 ret = 1; 1020 goto out; 1021 } 1022 1023 if (S_ISBLK(stb1.st_mode) || S_ISCHR(stb1.st_mode)) { 1024 if (stb1.st_rdev != stb2.st_rdev) 1025 ret = 1; 1026 goto out; 1027 } 1028 1029 if (S_ISREG(stb1.st_mode)) { 1030 void *p1, *p2; 1031 1032 if ((uintmax_t)stb1.st_size > SIZE_MAX) { 1033 ret = 1; 1034 goto out; 1035 } 1036 1037 if ((p1 = mmap(NULL, stb1.st_size, PROT_READ, 1038 MAP_FILE, fd1, (off_t)0)) == MAP_FAILED) 1039 fatal("cvs_file_cmp: mmap failed"); 1040 1041 if ((p2 = mmap(NULL, stb1.st_size, PROT_READ, 1042 MAP_FILE, fd2, (off_t)0)) == MAP_FAILED) 1043 fatal("cvs_file_cmp: mmap failed"); 1044 1045 madvise(p1, stb1.st_size, MADV_SEQUENTIAL); 1046 madvise(p2, stb1.st_size, MADV_SEQUENTIAL); 1047 1048 ret = memcmp(p1, p2, stb1.st_size); 1049 1050 (void)munmap(p1, stb1.st_size); 1051 (void)munmap(p2, stb1.st_size); 1052 } 1053 1054 out: 1055 (void)close(fd1); 1056 (void)close(fd2); 1057 1058 return (ret); 1059 } 1060 1061 int 1062 cvs_file_copy(const char *from, const char *to) 1063 { 1064 struct stat st; 1065 struct timeval tv[2]; 1066 time_t atime, mtime; 1067 int src, dst, ret; 1068 1069 ret = 0; 1070 1071 cvs_log(LP_TRACE, "cvs_file_copy(%s,%s)", from, to); 1072 1073 if (cvs_noexec == 1) 1074 return (0); 1075 1076 if ((src = open(from, O_RDONLY, 0)) == -1) 1077 fatal("cvs_file_copy: open: `%s': %s", from, strerror(errno)); 1078 1079 if (fstat(src, &st) == -1) 1080 fatal("cvs_file_copy: `%s': %s", from, strerror(errno)); 1081 1082 atime = st.st_atimespec.tv_sec; 1083 mtime = st.st_mtimespec.tv_sec; 1084 1085 if (S_ISREG(st.st_mode)) { 1086 char *p; 1087 int saved_errno; 1088 1089 if ((uintmax_t)st.st_size > SIZE_MAX) { 1090 ret = -1; 1091 goto out; 1092 } 1093 1094 if ((dst = open(to, O_CREAT|O_TRUNC|O_WRONLY, 1095 st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))) == -1) 1096 fatal("cvs_file_copy: open `%s': %s", 1097 to, strerror(errno)); 1098 1099 if ((p = mmap(NULL, st.st_size, PROT_READ, 1100 MAP_FILE, src, (off_t)0)) == MAP_FAILED) { 1101 saved_errno = errno; 1102 (void)unlink(to); 1103 fatal("cvs_file_copy: mmap: %s", strerror(saved_errno)); 1104 } 1105 1106 madvise(p, st.st_size, MADV_SEQUENTIAL); 1107 1108 if (atomicio(vwrite, dst, p, st.st_size) != (size_t)st.st_size) { 1109 saved_errno = errno; 1110 (void)unlink(to); 1111 fatal("cvs_file_copy: `%s': %s", from, 1112 strerror(saved_errno)); 1113 } 1114 1115 (void)munmap(p, st.st_size); 1116 1117 tv[0].tv_sec = atime; 1118 tv[1].tv_sec = mtime; 1119 1120 if (futimes(dst, tv) == -1) { 1121 saved_errno = errno; 1122 (void)unlink(to); 1123 fatal("cvs_file_copy: futimes: %s", 1124 strerror(saved_errno)); 1125 } 1126 (void)close(dst); 1127 } 1128 out: 1129 (void)close(src); 1130 1131 return (ret); 1132 } 1133 1134 int 1135 cvs_filelist_cmp(struct cvs_filelist *f1, struct cvs_filelist *f2) 1136 { 1137 return (strcmp(f1->file_path, f2->file_path)); 1138 } 1139