1 /* $OpenBSD: file.c,v 1.269 2016/10/15 22:20:17 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 <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 (current_cvsroot->cr_method != CVS_METHOD_LOCAL || 256 cvs_server_active == 1) 257 cvs_validate_directory(cf->file_path); 258 259 return (cf); 260 } 261 262 void 263 cvs_file_walklist(struct cvs_flisthead *fl, struct cvs_recursion *cr) 264 { 265 int fd, type; 266 struct stat st; 267 struct cvs_file *cf; 268 struct cvs_filelist *l, *nxt; 269 char *d, *f, 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 ((f = basename(l->file_path)) == NULL) 279 fatal("cvs_file_walklist: basename failed"); 280 if ((d = dirname(l->file_path)) == NULL) 281 fatal("cvs_file_walklist: dirname failed"); 282 283 type = l->type; 284 if ((fd = open(l->file_path, O_RDONLY)) != -1) { 285 if (type == 0) { 286 if (fstat(fd, &st) == -1) { 287 cvs_log(LP_ERRNO, "%s", l->file_path); 288 (void)close(fd); 289 goto next; 290 } 291 292 if (S_ISDIR(st.st_mode)) 293 type = CVS_DIR; 294 else if (S_ISREG(st.st_mode)) 295 type = CVS_FILE; 296 else { 297 cvs_log(LP_ERR, 298 "ignoring bad file type for %s", 299 l->file_path); 300 (void)close(fd); 301 goto next; 302 } 303 } 304 } else if (current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 305 /* 306 * During checkout -p, do not use any locally 307 * available directories. 308 */ 309 if ((cmdp->cmd_flags & CVS_USE_WDIR) && 310 (cvs_cmdop != CVS_OP_CHECKOUT || !print_stdout)) 311 if (stat(d, &st) == -1) { 312 cvs_log(LP_ERRNO, "%s", d); 313 goto next; 314 } 315 316 cvs_get_repository_path(d, repo, PATH_MAX); 317 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", 318 repo, f); 319 320 if ((fd = open(fpath, O_RDONLY)) == -1) { 321 strlcat(fpath, RCS_FILE_EXT, PATH_MAX); 322 fd = open(fpath, O_RDONLY); 323 } 324 325 if (fd != -1 && type == 0) { 326 if (fstat(fd, &st) == -1) 327 fatal("cvs_file_walklist: %s: %s", 328 fpath, strerror(errno)); 329 330 if (S_ISDIR(st.st_mode)) 331 type = CVS_DIR; 332 else if (S_ISREG(st.st_mode)) 333 type = CVS_FILE; 334 else { 335 cvs_log(LP_ERR, 336 "ignoring bad file type for %s", 337 l->file_path); 338 (void)close(fd); 339 goto next; 340 } 341 342 /* this file is not in our working copy yet */ 343 (void)close(fd); 344 fd = -1; 345 } else if (fd != -1) { 346 close(fd); 347 fd = -1; 348 } 349 } 350 351 cf = cvs_file_get_cf(d, f, l->file_path, fd, type, l->flags); 352 if (cf->file_type == CVS_DIR) { 353 cvs_file_walkdir(cf, cr); 354 } else { 355 if (l->flags & FILE_USER_SUPPLIED) { 356 cvs_parse_tagfile(cf->file_wd, 357 &cvs_directory_tag, NULL, NULL); 358 359 if (cvs_directory_tag == NULL && 360 cvs_specified_tag != NULL) 361 cvs_directory_tag = 362 xstrdup(cvs_specified_tag); 363 364 if (current_cvsroot->cr_method == 365 CVS_METHOD_LOCAL) { 366 cvs_get_repository_path(cf->file_wd, 367 repo, PATH_MAX); 368 cvs_repository_lock(repo, 369 (cmdp->cmd_flags & CVS_LOCK_REPO)); 370 } 371 } 372 373 if (cr->fileproc != NULL) 374 cr->fileproc(cf); 375 376 if (l->flags & FILE_USER_SUPPLIED) { 377 if (cmdp->cmd_flags & CVS_LOCK_REPO) 378 cvs_repository_unlock(repo); 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 if (cvs_file_chkign(dp->d_name) && 488 cvs_cmdop != CVS_OP_RLOG && 489 cvs_cmdop != CVS_OP_RTAG) { 490 cp += dp->d_reclen; 491 continue; 492 } 493 494 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", 495 cf->file_path, dp->d_name); 496 497 /* 498 * nfs and afs will show d_type as DT_UNKNOWN 499 * for files and/or directories so when we encounter 500 * this we call lstat() on the path to be sure. 501 */ 502 if (dp->d_type == DT_UNKNOWN) { 503 if (lstat(fpath, &st) == -1) 504 fatal("'%s': %s", fpath, 505 strerror(errno)); 506 507 switch (st.st_mode & S_IFMT) { 508 case S_IFDIR: 509 type = CVS_DIR; 510 break; 511 case S_IFREG: 512 type = CVS_FILE; 513 break; 514 default: 515 type = FILE_SKIP; 516 break; 517 } 518 } else { 519 switch (dp->d_type) { 520 case DT_DIR: 521 type = CVS_DIR; 522 break; 523 case DT_REG: 524 type = CVS_FILE; 525 break; 526 default: 527 type = FILE_SKIP; 528 break; 529 } 530 } 531 532 if (type == FILE_SKIP) { 533 if (verbosity > 1) { 534 cvs_log(LP_NOTICE, "ignoring `%s'", 535 dp->d_name); 536 } 537 cp += dp->d_reclen; 538 continue; 539 } 540 541 switch (type) { 542 case CVS_DIR: 543 if (cr->flags & CR_RECURSE_DIRS) 544 cvs_file_get(fpath, 0, &dl, CVS_DIR); 545 break; 546 case CVS_FILE: 547 cvs_file_get(fpath, 0, &fl, CVS_FILE); 548 break; 549 default: 550 fatal("type %d unknown, shouldn't happen", 551 type); 552 } 553 554 cp += dp->d_reclen; 555 } 556 } 557 558 if (nbytes == -1) 559 fatal("cvs_file_walkdir: %s %s", cf->file_path, 560 strerror(errno)); 561 562 free(buf); 563 564 while ((ip = TAILQ_FIRST(&dir_ign_pats)) != NULL) { 565 TAILQ_REMOVE(&dir_ign_pats, ip, ip_list); 566 free(ip); 567 } 568 569 entlist = cvs_ent_open(cf->file_path); 570 TAILQ_FOREACH(line, &(entlist->cef_ent), entries_list) { 571 ent = cvs_ent_parse(line->buf); 572 573 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", cf->file_path, 574 ent->ce_name); 575 576 if (!(cr->flags & CR_RECURSE_DIRS) && 577 ent->ce_type == CVS_ENT_DIR) 578 continue; 579 if (ent->ce_type == CVS_ENT_DIR) 580 cvs_file_get(fpath, 0, &dl, CVS_DIR); 581 else if (ent->ce_type == CVS_ENT_FILE) 582 cvs_file_get(fpath, 0, &fl, CVS_FILE); 583 584 cvs_ent_free(ent); 585 } 586 587 walkrepo: 588 if (current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 589 cvs_get_repository_path(cf->file_path, repo, PATH_MAX); 590 cvs_repository_lock(repo, (cmdp->cmd_flags & CVS_LOCK_REPO)); 591 } 592 593 if (cr->flags & CR_REPO) { 594 xsnprintf(fpath, sizeof(fpath), "%s/%s", cf->file_path, 595 CVS_PATH_STATICENTRIES); 596 597 if (!(cmdp->cmd_flags & CVS_USE_WDIR) || 598 stat(fpath, &st) == -1 || build_dirs == 1) 599 cvs_repository_getdir(repo, cf->file_path, &fl, &dl, 600 (cr->flags & CR_RECURSE_DIRS) ? 601 REPOSITORY_DODIRS : 0); 602 } 603 604 cvs_file_walklist(&fl, cr); 605 cvs_file_freelist(&fl); 606 607 if (current_cvsroot->cr_method == CVS_METHOD_LOCAL && 608 (cmdp->cmd_flags & CVS_LOCK_REPO)) 609 cvs_repository_unlock(repo); 610 611 if (cvs_directory_tag != NULL && cmdp->cmd_flags & CVS_USE_WDIR) { 612 cvs_write_tagfile(cf->file_path, cvs_directory_tag, NULL); 613 free(cvs_directory_tag); 614 cvs_directory_tag = NULL; 615 } 616 617 cvs_file_walklist(&dl, cr); 618 cvs_file_freelist(&dl); 619 620 if (cr->leavedir != NULL) 621 cr->leavedir(cf); 622 } 623 624 void 625 cvs_file_freelist(struct cvs_flisthead *fl) 626 { 627 struct cvs_filelist *f, *nxt; 628 629 for (f = RB_MIN(cvs_flisthead, fl); f != NULL; f = nxt) { 630 nxt = RB_NEXT(cvs_flisthead, fl, f); 631 RB_REMOVE(cvs_flisthead, fl, f); 632 free(f->file_path); 633 free(f); 634 } 635 } 636 637 void 638 cvs_file_classify(struct cvs_file *cf, const char *tag) 639 { 640 size_t len; 641 struct stat st; 642 BUF *b1, *b2; 643 int server_has_file, notag; 644 int rflags, ismodified, rcsdead; 645 CVSENTRIES *entlist = NULL; 646 const char *state; 647 char repo[PATH_MAX], rcsfile[PATH_MAX]; 648 649 cvs_log(LP_TRACE, "cvs_file_classify(%s, %s)", cf->file_path, 650 (tag != NULL) ? tag : "none"); 651 652 if (!strcmp(cf->file_path, ".")) { 653 cf->file_status = FILE_UPTODATE; 654 return; 655 } 656 657 cvs_get_repository_path(cf->file_wd, repo, PATH_MAX); 658 (void)xsnprintf(rcsfile, PATH_MAX, "%s/%s", 659 repo, cf->file_name); 660 661 if (cf->file_type == CVS_FILE) { 662 len = strlcat(rcsfile, RCS_FILE_EXT, PATH_MAX); 663 if (len >= PATH_MAX) 664 fatal("cvs_file_classify: truncation"); 665 } 666 667 cf->file_rpath = xstrdup(rcsfile); 668 669 if (cmdp->cmd_flags & CVS_USE_WDIR) { 670 entlist = cvs_ent_open(cf->file_wd); 671 cf->file_ent = cvs_ent_get(entlist, cf->file_name); 672 } else 673 cf->file_ent = NULL; 674 675 if (cf->file_ent != NULL) { 676 if (cf->file_ent->ce_tag != NULL && cvs_specified_tag == NULL) 677 tag = cf->file_ent->ce_tag; 678 679 if (cf->file_flags & FILE_ON_DISK && 680 cf->file_ent->ce_type == CVS_ENT_FILE && 681 cf->file_type == CVS_DIR && tag != NULL) { 682 cf->file_status = FILE_SKIP; 683 return; 684 } 685 686 if (cf->file_flags & FILE_ON_DISK && 687 cf->file_ent->ce_type == CVS_ENT_DIR && 688 cf->file_type == CVS_FILE && tag != NULL) { 689 cf->file_status = FILE_SKIP; 690 return; 691 } 692 693 if (cf->file_flags & FILE_INSIDE_ATTIC && 694 cf->file_ent->ce_type == CVS_ENT_DIR && 695 cf->file_type != CVS_DIR) { 696 cf->file_status = FILE_SKIP; 697 return; 698 } 699 700 if (cf->file_flags & FILE_ON_DISK && 701 cf->file_ent->ce_type == CVS_ENT_DIR && 702 cf->file_type != CVS_DIR) 703 fatal("%s is supposed to be a directory, but it is not", 704 cf->file_path); 705 if (cf->file_flags & FILE_ON_DISK && 706 cf->file_ent->ce_type == CVS_ENT_FILE && 707 cf->file_type != CVS_FILE) 708 fatal("%s is supposed to be a file, but it is not", 709 cf->file_path); 710 } 711 712 if (cf->file_type == CVS_DIR) { 713 if (!(cmdp->cmd_flags & CVS_USE_WDIR)) 714 cf->file_status = FILE_UPTODATE; 715 else if (cf->fd == -1 && stat(rcsfile, &st) != -1) 716 cf->file_status = DIR_CREATE; 717 else if (cf->file_ent != NULL || cvs_cmdop == CVS_OP_RLOG || 718 cvs_cmdop == CVS_OP_RTAG) 719 cf->file_status = FILE_UPTODATE; 720 else 721 cf->file_status = FILE_UNKNOWN; 722 723 return; 724 } 725 726 rflags = RCS_READ; 727 switch (cvs_cmdop) { 728 case CVS_OP_COMMIT: 729 case CVS_OP_TAG: 730 case CVS_OP_RTAG: 731 rflags = RCS_WRITE; 732 break; 733 case CVS_OP_ADMIN: 734 case CVS_OP_IMPORT: 735 case CVS_OP_LOG: 736 case CVS_OP_RLOG: 737 rflags |= RCS_PARSE_FULLY; 738 break; 739 default: 740 break; 741 } 742 743 cf->repo_fd = open(cf->file_rpath, O_RDONLY); 744 if (cf->repo_fd != -1) { 745 cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, rflags); 746 if (cf->file_rcs == NULL) 747 fatal("cvs_file_classify: failed to parse RCS"); 748 } else { 749 (void)xsnprintf(rcsfile, PATH_MAX, "%s/%s/%s%s", 750 repo, CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT); 751 752 cf->repo_fd = open(rcsfile, O_RDONLY); 753 if (cf->repo_fd != -1) { 754 free(cf->file_rpath); 755 cf->file_rpath = xstrdup(rcsfile); 756 cf->file_rcs = rcs_open(cf->file_rpath, 757 cf->repo_fd, rflags); 758 if (cf->file_rcs == NULL) 759 fatal("cvs_file_classify: failed to parse RCS"); 760 cf->in_attic = 1; 761 } else { 762 cf->file_rcs = NULL; 763 } 764 } 765 766 notag = 0; 767 cf->file_flags |= FILE_HAS_TAG; 768 if (tag != NULL && cf->file_rcs != NULL) { 769 if ((cf->file_rcsrev = rcs_translate_tag(tag, cf->file_rcs)) 770 == NULL) { 771 cf->file_rcsrev = rcs_translate_tag(NULL, cf->file_rcs); 772 if (cf->file_rcsrev != NULL) { 773 notag = 1; 774 cf->file_flags &= ~FILE_HAS_TAG; 775 } 776 } 777 } else if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL) { 778 cf->file_rcsrev = rcsnum_alloc(); 779 rcsnum_cpy(cf->file_ent->ce_rev, cf->file_rcsrev, 0); 780 } else if (cf->file_rcs != NULL) { 781 cf->file_rcsrev = rcs_translate_tag(NULL, cf->file_rcs); 782 } else { 783 cf->file_rcsrev = NULL; 784 } 785 786 ismodified = rcsdead = 0; 787 if ((cf->file_flags & FILE_ON_DISK) && cf->file_ent != NULL) { 788 if (fstat(cf->fd, &st) == -1) 789 fatal("cvs_file_classify: %s", strerror(errno)); 790 791 if (st.st_mtime != cf->file_ent->ce_mtime) 792 ismodified = 1; 793 } 794 795 server_has_file = 0; 796 if (cvs_server_active == 1 && cf->file_ent != NULL && 797 cf->file_ent->ce_mtime == CVS_SERVER_UPTODATE) { 798 server_has_file = 1; 799 ismodified = 0; 800 } 801 802 if ((server_has_file == 1) || (cf->fd != -1)) 803 cf->file_flags |= FILE_ON_DISK; 804 805 if (ismodified == 1 && 806 (cf->file_flags & FILE_ON_DISK) && cf->file_rcs != NULL && 807 cf->file_ent != NULL && !RCSNUM_ISBRANCH(cf->file_ent->ce_rev) && 808 cf->file_ent->ce_status != CVS_ENT_ADDED) { 809 b1 = rcs_rev_getbuf(cf->file_rcs, cf->file_ent->ce_rev, 0); 810 b2 = buf_load_fd(cf->fd); 811 812 if (buf_differ(b1, b2)) 813 ismodified = 1; 814 else 815 ismodified = 0; 816 buf_free(b1); 817 buf_free(b2); 818 } 819 820 if (cf->file_rcs != NULL && cf->file_rcsrev != NULL && 821 !RCSNUM_ISBRANCH(cf->file_rcsrev)) { 822 state = rcs_state_get(cf->file_rcs, cf->file_rcsrev); 823 if (state == NULL) 824 fatal("failed to get state for HEAD for %s", 825 cf->file_path); 826 if (!strcmp(state, RCS_STATE_DEAD)) 827 rcsdead = 1; 828 829 if (cvs_specified_date == -1 && cvs_directory_date == -1 && 830 tag == NULL && cf->in_attic && 831 !RCSNUM_ISBRANCHREV(cf->file_rcsrev)) 832 rcsdead = 1; 833 834 cf->file_rcs->rf_dead = rcsdead; 835 } 836 837 /* 838 * 10 Sin 839 * 20 Goto hell 840 * (I welcome you if-else hell) 841 */ 842 if (cf->file_ent == NULL) { 843 if (cf->file_rcs == NULL) { 844 if (!(cf->file_flags & FILE_ON_DISK)) { 845 cvs_log(LP_NOTICE, 846 "nothing known about '%s'", 847 cf->file_path); 848 } 849 850 cf->file_status = FILE_UNKNOWN; 851 } else if (rcsdead == 1 || !(cf->file_flags & FILE_HAS_TAG)) { 852 if (!(cf->file_flags & FILE_ON_DISK)) { 853 cf->file_status = FILE_UPTODATE; 854 } else if (cvs_cmdop != CVS_OP_ADD) { 855 cf->file_status = FILE_UNKNOWN; 856 } 857 } else if (notag == 0 && cf->file_rcsrev != NULL) { 858 cf->file_status = FILE_CHECKOUT; 859 } else { 860 cf->file_status = FILE_UPTODATE; 861 } 862 863 return; 864 } 865 866 switch (cf->file_ent->ce_status) { 867 case CVS_ENT_ADDED: 868 if (!(cf->file_flags & FILE_ON_DISK)) { 869 if (cvs_cmdop != CVS_OP_REMOVE) { 870 cvs_log(LP_NOTICE, 871 "warning: new-born %s has disappeared", 872 cf->file_path); 873 } 874 cf->file_status = FILE_REMOVE_ENTRY; 875 } else if (cf->file_rcs == NULL || rcsdead == 1 || 876 !(cf->file_flags & FILE_HAS_TAG)) { 877 cf->file_status = FILE_ADDED; 878 } else { 879 cvs_log(LP_NOTICE, 880 "conflict: %s already created by others", 881 cf->file_path); 882 cf->file_status = FILE_CONFLICT; 883 } 884 break; 885 case CVS_ENT_REMOVED: 886 if (cf->file_flags & FILE_ON_DISK) { 887 cvs_log(LP_NOTICE, 888 "%s should be removed but is still there", 889 cf->file_path); 890 cf->file_status = FILE_REMOVED; 891 } else if (cf->file_rcs == NULL || rcsdead == 1) { 892 cf->file_status = FILE_REMOVE_ENTRY; 893 } else { 894 if (rcsnum_differ(cf->file_ent->ce_rev, 895 cf->file_rcsrev) && cvs_cmdop != CVS_OP_ADD) { 896 cvs_log(LP_NOTICE, 897 "conflict: removed %s was modified" 898 " by a second party", 899 cf->file_path); 900 cf->file_status = FILE_CONFLICT; 901 } else { 902 cf->file_status = FILE_REMOVED; 903 } 904 } 905 break; 906 case CVS_ENT_REG: 907 if (cf->file_rcs == NULL || cf->file_rcsrev == NULL || 908 rcsdead == 1 || (reset_tag == 1 && cf->in_attic == 1) || 909 (notag == 1 && tag != NULL)) { 910 if (!(cf->file_flags & FILE_ON_DISK)) { 911 cvs_log(LP_NOTICE, 912 "warning: %s's entry exists but" 913 " is no longer in the repository," 914 " removing entry", 915 cf->file_path); 916 cf->file_status = FILE_REMOVE_ENTRY; 917 } else { 918 if (ismodified) { 919 cvs_log(LP_NOTICE, 920 "conflict: %s is no longer " 921 "in the repository but is " 922 "locally modified", 923 cf->file_path); 924 if (cvs_cmdop == CVS_OP_COMMIT) 925 cf->file_status = FILE_UNLINK; 926 else 927 cf->file_status = FILE_CONFLICT; 928 } else if (cvs_cmdop != CVS_OP_IMPORT) { 929 cvs_log(LP_NOTICE, 930 "%s is no longer in the " 931 "repository", 932 cf->file_path); 933 934 cf->file_status = FILE_UNLINK; 935 } 936 } 937 } else if (cf->file_rcsrev == NULL) { 938 cf->file_status = FILE_UNLINK; 939 } else { 940 if (!(cf->file_flags & FILE_ON_DISK)) { 941 if (cvs_cmdop != CVS_OP_REMOVE) { 942 cvs_log(LP_NOTICE, 943 "warning: %s was lost", 944 cf->file_path); 945 } 946 cf->file_status = FILE_LOST; 947 } else { 948 if (ismodified == 1) 949 cf->file_status = FILE_MODIFIED; 950 else 951 cf->file_status = FILE_UPTODATE; 952 if (rcsnum_differ(cf->file_ent->ce_rev, 953 cf->file_rcsrev)) { 954 if (cf->file_status == FILE_MODIFIED) 955 cf->file_status = FILE_MERGE; 956 else 957 cf->file_status = FILE_PATCH; 958 } 959 } 960 } 961 break; 962 case CVS_ENT_UNKNOWN: 963 if (cvs_server_active != 1) 964 fatal("server-side questionable in local mode?"); 965 cf->file_status = FILE_UNKNOWN; 966 break; 967 default: 968 break; 969 } 970 } 971 972 void 973 cvs_file_free(struct cvs_file *cf) 974 { 975 free(cf->file_name); 976 free(cf->file_wd); 977 free(cf->file_path); 978 free(cf->file_rcsrev); 979 free(cf->file_rpath); 980 if (cf->file_ent != NULL) 981 cvs_ent_free(cf->file_ent); 982 if (cf->file_rcs != NULL) 983 rcs_close(cf->file_rcs); 984 if (cf->fd != -1) 985 (void)close(cf->fd); 986 if (cf->repo_fd != -1) 987 (void)close(cf->repo_fd); 988 free(cf); 989 } 990 991 int 992 cvs_file_cmpname(const char *name1, const char *name2) 993 { 994 return (cvs_nocase == 0) ? (strcmp(name1, name2)) : 995 (strcasecmp(name1, name2)); 996 } 997 998 int 999 cvs_file_cmp(const char *file1, const char *file2) 1000 { 1001 struct stat stb1, stb2; 1002 int fd1, fd2, ret; 1003 1004 ret = 0; 1005 1006 if ((fd1 = open(file1, O_RDONLY|O_NOFOLLOW, 0)) == -1) 1007 fatal("cvs_file_cmp: open: `%s': %s", file1, strerror(errno)); 1008 if ((fd2 = open(file2, O_RDONLY|O_NOFOLLOW, 0)) == -1) 1009 fatal("cvs_file_cmp: open: `%s': %s", file2, strerror(errno)); 1010 1011 if (fstat(fd1, &stb1) == -1) 1012 fatal("cvs_file_cmp: `%s': %s", file1, strerror(errno)); 1013 if (fstat(fd2, &stb2) == -1) 1014 fatal("cvs_file_cmp: `%s': %s", file2, strerror(errno)); 1015 1016 if (stb1.st_size != stb2.st_size || 1017 (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)) { 1018 ret = 1; 1019 goto out; 1020 } 1021 1022 if (S_ISBLK(stb1.st_mode) || S_ISCHR(stb1.st_mode)) { 1023 if (stb1.st_rdev != stb2.st_rdev) 1024 ret = 1; 1025 goto out; 1026 } 1027 1028 if (S_ISREG(stb1.st_mode)) { 1029 void *p1, *p2; 1030 1031 if ((uintmax_t)stb1.st_size > SIZE_MAX) { 1032 ret = 1; 1033 goto out; 1034 } 1035 1036 if ((p1 = mmap(NULL, stb1.st_size, PROT_READ, 1037 MAP_FILE, fd1, (off_t)0)) == MAP_FAILED) 1038 fatal("cvs_file_cmp: mmap failed"); 1039 1040 if ((p2 = mmap(NULL, stb1.st_size, PROT_READ, 1041 MAP_FILE, fd2, (off_t)0)) == MAP_FAILED) 1042 fatal("cvs_file_cmp: mmap failed"); 1043 1044 madvise(p1, stb1.st_size, MADV_SEQUENTIAL); 1045 madvise(p2, stb1.st_size, MADV_SEQUENTIAL); 1046 1047 ret = memcmp(p1, p2, stb1.st_size); 1048 1049 (void)munmap(p1, stb1.st_size); 1050 (void)munmap(p2, stb1.st_size); 1051 } 1052 1053 out: 1054 (void)close(fd1); 1055 (void)close(fd2); 1056 1057 return (ret); 1058 } 1059 1060 int 1061 cvs_file_copy(const char *from, const char *to) 1062 { 1063 struct stat st; 1064 struct timeval tv[2]; 1065 time_t atime, mtime; 1066 int src, dst, ret; 1067 1068 ret = 0; 1069 1070 cvs_log(LP_TRACE, "cvs_file_copy(%s,%s)", from, to); 1071 1072 if (cvs_noexec == 1) 1073 return (0); 1074 1075 if ((src = open(from, O_RDONLY, 0)) == -1) 1076 fatal("cvs_file_copy: open: `%s': %s", from, strerror(errno)); 1077 1078 if (fstat(src, &st) == -1) 1079 fatal("cvs_file_copy: `%s': %s", from, strerror(errno)); 1080 1081 atime = st.st_atimespec.tv_sec; 1082 mtime = st.st_mtimespec.tv_sec; 1083 1084 if (S_ISREG(st.st_mode)) { 1085 char *p; 1086 int saved_errno; 1087 1088 if ((uintmax_t)st.st_size > SIZE_MAX) { 1089 ret = -1; 1090 goto out; 1091 } 1092 1093 if ((dst = open(to, O_CREAT|O_TRUNC|O_WRONLY, 1094 st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))) == -1) 1095 fatal("cvs_file_copy: open `%s': %s", 1096 to, strerror(errno)); 1097 1098 if ((p = mmap(NULL, st.st_size, PROT_READ, 1099 MAP_FILE, src, (off_t)0)) == MAP_FAILED) { 1100 saved_errno = errno; 1101 (void)unlink(to); 1102 fatal("cvs_file_copy: mmap: %s", strerror(saved_errno)); 1103 } 1104 1105 madvise(p, st.st_size, MADV_SEQUENTIAL); 1106 1107 if (atomicio(vwrite, dst, p, st.st_size) != (size_t)st.st_size) { 1108 saved_errno = errno; 1109 (void)unlink(to); 1110 fatal("cvs_file_copy: `%s': %s", from, 1111 strerror(saved_errno)); 1112 } 1113 1114 (void)munmap(p, st.st_size); 1115 1116 tv[0].tv_sec = atime; 1117 tv[1].tv_sec = mtime; 1118 1119 if (futimes(dst, tv) == -1) { 1120 saved_errno = errno; 1121 (void)unlink(to); 1122 fatal("cvs_file_copy: futimes: %s", 1123 strerror(saved_errno)); 1124 } 1125 (void)close(dst); 1126 } 1127 out: 1128 (void)close(src); 1129 1130 return (ret); 1131 } 1132 1133 int 1134 cvs_filelist_cmp(struct cvs_filelist *f1, struct cvs_filelist *f2) 1135 { 1136 return (strcmp(f1->file_path, f2->file_path)); 1137 } 1138