1 /* $OpenBSD: file.c,v 1.258 2009/03/28 16:47:33 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 <errno.h> 34 #include <fcntl.h> 35 #include <fnmatch.h> 36 #include <libgen.h> 37 #include <string.h> 38 #include <unistd.h> 39 40 #include "atomicio.h" 41 #include "cvs.h" 42 #include "remote.h" 43 44 #define CVS_IGN_STATIC 0x01 /* pattern is static, no need to glob */ 45 46 #define CVS_CHAR_ISMETA(c) ((c == '*') || (c == '?') || (c == '[')) 47 48 extern int print_stdout; 49 extern int build_dirs; 50 51 /* 52 * Standard patterns to ignore. 53 */ 54 static const char *cvs_ign_std[] = { 55 ".", 56 "..", 57 "*.o", 58 "*.a", 59 "*.bak", 60 "*.orig", 61 "*.rej", 62 "*.old", 63 "*.exe", 64 "*.depend", 65 "*.obj", 66 "*.elc", 67 "*.ln", 68 "*.olb", 69 "CVS", 70 "core", 71 "cvslog*", 72 "*.core", 73 ".#*", 74 "*~", 75 "_$*", 76 "*$", 77 }; 78 79 char *cvs_directory_tag = NULL; 80 struct ignore_head cvs_ign_pats; 81 struct ignore_head dir_ign_pats; 82 struct ignore_head checkout_ign_pats; 83 84 RB_GENERATE(cvs_flisthead, cvs_filelist, flist, cvs_filelist_cmp); 85 86 void 87 cvs_file_init(void) 88 { 89 int i; 90 FILE *ifp; 91 char path[MAXPATHLEN], buf[MAXNAMLEN]; 92 93 TAILQ_INIT(&cvs_ign_pats); 94 TAILQ_INIT(&dir_ign_pats); 95 TAILQ_INIT(&checkout_ign_pats); 96 97 /* standard patterns to ignore */ 98 for (i = 0; i < (int)(sizeof(cvs_ign_std)/sizeof(char *)); i++) 99 cvs_file_ignore(cvs_ign_std[i], &cvs_ign_pats); 100 101 if (cvs_homedir == NULL) 102 return; 103 104 /* read the cvsignore file in the user's home directory, if any */ 105 (void)xsnprintf(path, MAXPATHLEN, "%s/.cvsignore", cvs_homedir); 106 107 ifp = fopen(path, "r"); 108 if (ifp == NULL) { 109 if (errno != ENOENT) 110 cvs_log(LP_ERRNO, 111 "failed to open user's cvsignore file `%s'", path); 112 } else { 113 while (fgets(buf, MAXNAMLEN, ifp) != NULL) { 114 buf[strcspn(buf, "\n")] = '\0'; 115 if (buf[0] == '\0') 116 continue; 117 118 cvs_file_ignore(buf, &cvs_ign_pats); 119 } 120 121 (void)fclose(ifp); 122 } 123 } 124 125 void 126 cvs_file_ignore(const char *pat, struct ignore_head *list) 127 { 128 char *cp; 129 size_t len; 130 struct cvs_ignpat *ip; 131 132 ip = xmalloc(sizeof(*ip)); 133 len = strlcpy(ip->ip_pat, pat, sizeof(ip->ip_pat)); 134 if (len >= sizeof(ip->ip_pat)) 135 fatal("cvs_file_ignore: truncation of pattern '%s'", pat); 136 137 /* check if we will need globbing for that pattern */ 138 ip->ip_flags = CVS_IGN_STATIC; 139 for (cp = ip->ip_pat; *cp != '\0'; cp++) { 140 if (CVS_CHAR_ISMETA(*cp)) { 141 ip->ip_flags &= ~CVS_IGN_STATIC; 142 break; 143 } 144 } 145 146 TAILQ_INSERT_TAIL(list, ip, ip_list); 147 } 148 149 int 150 cvs_file_chkign(const char *file) 151 { 152 int flags; 153 struct cvs_ignpat *ip; 154 155 flags = FNM_PERIOD; 156 if (cvs_nocase) 157 flags |= FNM_CASEFOLD; 158 159 TAILQ_FOREACH(ip, &cvs_ign_pats, ip_list) { 160 if (ip->ip_flags & CVS_IGN_STATIC) { 161 if (cvs_file_cmpname(file, ip->ip_pat) == 0) 162 return (1); 163 } else if (fnmatch(ip->ip_pat, file, flags) == 0) 164 return (1); 165 } 166 167 TAILQ_FOREACH(ip, &dir_ign_pats, ip_list) { 168 if (ip->ip_flags & CVS_IGN_STATIC) { 169 if (cvs_file_cmpname(file, ip->ip_pat) == 0) 170 return (1); 171 } else if (fnmatch(ip->ip_pat, file, flags) == 0) 172 return (1); 173 } 174 175 TAILQ_FOREACH(ip, &checkout_ign_pats, ip_list) { 176 if (ip->ip_flags & CVS_IGN_STATIC) { 177 if (cvs_file_cmpname(file, ip->ip_pat) == 0) 178 return (1); 179 } else if (fnmatch(ip->ip_pat, file, flags) == 0) 180 return (1); 181 } 182 183 return (0); 184 } 185 186 void 187 cvs_file_run(int argc, char **argv, struct cvs_recursion *cr) 188 { 189 int i; 190 struct cvs_flisthead fl; 191 192 RB_INIT(&fl); 193 194 for (i = 0; i < argc; i++) { 195 STRIP_SLASH(argv[i]); 196 cvs_file_get(argv[i], FILE_USER_SUPPLIED, &fl, 0); 197 } 198 199 cvs_file_walklist(&fl, cr); 200 cvs_file_freelist(&fl); 201 } 202 203 struct cvs_filelist * 204 cvs_file_get(char *name, int flags, struct cvs_flisthead *fl, int type) 205 { 206 char *p; 207 struct cvs_filelist *l, find; 208 209 for (p = name; p[0] == '.' && p[1] == '/';) 210 p += 2; 211 212 find.file_path = p; 213 l = RB_FIND(cvs_flisthead, fl, &find); 214 if (l != NULL) 215 return (l); 216 217 l = (struct cvs_filelist *)xmalloc(sizeof(*l)); 218 l->file_path = xstrdup(p); 219 l->flags = flags; 220 l->type = type; 221 222 RB_INSERT(cvs_flisthead, fl, l); 223 return (l); 224 } 225 226 struct cvs_file * 227 cvs_file_get_cf(const char *d, const char *f, const char *fpath, int fd, 228 int type, int flags) 229 { 230 const char *p; 231 struct cvs_file *cf; 232 233 for (p = fpath; p[0] == '.' && p[1] == '/';) 234 p += 2; 235 236 cf = (struct cvs_file *)xcalloc(1, sizeof(*cf)); 237 238 cf->file_name = xstrdup(f); 239 cf->file_wd = xstrdup(d); 240 cf->file_path = xstrdup(p); 241 cf->fd = fd; 242 cf->repo_fd = -1; 243 cf->file_type = type; 244 cf->file_status = 0; 245 cf->file_flags = flags; 246 cf->in_attic = 0; 247 cf->file_ent = NULL; 248 249 if (cf->fd != -1) 250 cf->file_flags |= FILE_ON_DISK; 251 252 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL || 253 cvs_server_active == 1) 254 cvs_validate_directory(cf->file_path); 255 256 return (cf); 257 } 258 259 void 260 cvs_file_walklist(struct cvs_flisthead *fl, struct cvs_recursion *cr) 261 { 262 int fd, type; 263 struct stat st; 264 struct cvs_file *cf; 265 struct cvs_filelist *l, *nxt; 266 char *d, *f, repo[MAXPATHLEN], fpath[MAXPATHLEN]; 267 268 for (l = RB_MIN(cvs_flisthead, fl); l != NULL; l = nxt) { 269 if (cvs_quit) 270 fatal("received signal %d", sig_received); 271 272 cvs_log(LP_TRACE, "cvs_file_walklist: element '%s'", 273 l->file_path); 274 275 if ((f = basename(l->file_path)) == NULL) 276 fatal("cvs_file_walklist: basename failed"); 277 if ((d = dirname(l->file_path)) == NULL) 278 fatal("cvs_file_walklist: dirname failed"); 279 280 type = l->type; 281 if ((fd = open(l->file_path, O_RDONLY)) != -1) { 282 if (type == 0) { 283 if (fstat(fd, &st) == -1) { 284 cvs_log(LP_ERRNO, "%s", l->file_path); 285 (void)close(fd); 286 goto next; 287 } 288 289 if (S_ISDIR(st.st_mode)) 290 type = CVS_DIR; 291 else if (S_ISREG(st.st_mode)) 292 type = CVS_FILE; 293 else { 294 cvs_log(LP_ERR, 295 "ignoring bad file type for %s", 296 l->file_path); 297 (void)close(fd); 298 goto next; 299 } 300 } 301 } else if (current_cvsroot->cr_method == CVS_METHOD_LOCAL) { 302 /* 303 * During checkout -p, do not use any locally 304 * available directories. 305 */ 306 if ((cmdp->cmd_flags & CVS_USE_WDIR) && 307 (cvs_cmdop != CVS_OP_CHECKOUT || !print_stdout)) 308 if (stat(d, &st) == -1) { 309 cvs_log(LP_ERRNO, "%s", d); 310 goto next; 311 } 312 313 cvs_get_repository_path(d, repo, MAXPATHLEN); 314 (void)xsnprintf(fpath, MAXPATHLEN, "%s/%s", 315 repo, f); 316 317 if ((fd = open(fpath, O_RDONLY)) == -1) { 318 strlcat(fpath, RCS_FILE_EXT, MAXPATHLEN); 319 fd = open(fpath, O_RDONLY); 320 } 321 322 if (fd != -1 && type == 0) { 323 if (fstat(fd, &st) == -1) 324 fatal("cvs_file_walklist: %s: %s", 325 fpath, strerror(errno)); 326 327 if (S_ISDIR(st.st_mode)) 328 type = CVS_DIR; 329 else if (S_ISREG(st.st_mode)) 330 type = CVS_FILE; 331 else { 332 cvs_log(LP_ERR, 333 "ignoring bad file type for %s", 334 l->file_path); 335 (void)close(fd); 336 goto next; 337 } 338 339 /* this file is not in our working copy yet */ 340 (void)close(fd); 341 fd = -1; 342 } else if (fd != -1) { 343 close(fd); 344 fd = -1; 345 } 346 } 347 348 cf = cvs_file_get_cf(d, f, l->file_path, fd, type, l->flags); 349 if (cf->file_type == CVS_DIR) { 350 cvs_file_walkdir(cf, cr); 351 } else { 352 if (l->flags & FILE_USER_SUPPLIED) { 353 cvs_parse_tagfile(cf->file_wd, 354 &cvs_directory_tag, NULL, NULL); 355 356 if (cvs_directory_tag == NULL && 357 cvs_specified_tag != NULL) 358 cvs_directory_tag = 359 xstrdup(cvs_specified_tag); 360 361 if (current_cvsroot->cr_method == 362 CVS_METHOD_LOCAL) { 363 cvs_get_repository_path(cf->file_wd, 364 repo, MAXPATHLEN); 365 cvs_repository_lock(repo, 366 (cmdp->cmd_flags & CVS_LOCK_REPO)); 367 } 368 } 369 370 if (cr->fileproc != NULL) 371 cr->fileproc(cf); 372 373 if (l->flags & FILE_USER_SUPPLIED) { 374 if (cmdp->cmd_flags & CVS_LOCK_REPO) 375 cvs_repository_unlock(repo); 376 if (cvs_directory_tag != NULL) { 377 xfree(cvs_directory_tag); 378 cvs_directory_tag = NULL; 379 } 380 } 381 } 382 383 cvs_file_free(cf); 384 385 next: 386 nxt = RB_NEXT(cvs_flisthead, fl, l); 387 } 388 } 389 390 void 391 cvs_file_walkdir(struct cvs_file *cf, struct cvs_recursion *cr) 392 { 393 int l, type; 394 FILE *fp; 395 int nbytes; 396 long base; 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[MAXPATHLEN], fpath[MAXPATHLEN]; 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, MAXPATHLEN, "%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, MAXPATHLEN, "%s/.cvsignore", cf->file_path); 447 448 if ((fp = fopen(fpath, "r")) != NULL) { 449 while (fgets(fpath, MAXPATHLEN, 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 = getdirentries(cf->fd, buf, bufsize, &base)) > 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, MAXPATHLEN, "%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 xfree(buf); 565 566 while ((ip = TAILQ_FIRST(&dir_ign_pats)) != NULL) { 567 TAILQ_REMOVE(&dir_ign_pats, ip, ip_list); 568 xfree(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, MAXPATHLEN, "%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, MAXPATHLEN); 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 (stat(fpath, &st) == -1 || build_dirs == 1) 600 cvs_repository_getdir(repo, cf->file_path, &fl, &dl, 601 (cr->flags & CR_RECURSE_DIRS) ? 602 REPOSITORY_DODIRS : 0); 603 } 604 605 cvs_file_walklist(&fl, cr); 606 cvs_file_freelist(&fl); 607 608 if (current_cvsroot->cr_method == CVS_METHOD_LOCAL && 609 (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 xfree(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 xfree(f->file_path); 634 xfree(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[MAXPATHLEN], rcsfile[MAXPATHLEN]; 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, MAXPATHLEN); 659 (void)xsnprintf(rcsfile, MAXPATHLEN, "%s/%s", 660 repo, cf->file_name); 661 662 if (cf->file_type == CVS_FILE) { 663 len = strlcat(rcsfile, RCS_FILE_EXT, MAXPATHLEN); 664 if (len >= MAXPATHLEN) 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 (cf->file_ent->ce_tag != NULL && 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, MAXPATHLEN, "%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 xfree(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 = cvs_buf_load_fd(cf->fd); 812 813 if (cvs_buf_differ(b1, b2)) 814 ismodified = 1; 815 else 816 ismodified = 0; 817 cvs_buf_free(b1); 818 cvs_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 } else if (cf->file_ent->ce_conflict != NULL && 960 cf->file_status != FILE_MODIFIED) { 961 cf->file_status = FILE_CONFLICT; 962 } 963 } 964 } 965 break; 966 case CVS_ENT_UNKNOWN: 967 if (cvs_server_active != 1) 968 fatal("server-side questionable in local mode?"); 969 cf->file_status = FILE_UNKNOWN; 970 break; 971 default: 972 break; 973 } 974 } 975 976 void 977 cvs_file_free(struct cvs_file *cf) 978 { 979 xfree(cf->file_name); 980 xfree(cf->file_wd); 981 xfree(cf->file_path); 982 983 if (cf->file_rcsrev != NULL) 984 rcsnum_free(cf->file_rcsrev); 985 if (cf->file_rpath != NULL) 986 xfree(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 xfree(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, 0)) == -1) 1014 fatal("cvs_file_cmp: open: `%s': %s", file1, strerror(errno)); 1015 if ((fd2 = open(file2, O_RDONLY|O_NOFOLLOW, 0)) == -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 (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, 0)) == -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 (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) != 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