1 /* $OpenBSD: file.c,v 1.267 2015/11/05 09:48:21 nicm 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 (st.st_size > SIZE_MAX) 465 fatal("cvs_file_walkdir: %s: file size too big", cf->file_name); 466 467 bufsize = st.st_size; 468 if (bufsize < st.st_blksize) 469 bufsize = st.st_blksize; 470 471 buf = xmalloc(bufsize); 472 RB_INIT(&fl); 473 RB_INIT(&dl); 474 475 while ((nbytes = getdents(cf->fd, buf, bufsize)) > 0) { 476 ebuf = buf + nbytes; 477 cp = buf; 478 479 while (cp < ebuf) { 480 dp = (struct dirent *)cp; 481 if (!strcmp(dp->d_name, ".") || 482 !strcmp(dp->d_name, "..") || 483 !strcmp(dp->d_name, CVS_PATH_CVSDIR) || 484 dp->d_fileno == 0) { 485 cp += dp->d_reclen; 486 continue; 487 } 488 489 if (cvs_file_chkign(dp->d_name) && 490 cvs_cmdop != CVS_OP_RLOG && 491 cvs_cmdop != CVS_OP_RTAG) { 492 cp += dp->d_reclen; 493 continue; 494 } 495 496 (void)xsnprintf(fpath, PATH_MAX, "%s/%s", 497 cf->file_path, dp->d_name); 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 (current_cvsroot->cr_method == CVS_METHOD_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 (current_cvsroot->cr_method == CVS_METHOD_LOCAL && 610 (cmdp->cmd_flags & CVS_LOCK_REPO)) 611 cvs_repository_unlock(repo); 612 613 if (cvs_directory_tag != NULL && cmdp->cmd_flags & CVS_USE_WDIR) { 614 cvs_write_tagfile(cf->file_path, cvs_directory_tag, NULL); 615 free(cvs_directory_tag); 616 cvs_directory_tag = NULL; 617 } 618 619 cvs_file_walklist(&dl, cr); 620 cvs_file_freelist(&dl); 621 622 if (cr->leavedir != NULL) 623 cr->leavedir(cf); 624 } 625 626 void 627 cvs_file_freelist(struct cvs_flisthead *fl) 628 { 629 struct cvs_filelist *f, *nxt; 630 631 for (f = RB_MIN(cvs_flisthead, fl); f != NULL; f = nxt) { 632 nxt = RB_NEXT(cvs_flisthead, fl, f); 633 RB_REMOVE(cvs_flisthead, fl, f); 634 free(f->file_path); 635 free(f); 636 } 637 } 638 639 void 640 cvs_file_classify(struct cvs_file *cf, const char *tag) 641 { 642 size_t len; 643 struct stat st; 644 BUF *b1, *b2; 645 int server_has_file, notag; 646 int rflags, ismodified, rcsdead; 647 CVSENTRIES *entlist = NULL; 648 const char *state; 649 char repo[PATH_MAX], rcsfile[PATH_MAX]; 650 651 cvs_log(LP_TRACE, "cvs_file_classify(%s, %s)", cf->file_path, 652 (tag != NULL) ? tag : "none"); 653 654 if (!strcmp(cf->file_path, ".")) { 655 cf->file_status = FILE_UPTODATE; 656 return; 657 } 658 659 cvs_get_repository_path(cf->file_wd, repo, PATH_MAX); 660 (void)xsnprintf(rcsfile, PATH_MAX, "%s/%s", 661 repo, cf->file_name); 662 663 if (cf->file_type == CVS_FILE) { 664 len = strlcat(rcsfile, RCS_FILE_EXT, PATH_MAX); 665 if (len >= PATH_MAX) 666 fatal("cvs_file_classify: truncation"); 667 } 668 669 cf->file_rpath = xstrdup(rcsfile); 670 671 if (cmdp->cmd_flags & CVS_USE_WDIR) { 672 entlist = cvs_ent_open(cf->file_wd); 673 cf->file_ent = cvs_ent_get(entlist, cf->file_name); 674 } else 675 cf->file_ent = NULL; 676 677 if (cf->file_ent != NULL) { 678 if (cf->file_ent->ce_tag != NULL && cvs_specified_tag == NULL) 679 tag = cf->file_ent->ce_tag; 680 681 if (cf->file_flags & FILE_ON_DISK && 682 cf->file_ent->ce_type == CVS_ENT_FILE && 683 cf->file_type == CVS_DIR && tag != NULL) { 684 cf->file_status = FILE_SKIP; 685 return; 686 } 687 688 if (cf->file_flags & FILE_ON_DISK && 689 cf->file_ent->ce_type == CVS_ENT_DIR && 690 cf->file_type == CVS_FILE && tag != NULL) { 691 cf->file_status = FILE_SKIP; 692 return; 693 } 694 695 if (cf->file_flags & FILE_INSIDE_ATTIC && 696 cf->file_ent->ce_type == CVS_ENT_DIR && 697 cf->file_type != CVS_DIR) { 698 cf->file_status = FILE_SKIP; 699 return; 700 } 701 702 if (cf->file_flags & FILE_ON_DISK && 703 cf->file_ent->ce_type == CVS_ENT_DIR && 704 cf->file_type != CVS_DIR) 705 fatal("%s is supposed to be a directory, but it is not", 706 cf->file_path); 707 if (cf->file_flags & FILE_ON_DISK && 708 cf->file_ent->ce_type == CVS_ENT_FILE && 709 cf->file_type != CVS_FILE) 710 fatal("%s is supposed to be a file, but it is not", 711 cf->file_path); 712 } 713 714 if (cf->file_type == CVS_DIR) { 715 if (!(cmdp->cmd_flags & CVS_USE_WDIR)) 716 cf->file_status = FILE_UPTODATE; 717 else if (cf->fd == -1 && stat(rcsfile, &st) != -1) 718 cf->file_status = DIR_CREATE; 719 else if (cf->file_ent != NULL || cvs_cmdop == CVS_OP_RLOG || 720 cvs_cmdop == CVS_OP_RTAG) 721 cf->file_status = FILE_UPTODATE; 722 else 723 cf->file_status = FILE_UNKNOWN; 724 725 return; 726 } 727 728 rflags = RCS_READ; 729 switch (cvs_cmdop) { 730 case CVS_OP_COMMIT: 731 case CVS_OP_TAG: 732 case CVS_OP_RTAG: 733 rflags = RCS_WRITE; 734 break; 735 case CVS_OP_ADMIN: 736 case CVS_OP_IMPORT: 737 case CVS_OP_LOG: 738 case CVS_OP_RLOG: 739 rflags |= RCS_PARSE_FULLY; 740 break; 741 default: 742 break; 743 } 744 745 cf->repo_fd = open(cf->file_rpath, O_RDONLY); 746 if (cf->repo_fd != -1) { 747 cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, rflags); 748 if (cf->file_rcs == NULL) 749 fatal("cvs_file_classify: failed to parse RCS"); 750 } else { 751 (void)xsnprintf(rcsfile, PATH_MAX, "%s/%s/%s%s", 752 repo, CVS_PATH_ATTIC, cf->file_name, RCS_FILE_EXT); 753 754 cf->repo_fd = open(rcsfile, O_RDONLY); 755 if (cf->repo_fd != -1) { 756 free(cf->file_rpath); 757 cf->file_rpath = xstrdup(rcsfile); 758 cf->file_rcs = rcs_open(cf->file_rpath, 759 cf->repo_fd, rflags); 760 if (cf->file_rcs == NULL) 761 fatal("cvs_file_classify: failed to parse RCS"); 762 cf->in_attic = 1; 763 } else { 764 cf->file_rcs = NULL; 765 } 766 } 767 768 notag = 0; 769 cf->file_flags |= FILE_HAS_TAG; 770 if (tag != NULL && cf->file_rcs != NULL) { 771 if ((cf->file_rcsrev = rcs_translate_tag(tag, cf->file_rcs)) 772 == NULL) { 773 cf->file_rcsrev = rcs_translate_tag(NULL, cf->file_rcs); 774 if (cf->file_rcsrev != NULL) { 775 notag = 1; 776 cf->file_flags &= ~FILE_HAS_TAG; 777 } 778 } 779 } else if (cf->file_ent != NULL && cf->file_ent->ce_tag != NULL) { 780 cf->file_rcsrev = rcsnum_alloc(); 781 rcsnum_cpy(cf->file_ent->ce_rev, cf->file_rcsrev, 0); 782 } else if (cf->file_rcs != NULL) { 783 cf->file_rcsrev = rcs_translate_tag(NULL, cf->file_rcs); 784 } else { 785 cf->file_rcsrev = NULL; 786 } 787 788 ismodified = rcsdead = 0; 789 if ((cf->file_flags & FILE_ON_DISK) && cf->file_ent != NULL) { 790 if (fstat(cf->fd, &st) == -1) 791 fatal("cvs_file_classify: %s", strerror(errno)); 792 793 if (st.st_mtime != cf->file_ent->ce_mtime) 794 ismodified = 1; 795 } 796 797 server_has_file = 0; 798 if (cvs_server_active == 1 && cf->file_ent != NULL && 799 cf->file_ent->ce_mtime == CVS_SERVER_UPTODATE) { 800 server_has_file = 1; 801 ismodified = 0; 802 } 803 804 if ((server_has_file == 1) || (cf->fd != -1)) 805 cf->file_flags |= FILE_ON_DISK; 806 807 if (ismodified == 1 && 808 (cf->file_flags & FILE_ON_DISK) && cf->file_rcs != NULL && 809 cf->file_ent != NULL && !RCSNUM_ISBRANCH(cf->file_ent->ce_rev) && 810 cf->file_ent->ce_status != CVS_ENT_ADDED) { 811 b1 = rcs_rev_getbuf(cf->file_rcs, cf->file_ent->ce_rev, 0); 812 b2 = buf_load_fd(cf->fd); 813 814 if (buf_differ(b1, b2)) 815 ismodified = 1; 816 else 817 ismodified = 0; 818 buf_free(b1); 819 buf_free(b2); 820 } 821 822 if (cf->file_rcs != NULL && cf->file_rcsrev != NULL && 823 !RCSNUM_ISBRANCH(cf->file_rcsrev)) { 824 state = rcs_state_get(cf->file_rcs, cf->file_rcsrev); 825 if (state == NULL) 826 fatal("failed to get state for HEAD for %s", 827 cf->file_path); 828 if (!strcmp(state, RCS_STATE_DEAD)) 829 rcsdead = 1; 830 831 if (cvs_specified_date == -1 && cvs_directory_date == -1 && 832 tag == NULL && cf->in_attic && 833 !RCSNUM_ISBRANCHREV(cf->file_rcsrev)) 834 rcsdead = 1; 835 836 cf->file_rcs->rf_dead = rcsdead; 837 } 838 839 /* 840 * 10 Sin 841 * 20 Goto hell 842 * (I welcome you if-else hell) 843 */ 844 if (cf->file_ent == NULL) { 845 if (cf->file_rcs == NULL) { 846 if (!(cf->file_flags & FILE_ON_DISK)) { 847 cvs_log(LP_NOTICE, 848 "nothing known about '%s'", 849 cf->file_path); 850 } 851 852 cf->file_status = FILE_UNKNOWN; 853 } else if (rcsdead == 1 || !(cf->file_flags & FILE_HAS_TAG)) { 854 if (!(cf->file_flags & FILE_ON_DISK)) { 855 cf->file_status = FILE_UPTODATE; 856 } else if (cvs_cmdop != CVS_OP_ADD) { 857 cf->file_status = FILE_UNKNOWN; 858 } 859 } else if (notag == 0 && cf->file_rcsrev != NULL) { 860 cf->file_status = FILE_CHECKOUT; 861 } else { 862 cf->file_status = FILE_UPTODATE; 863 } 864 865 return; 866 } 867 868 switch (cf->file_ent->ce_status) { 869 case CVS_ENT_ADDED: 870 if (!(cf->file_flags & FILE_ON_DISK)) { 871 if (cvs_cmdop != CVS_OP_REMOVE) { 872 cvs_log(LP_NOTICE, 873 "warning: new-born %s has disappeared", 874 cf->file_path); 875 } 876 cf->file_status = FILE_REMOVE_ENTRY; 877 } else if (cf->file_rcs == NULL || rcsdead == 1 || 878 !(cf->file_flags & FILE_HAS_TAG)) { 879 cf->file_status = FILE_ADDED; 880 } else { 881 cvs_log(LP_NOTICE, 882 "conflict: %s already created by others", 883 cf->file_path); 884 cf->file_status = FILE_CONFLICT; 885 } 886 break; 887 case CVS_ENT_REMOVED: 888 if (cf->file_flags & FILE_ON_DISK) { 889 cvs_log(LP_NOTICE, 890 "%s should be removed but is still there", 891 cf->file_path); 892 cf->file_status = FILE_REMOVED; 893 } else if (cf->file_rcs == NULL || rcsdead == 1) { 894 cf->file_status = FILE_REMOVE_ENTRY; 895 } else { 896 if (rcsnum_differ(cf->file_ent->ce_rev, 897 cf->file_rcsrev) && cvs_cmdop != CVS_OP_ADD) { 898 cvs_log(LP_NOTICE, 899 "conflict: removed %s was modified" 900 " by a second party", 901 cf->file_path); 902 cf->file_status = FILE_CONFLICT; 903 } else { 904 cf->file_status = FILE_REMOVED; 905 } 906 } 907 break; 908 case CVS_ENT_REG: 909 if (cf->file_rcs == NULL || cf->file_rcsrev == NULL || 910 rcsdead == 1 || (reset_tag == 1 && cf->in_attic == 1) || 911 (notag == 1 && tag != NULL)) { 912 if (!(cf->file_flags & FILE_ON_DISK)) { 913 cvs_log(LP_NOTICE, 914 "warning: %s's entry exists but" 915 " is no longer in the repository," 916 " removing entry", 917 cf->file_path); 918 cf->file_status = FILE_REMOVE_ENTRY; 919 } else { 920 if (ismodified) { 921 cvs_log(LP_NOTICE, 922 "conflict: %s is no longer " 923 "in the repository but is " 924 "locally modified", 925 cf->file_path); 926 if (cvs_cmdop == CVS_OP_COMMIT) 927 cf->file_status = FILE_UNLINK; 928 else 929 cf->file_status = FILE_CONFLICT; 930 } else if (cvs_cmdop != CVS_OP_IMPORT) { 931 cvs_log(LP_NOTICE, 932 "%s is no longer in the " 933 "repository", 934 cf->file_path); 935 936 cf->file_status = FILE_UNLINK; 937 } 938 } 939 } else if (cf->file_rcsrev == NULL) { 940 cf->file_status = FILE_UNLINK; 941 } else { 942 if (!(cf->file_flags & FILE_ON_DISK)) { 943 if (cvs_cmdop != CVS_OP_REMOVE) { 944 cvs_log(LP_NOTICE, 945 "warning: %s was lost", 946 cf->file_path); 947 } 948 cf->file_status = FILE_LOST; 949 } else { 950 if (ismodified == 1) 951 cf->file_status = FILE_MODIFIED; 952 else 953 cf->file_status = FILE_UPTODATE; 954 if (rcsnum_differ(cf->file_ent->ce_rev, 955 cf->file_rcsrev)) { 956 if (cf->file_status == FILE_MODIFIED) 957 cf->file_status = FILE_MERGE; 958 else 959 cf->file_status = FILE_PATCH; 960 } 961 } 962 } 963 break; 964 case CVS_ENT_UNKNOWN: 965 if (cvs_server_active != 1) 966 fatal("server-side questionable in local mode?"); 967 cf->file_status = FILE_UNKNOWN; 968 break; 969 default: 970 break; 971 } 972 } 973 974 void 975 cvs_file_free(struct cvs_file *cf) 976 { 977 free(cf->file_name); 978 free(cf->file_wd); 979 free(cf->file_path); 980 981 if (cf->file_rcsrev != NULL) 982 rcsnum_free(cf->file_rcsrev); 983 free(cf->file_rpath); 984 if (cf->file_ent != NULL) 985 cvs_ent_free(cf->file_ent); 986 if (cf->file_rcs != NULL) 987 rcs_close(cf->file_rcs); 988 if (cf->fd != -1) 989 (void)close(cf->fd); 990 if (cf->repo_fd != -1) 991 (void)close(cf->repo_fd); 992 free(cf); 993 } 994 995 int 996 cvs_file_cmpname(const char *name1, const char *name2) 997 { 998 return (cvs_nocase == 0) ? (strcmp(name1, name2)) : 999 (strcasecmp(name1, name2)); 1000 } 1001 1002 int 1003 cvs_file_cmp(const char *file1, const char *file2) 1004 { 1005 struct stat stb1, stb2; 1006 int fd1, fd2, ret; 1007 1008 ret = 0; 1009 1010 if ((fd1 = open(file1, O_RDONLY|O_NOFOLLOW, 0)) == -1) 1011 fatal("cvs_file_cmp: open: `%s': %s", file1, strerror(errno)); 1012 if ((fd2 = open(file2, O_RDONLY|O_NOFOLLOW, 0)) == -1) 1013 fatal("cvs_file_cmp: open: `%s': %s", file2, strerror(errno)); 1014 1015 if (fstat(fd1, &stb1) == -1) 1016 fatal("cvs_file_cmp: `%s': %s", file1, strerror(errno)); 1017 if (fstat(fd2, &stb2) == -1) 1018 fatal("cvs_file_cmp: `%s': %s", file2, strerror(errno)); 1019 1020 if (stb1.st_size != stb2.st_size || 1021 (stb1.st_mode & S_IFMT) != (stb2.st_mode & S_IFMT)) { 1022 ret = 1; 1023 goto out; 1024 } 1025 1026 if (S_ISBLK(stb1.st_mode) || S_ISCHR(stb1.st_mode)) { 1027 if (stb1.st_rdev != stb2.st_rdev) 1028 ret = 1; 1029 goto out; 1030 } 1031 1032 if (S_ISREG(stb1.st_mode)) { 1033 void *p1, *p2; 1034 1035 if (stb1.st_size > SIZE_MAX) { 1036 ret = 1; 1037 goto out; 1038 } 1039 1040 if ((p1 = mmap(NULL, stb1.st_size, PROT_READ, 1041 MAP_FILE, fd1, (off_t)0)) == MAP_FAILED) 1042 fatal("cvs_file_cmp: mmap failed"); 1043 1044 if ((p2 = mmap(NULL, stb1.st_size, PROT_READ, 1045 MAP_FILE, fd2, (off_t)0)) == MAP_FAILED) 1046 fatal("cvs_file_cmp: mmap failed"); 1047 1048 madvise(p1, stb1.st_size, MADV_SEQUENTIAL); 1049 madvise(p2, stb1.st_size, MADV_SEQUENTIAL); 1050 1051 ret = memcmp(p1, p2, stb1.st_size); 1052 1053 (void)munmap(p1, stb1.st_size); 1054 (void)munmap(p2, stb1.st_size); 1055 } 1056 1057 out: 1058 (void)close(fd1); 1059 (void)close(fd2); 1060 1061 return (ret); 1062 } 1063 1064 int 1065 cvs_file_copy(const char *from, const char *to) 1066 { 1067 struct stat st; 1068 struct timeval tv[2]; 1069 time_t atime, mtime; 1070 int src, dst, ret; 1071 1072 ret = 0; 1073 1074 cvs_log(LP_TRACE, "cvs_file_copy(%s,%s)", from, to); 1075 1076 if (cvs_noexec == 1) 1077 return (0); 1078 1079 if ((src = open(from, O_RDONLY, 0)) == -1) 1080 fatal("cvs_file_copy: open: `%s': %s", from, strerror(errno)); 1081 1082 if (fstat(src, &st) == -1) 1083 fatal("cvs_file_copy: `%s': %s", from, strerror(errno)); 1084 1085 atime = st.st_atimespec.tv_sec; 1086 mtime = st.st_mtimespec.tv_sec; 1087 1088 if (S_ISREG(st.st_mode)) { 1089 char *p; 1090 int saved_errno; 1091 1092 if (st.st_size > SIZE_MAX) { 1093 ret = -1; 1094 goto out; 1095 } 1096 1097 if ((dst = open(to, O_CREAT|O_TRUNC|O_WRONLY, 1098 st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))) == -1) 1099 fatal("cvs_file_copy: open `%s': %s", 1100 to, strerror(errno)); 1101 1102 if ((p = mmap(NULL, st.st_size, PROT_READ, 1103 MAP_FILE, src, (off_t)0)) == MAP_FAILED) { 1104 saved_errno = errno; 1105 (void)unlink(to); 1106 fatal("cvs_file_copy: mmap: %s", strerror(saved_errno)); 1107 } 1108 1109 madvise(p, st.st_size, MADV_SEQUENTIAL); 1110 1111 if (atomicio(vwrite, dst, p, st.st_size) != st.st_size) { 1112 saved_errno = errno; 1113 (void)unlink(to); 1114 fatal("cvs_file_copy: `%s': %s", from, 1115 strerror(saved_errno)); 1116 } 1117 1118 (void)munmap(p, st.st_size); 1119 1120 tv[0].tv_sec = atime; 1121 tv[1].tv_sec = mtime; 1122 1123 if (futimes(dst, tv) == -1) { 1124 saved_errno = errno; 1125 (void)unlink(to); 1126 fatal("cvs_file_copy: futimes: %s", 1127 strerror(saved_errno)); 1128 } 1129 (void)close(dst); 1130 } 1131 out: 1132 (void)close(src); 1133 1134 return (ret); 1135 } 1136 1137 int 1138 cvs_filelist_cmp(struct cvs_filelist *f1, struct cvs_filelist *f2) 1139 { 1140 return (strcmp(f1->file_path, f2->file_path)); 1141 } 1142