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