1 /* filesubr.c --- subroutines for dealing with files 2 Jim Blandy <jimb@cyclic.com> 3 4 This file is part of GNU CVS. 5 6 GNU CVS is free software; you can redistribute it and/or modify it 7 under the terms of the GNU General Public License as published by the 8 Free Software Foundation; either version 2, or (at your option) any 9 later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ 19 20 /* These functions were moved out of subr.c because they need different 21 definitions under operating systems (like, say, Windows NT) with different 22 file system semantics. */ 23 24 #include "cvs.h" 25 26 /* 27 * I don't know of a convenient way to test this at configure time, or else 28 * I'd certainly do it there. 29 */ 30 #if defined(NeXT) 31 #define LOSING_TMPNAM_FUNCTION 32 #endif 33 34 static int deep_remove_dir PROTO((const char *path)); 35 36 /* 37 * Copies "from" to "to". 38 */ 39 void 40 copy_file (from, to) 41 const char *from; 42 const char *to; 43 { 44 struct stat sb; 45 struct utimbuf t; 46 int fdin, fdout; 47 48 if (trace) 49 #ifdef SERVER_SUPPORT 50 (void) fprintf (stderr, "%c-> copy(%s,%s)\n", 51 (server_active) ? 'S' : ' ', from, to); 52 #else 53 (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to); 54 #endif 55 if (noexec) 56 return; 57 58 if ((fdin = open (from, O_RDONLY)) < 0) 59 error (1, errno, "cannot open %s for copying", from); 60 if (fstat (fdin, &sb) < 0) 61 error (1, errno, "cannot fstat %s", from); 62 if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0) 63 error (1, errno, "cannot create %s for copying", to); 64 if (sb.st_size > 0) 65 { 66 char buf[BUFSIZ]; 67 int n; 68 69 for (;;) 70 { 71 n = read (fdin, buf, sizeof(buf)); 72 if (n == -1) 73 { 74 #ifdef EINTR 75 if (errno == EINTR) 76 continue; 77 #endif 78 error (1, errno, "cannot read file %s for copying", from); 79 } 80 else if (n == 0) 81 break; 82 83 if (write(fdout, buf, n) != n) { 84 error (1, errno, "cannot write file %s for copying", to); 85 } 86 } 87 88 #ifdef HAVE_FSYNC 89 if (fsync (fdout)) 90 error (1, errno, "cannot fsync file %s after copying", to); 91 #endif 92 } 93 94 if (close (fdin) < 0) 95 error (0, errno, "cannot close %s", from); 96 if (close (fdout) < 0) 97 error (1, errno, "cannot close %s", to); 98 99 /* now, set the times for the copied file to match those of the original */ 100 memset ((char *) &t, 0, sizeof (t)); 101 t.actime = sb.st_atime; 102 t.modtime = sb.st_mtime; 103 (void) utime (to, &t); 104 } 105 106 /* FIXME-krp: these functions would benefit from caching the char * & 107 stat buf. */ 108 109 /* 110 * Returns non-zero if the argument file is a directory, or is a symbolic 111 * link which points to a directory. 112 */ 113 int 114 isdir (file) 115 const char *file; 116 { 117 struct stat sb; 118 119 if (stat (file, &sb) < 0) 120 return (0); 121 return (S_ISDIR (sb.st_mode)); 122 } 123 124 /* 125 * Returns non-zero if the argument file is a symbolic link. 126 */ 127 int 128 islink (file) 129 const char *file; 130 { 131 #ifdef S_ISLNK 132 struct stat sb; 133 134 if (lstat (file, &sb) < 0) 135 return (0); 136 return (S_ISLNK (sb.st_mode)); 137 #else 138 return (0); 139 #endif 140 } 141 142 /* 143 * Returns non-zero if the argument file exists. 144 */ 145 int 146 isfile (file) 147 const char *file; 148 { 149 return isaccessible(file, F_OK); 150 } 151 152 /* 153 * Returns non-zero if the argument file is readable. 154 */ 155 int 156 isreadable (file) 157 const char *file; 158 { 159 return isaccessible(file, R_OK); 160 } 161 162 /* 163 * Returns non-zero if the argument file is writable. 164 */ 165 int 166 iswritable (file) 167 const char *file; 168 { 169 return isaccessible(file, W_OK); 170 } 171 172 /* 173 * Returns non-zero if the argument file is accessable according to 174 * mode. If compiled with SETXID_SUPPORT also works if cvs has setxid 175 * bits set. 176 */ 177 int 178 isaccessible (file, mode) 179 const char *file; 180 const int mode; 181 { 182 #ifdef SETXID_SUPPORT 183 struct stat sb; 184 int umask = 0; 185 int gmask = 0; 186 int omask = 0; 187 int uid; 188 189 if (stat(file, &sb) == -1) 190 return 0; 191 if (mode == F_OK) 192 return 1; 193 194 uid = geteuid(); 195 if (uid == 0) /* superuser */ 196 { 197 if (mode & X_OK) 198 return sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH); 199 else 200 return 1; 201 } 202 203 if (mode & R_OK) 204 { 205 umask |= S_IRUSR; 206 gmask |= S_IRGRP; 207 omask |= S_IROTH; 208 } 209 if (mode & W_OK) 210 { 211 umask |= S_IWUSR; 212 gmask |= S_IWGRP; 213 omask |= S_IWOTH; 214 } 215 if (mode & X_OK) 216 { 217 umask |= S_IXUSR; 218 gmask |= S_IXGRP; 219 omask |= S_IXOTH; 220 } 221 222 if (sb.st_uid == uid) 223 return (sb.st_mode & umask) == umask; 224 else if (sb.st_gid == getegid()) 225 return (sb.st_mode & gmask) == gmask; 226 else 227 return (sb.st_mode & omask) == omask; 228 #else 229 return access(file, mode) == 0; 230 #endif 231 } 232 233 /* 234 * Open a file and die if it fails 235 */ 236 FILE * 237 open_file (name, mode) 238 const char *name; 239 const char *mode; 240 { 241 FILE *fp; 242 243 if ((fp = fopen (name, mode)) == NULL) 244 error (1, errno, "cannot open %s", name); 245 return (fp); 246 } 247 248 /* 249 * Make a directory and die if it fails 250 */ 251 void 252 make_directory (name) 253 const char *name; 254 { 255 struct stat sb; 256 257 if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode))) 258 error (0, 0, "%s already exists but is not a directory", name); 259 if (!noexec && mkdir (name, 0777) < 0) 260 error (1, errno, "cannot make directory %s", name); 261 } 262 263 /* 264 * Make a path to the argument directory, printing a message if something 265 * goes wrong. 266 */ 267 void 268 make_directories (name) 269 const char *name; 270 { 271 char *cp; 272 273 if (noexec) 274 return; 275 276 if (mkdir (name, 0777) == 0 || errno == EEXIST) 277 return; 278 if (! existence_error (errno)) 279 { 280 error (0, errno, "cannot make path to %s", name); 281 return; 282 } 283 if ((cp = strrchr (name, '/')) == NULL) 284 return; 285 *cp = '\0'; 286 make_directories (name); 287 *cp++ = '/'; 288 if (*cp == '\0') 289 return; 290 (void) mkdir (name, 0777); 291 } 292 293 /* 294 * Change the mode of a file, either adding write permissions, or removing 295 * all write permissions. Either change honors the current umask setting. 296 */ 297 void 298 xchmod (fname, writable) 299 char *fname; 300 int writable; 301 { 302 struct stat sb; 303 mode_t mode, oumask; 304 305 if (stat (fname, &sb) < 0) 306 { 307 if (!noexec) 308 error (0, errno, "cannot stat %s", fname); 309 return; 310 } 311 oumask = umask (0); 312 (void) umask (oumask); 313 if (writable) 314 { 315 mode = sb.st_mode | (~oumask 316 & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0) 317 | ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0) 318 | ((sb.st_mode & S_IROTH) ? S_IWOTH : 0))); 319 } 320 else 321 { 322 mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask; 323 } 324 325 if (trace) 326 #ifdef SERVER_SUPPORT 327 (void) fprintf (stderr, "%c-> chmod(%s,%o)\n", 328 (server_active) ? 'S' : ' ', fname, mode); 329 #else 330 (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode); 331 #endif 332 if (noexec) 333 return; 334 335 if (chmod (fname, mode) < 0) 336 error (0, errno, "cannot change mode of file %s", fname); 337 } 338 339 /* 340 * Rename a file and die if it fails 341 */ 342 void 343 rename_file (from, to) 344 const char *from; 345 const char *to; 346 { 347 if (trace) 348 #ifdef SERVER_SUPPORT 349 (void) fprintf (stderr, "%c-> rename(%s,%s)\n", 350 (server_active) ? 'S' : ' ', from, to); 351 #else 352 (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to); 353 #endif 354 if (noexec) 355 return; 356 357 if (rename (from, to) < 0) 358 error (1, errno, "cannot rename file %s to %s", from, to); 359 } 360 361 /* 362 * link a file, if possible. Warning: the Windows NT version of this 363 * function just copies the file, so only use this function in ways 364 * that can deal with either a link or a copy. 365 */ 366 int 367 link_file (from, to) 368 const char *from; 369 const char *to; 370 { 371 if (trace) 372 #ifdef SERVER_SUPPORT 373 (void) fprintf (stderr, "%c-> link(%s,%s)\n", 374 (server_active) ? 'S' : ' ', from, to); 375 #else 376 (void) fprintf (stderr, "-> link(%s,%s)\n", from, to); 377 #endif 378 if (noexec) 379 return (0); 380 381 return (link (from, to)); 382 } 383 384 /* 385 * unlink a file, if possible. 386 */ 387 int 388 unlink_file (f) 389 const char *f; 390 { 391 if (trace) 392 #ifdef SERVER_SUPPORT 393 (void) fprintf (stderr, "%c-> unlink(%s)\n", 394 (server_active) ? 'S' : ' ', f); 395 #else 396 (void) fprintf (stderr, "-> unlink(%s)\n", f); 397 #endif 398 if (noexec) 399 return (0); 400 401 return (unlink (f)); 402 } 403 404 /* 405 * Unlink a file or dir, if possible. If it is a directory do a deep 406 * removal of all of the files in the directory. Return -1 on error 407 * (in which case errno is set). 408 */ 409 int 410 unlink_file_dir (f) 411 const char *f; 412 { 413 if (trace) 414 #ifdef SERVER_SUPPORT 415 (void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n", 416 (server_active) ? 'S' : ' ', f); 417 #else 418 (void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f); 419 #endif 420 if (noexec) 421 return (0); 422 423 if (unlink (f) != 0) 424 { 425 /* under NEXTSTEP errno is set to return EPERM if 426 * the file is a directory,or if the user is not 427 * allowed to read or write to the file. 428 * [This is probably a bug in the O/S] 429 * other systems will return EISDIR to indicate 430 * that the path is a directory. 431 */ 432 if (errno == EISDIR || errno == EPERM) 433 return deep_remove_dir (f); 434 else 435 /* The file wasn't a directory and some other 436 * error occured 437 */ 438 return -1; 439 } 440 /* We were able to remove the file from the disk */ 441 return 0; 442 } 443 444 /* Remove a directory and everything it contains. Returns 0 for 445 * success, -1 for failure (in which case errno is set). 446 */ 447 448 static int 449 deep_remove_dir (path) 450 const char *path; 451 { 452 DIR *dirp; 453 struct dirent *dp; 454 char buf[PATH_MAX]; 455 456 if (rmdir (path) != 0 && (errno == ENOTEMPTY || errno == EEXIST)) 457 { 458 if ((dirp = opendir (path)) == NULL) 459 /* If unable to open the directory return 460 * an error 461 */ 462 return -1; 463 464 while ((dp = readdir (dirp)) != NULL) 465 { 466 if (strcmp (dp->d_name, ".") == 0 || 467 strcmp (dp->d_name, "..") == 0) 468 continue; 469 470 sprintf (buf, "%s/%s", path, dp->d_name); 471 472 if (unlink (buf) != 0 ) 473 { 474 if (errno == EISDIR || errno == EPERM) 475 { 476 if (deep_remove_dir (buf)) 477 { 478 closedir (dirp); 479 return -1; 480 } 481 } 482 else 483 { 484 /* buf isn't a directory, or there are 485 * some sort of permision problems 486 */ 487 closedir (dirp); 488 return -1; 489 } 490 } 491 } 492 closedir (dirp); 493 return rmdir (path); 494 } 495 496 /* Was able to remove the directory return 0 */ 497 return 0; 498 } 499 500 /* Read NCHARS bytes from descriptor FD into BUF. 501 Return the number of characters successfully read. 502 The number returned is always NCHARS unless end-of-file or error. */ 503 static size_t 504 block_read (fd, buf, nchars) 505 int fd; 506 char *buf; 507 size_t nchars; 508 { 509 char *bp = buf; 510 size_t nread; 511 512 do 513 { 514 nread = read (fd, bp, nchars); 515 if (nread == (size_t)-1) 516 { 517 #ifdef EINTR 518 if (errno == EINTR) 519 continue; 520 #endif 521 return (size_t)-1; 522 } 523 524 if (nread == 0) 525 break; 526 527 bp += nread; 528 nchars -= nread; 529 } while (nchars != 0); 530 531 return bp - buf; 532 } 533 534 535 /* 536 * Compare "file1" to "file2". Return non-zero if they don't compare exactly. 537 */ 538 int 539 xcmp (file1, file2) 540 const char *file1; 541 const char *file2; 542 { 543 char *buf1, *buf2; 544 struct stat sb1, sb2; 545 int fd1, fd2; 546 int ret; 547 548 if ((fd1 = open (file1, O_RDONLY)) < 0) 549 error (1, errno, "cannot open file %s for comparing", file1); 550 if ((fd2 = open (file2, O_RDONLY)) < 0) 551 error (1, errno, "cannot open file %s for comparing", file2); 552 if (fstat (fd1, &sb1) < 0) 553 error (1, errno, "cannot fstat %s", file1); 554 if (fstat (fd2, &sb2) < 0) 555 error (1, errno, "cannot fstat %s", file2); 556 557 /* A generic file compare routine might compare st_dev & st_ino here 558 to see if the two files being compared are actually the same file. 559 But that won't happen in CVS, so we won't bother. */ 560 561 if (sb1.st_size != sb2.st_size) 562 ret = 1; 563 else if (sb1.st_size == 0) 564 ret = 0; 565 else 566 { 567 /* FIXME: compute the optimal buffer size by computing the least 568 common multiple of the files st_blocks field */ 569 size_t buf_size = 8 * 1024; 570 size_t read1; 571 size_t read2; 572 573 buf1 = xmalloc (buf_size); 574 buf2 = xmalloc (buf_size); 575 576 do 577 { 578 read1 = block_read (fd1, buf1, buf_size); 579 if (read1 == (size_t)-1) 580 error (1, errno, "cannot read file %s for comparing", file1); 581 582 read2 = block_read (fd2, buf2, buf_size); 583 if (read2 == (size_t)-1) 584 error (1, errno, "cannot read file %s for comparing", file2); 585 586 /* assert (read1 == read2); */ 587 588 ret = memcmp(buf1, buf2, read1); 589 } while (ret == 0 && read1 == buf_size); 590 591 free (buf1); 592 free (buf2); 593 } 594 595 (void) close (fd1); 596 (void) close (fd2); 597 return (ret); 598 } 599 600 #ifdef LOSING_TMPNAM_FUNCTION 601 char *tmpnam(char *s) 602 { 603 static char value[L_tmpnam+1]; 604 605 if (s){ 606 strcpy(s,"/tmp/cvsXXXXXX"); 607 mktemp(s); 608 return s; 609 }else{ 610 strcpy(value,"/tmp/cvsXXXXXX"); 611 mktemp(s); 612 return value; 613 } 614 } 615 #endif 616 617 /* Return non-zero iff FILENAME is absolute. 618 Trivial under Unix, but more complicated under other systems. */ 619 int 620 isabsolute (filename) 621 const char *filename; 622 { 623 return filename[0] == '/'; 624 } 625 626 627 /* Return a pointer into PATH's last component. */ 628 char * 629 last_component (path) 630 char *path; 631 { 632 char *last = strrchr (path, '/'); 633 634 if (last) 635 return last + 1; 636 else 637 return path; 638 } 639 640 /* Return the home directory. Returns a pointer to storage 641 managed by this function or its callees (currently getenv). */ 642 char * 643 get_homedir () 644 { 645 return getenv ("HOME"); 646 } 647