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 struct stat sb; 155 156 if (stat (file, &sb) < 0) 157 return (0); 158 return (1); 159 } 160 161 /* 162 * Returns non-zero if the argument file is readable. 163 * XXX - must be careful if "cvs" is ever made setuid! 164 */ 165 int 166 isreadable (file) 167 const char *file; 168 { 169 return (access (file, R_OK) != -1); 170 } 171 172 /* 173 * Returns non-zero if the argument file is writable 174 * XXX - muct be careful if "cvs" is ever made setuid! 175 */ 176 int 177 iswritable (file) 178 const char *file; 179 { 180 return (access (file, W_OK) != -1); 181 } 182 183 /* 184 * Open a file and die if it fails 185 */ 186 FILE * 187 open_file (name, mode) 188 const char *name; 189 const char *mode; 190 { 191 FILE *fp; 192 193 if ((fp = fopen (name, mode)) == NULL) 194 error (1, errno, "cannot open %s", name); 195 return (fp); 196 } 197 198 /* 199 * Make a directory and die if it fails 200 */ 201 void 202 make_directory (name) 203 const char *name; 204 { 205 struct stat buf; 206 207 if (stat (name, &buf) == 0 && (!S_ISDIR (buf.st_mode))) 208 error (0, 0, "%s already exists but is not a directory", name); 209 if (!noexec && mkdir (name, 0777) < 0) 210 error (1, errno, "cannot make directory %s", name); 211 } 212 213 /* 214 * Make a path to the argument directory, printing a message if something 215 * goes wrong. 216 */ 217 void 218 make_directories (name) 219 const char *name; 220 { 221 char *cp; 222 223 if (noexec) 224 return; 225 226 if (mkdir (name, 0777) == 0 || errno == EEXIST) 227 return; 228 if (errno != ENOENT) 229 { 230 error (0, errno, "cannot make path to %s", name); 231 return; 232 } 233 if ((cp = strrchr (name, '/')) == NULL) 234 return; 235 *cp = '\0'; 236 make_directories (name); 237 *cp++ = '/'; 238 if (*cp == '\0') 239 return; 240 (void) mkdir (name, 0777); 241 } 242 243 /* 244 * Change the mode of a file, either adding write permissions, or removing 245 * all write permissions. Adding write permissions honors the current umask 246 * setting. 247 */ 248 void 249 xchmod (fname, writable) 250 char *fname; 251 int writable; 252 { 253 struct stat sb; 254 mode_t mode, oumask; 255 256 if (stat (fname, &sb) < 0) 257 { 258 if (!noexec) 259 error (0, errno, "cannot stat %s", fname); 260 return; 261 } 262 if (writable) 263 { 264 oumask = umask (0); 265 (void) umask (oumask); 266 mode = sb.st_mode | ~oumask & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0) | 267 ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0) | 268 ((sb.st_mode & S_IROTH) ? S_IWOTH : 0)); 269 } 270 else 271 { 272 mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH); 273 } 274 275 if (trace) 276 #ifdef SERVER_SUPPORT 277 (void) fprintf (stderr, "%c-> chmod(%s,%o)\n", 278 (server_active) ? 'S' : ' ', fname, mode); 279 #else 280 (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode); 281 #endif 282 if (noexec) 283 return; 284 285 if (chmod (fname, mode) < 0) 286 error (0, errno, "cannot change mode of file %s", fname); 287 } 288 289 /* 290 * Rename a file and die if it fails 291 */ 292 void 293 rename_file (from, to) 294 const char *from; 295 const char *to; 296 { 297 if (trace) 298 #ifdef SERVER_SUPPORT 299 (void) fprintf (stderr, "%c-> rename(%s,%s)\n", 300 (server_active) ? 'S' : ' ', from, to); 301 #else 302 (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to); 303 #endif 304 if (noexec) 305 return; 306 307 if (rename (from, to) < 0) 308 error (1, errno, "cannot rename file %s to %s", from, to); 309 } 310 311 /* 312 * link a file, if possible. 313 */ 314 int 315 link_file (from, to) 316 const char *from; 317 const char *to; 318 { 319 if (trace) 320 #ifdef SERVER_SUPPORT 321 (void) fprintf (stderr, "%c-> link(%s,%s)\n", 322 (server_active) ? 'S' : ' ', from, to); 323 #else 324 (void) fprintf (stderr, "-> link(%s,%s)\n", from, to); 325 #endif 326 if (noexec) 327 return (0); 328 329 return (link (from, to)); 330 } 331 332 /* 333 * unlink a file, if possible. 334 */ 335 int 336 unlink_file (f) 337 const char *f; 338 { 339 if (trace) 340 #ifdef SERVER_SUPPORT 341 (void) fprintf (stderr, "%c-> unlink(%s)\n", 342 (server_active) ? 'S' : ' ', f); 343 #else 344 (void) fprintf (stderr, "-> unlink(%s)\n", f); 345 #endif 346 if (noexec) 347 return (0); 348 349 return (unlink (f)); 350 } 351 352 /* 353 * Unlink a file or dir, if possible. If it is a directory do a deep 354 * removal of all of the files in the directory. Return -1 on error 355 * (in which case errno is set). 356 */ 357 int 358 unlink_file_dir (f) 359 const char *f; 360 { 361 if (trace) 362 #ifdef SERVER_SUPPORT 363 (void) fprintf (stderr, "%c-> unlink_file_dir(%s)\n", 364 (server_active) ? 'S' : ' ', f); 365 #else 366 (void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f); 367 #endif 368 if (noexec) 369 return (0); 370 371 if (unlink (f) != 0) 372 { 373 /* under NEXTSTEP errno is set to return EPERM if 374 * the file is a directory,or if the user is not 375 * allowed to read or write to the file. 376 * [This is probably a bug in the O/S] 377 * other systems will return EISDIR to indicate 378 * that the path is a directory. 379 */ 380 if (errno == EISDIR || errno == EPERM) 381 return deep_remove_dir (f); 382 else 383 /* The file wasn't a directory and some other 384 * error occured 385 */ 386 return -1; 387 } 388 /* We were able to remove the file from the disk */ 389 return 0; 390 } 391 392 /* Remove a directory and everything it contains. Returns 0 for 393 * success, -1 for failure (in which case errno is set). 394 */ 395 396 static int 397 deep_remove_dir (path) 398 const char *path; 399 { 400 DIR *dirp; 401 struct dirent *dp; 402 char buf[PATH_MAX]; 403 404 if ( rmdir (path) != 0 && errno == ENOTEMPTY ) 405 { 406 if ((dirp = opendir (path)) == NULL) 407 /* If unable to open the directory return 408 * an error 409 */ 410 return -1; 411 412 while ((dp = readdir (dirp)) != NULL) 413 { 414 if (strcmp (dp->d_name, ".") == 0 || 415 strcmp (dp->d_name, "..") == 0) 416 continue; 417 418 sprintf (buf, "%s/%s", path, dp->d_name); 419 420 if (unlink (buf) != 0 ) 421 { 422 if (errno == EISDIR || errno == EPERM) 423 { 424 if (deep_remove_dir (buf)) 425 { 426 closedir (dirp); 427 return -1; 428 } 429 } 430 else 431 { 432 /* buf isn't a directory, or there are 433 * some sort of permision problems 434 */ 435 closedir (dirp); 436 return -1; 437 } 438 } 439 } 440 closedir (dirp); 441 return rmdir (path); 442 } 443 /* Was able to remove the directory return 0 */ 444 return 0; 445 } 446 447 /* Read NCHARS bytes from descriptor FD into BUF. 448 Return the number of characters successfully read. 449 The number returned is always NCHARS unless end-of-file or error. */ 450 static size_t 451 block_read (fd, buf, nchars) 452 int fd; 453 char *buf; 454 size_t nchars; 455 { 456 char *bp = buf; 457 size_t nread; 458 459 do 460 { 461 nread = read (fd, bp, nchars); 462 if (nread == (size_t)-1) 463 { 464 #ifdef EINTR 465 if (errno == EINTR) 466 continue; 467 #endif 468 return (size_t)-1; 469 } 470 471 if (nread == 0) 472 break; 473 474 bp += nread; 475 nchars -= nread; 476 } while (nchars != 0); 477 478 return bp - buf; 479 } 480 481 482 /* 483 * Compare "file1" to "file2". Return non-zero if they don't compare exactly. 484 */ 485 int 486 xcmp (file1, file2) 487 const char *file1; 488 const char *file2; 489 { 490 char *buf1, *buf2; 491 struct stat sb1, sb2; 492 int fd1, fd2; 493 int ret; 494 495 if ((fd1 = open (file1, O_RDONLY)) < 0) 496 error (1, errno, "cannot open file %s for comparing", file1); 497 if ((fd2 = open (file2, O_RDONLY)) < 0) 498 error (1, errno, "cannot open file %s for comparing", file2); 499 if (fstat (fd1, &sb1) < 0) 500 error (1, errno, "cannot fstat %s", file1); 501 if (fstat (fd2, &sb2) < 0) 502 error (1, errno, "cannot fstat %s", file2); 503 504 /* A generic file compare routine might compare st_dev & st_ino here 505 to see if the two files being compared are actually the same file. 506 But that won't happen in CVS, so we won't bother. */ 507 508 if (sb1.st_size != sb2.st_size) 509 ret = 1; 510 else if (sb1.st_size == 0) 511 ret = 0; 512 else 513 { 514 /* FIXME: compute the optimal buffer size by computing the least 515 common multiple of the files st_blocks field */ 516 size_t buf_size = 8 * 1024; 517 size_t read1; 518 size_t read2; 519 520 buf1 = xmalloc (buf_size); 521 buf2 = xmalloc (buf_size); 522 523 do 524 { 525 read1 = block_read (fd1, buf1, buf_size); 526 if (read1 == (size_t)-1) 527 error (1, errno, "cannot read file %s for comparing", file1); 528 529 read2 = block_read (fd2, buf2, buf_size); 530 if (read2 == (size_t)-1) 531 error (1, errno, "cannot read file %s for comparing", file2); 532 533 /* assert (read1 == read2); */ 534 535 ret = memcmp(buf1, buf2, read1); 536 } while (ret == 0 && read1 == buf_size); 537 538 free (buf1); 539 free (buf2); 540 } 541 542 (void) close (fd1); 543 (void) close (fd2); 544 return (ret); 545 } 546 547 #ifdef LOSING_TMPNAM_FUNCTION 548 char *tmpnam(char *s) 549 { 550 static char value[L_tmpnam+1]; 551 552 if (s){ 553 strcpy(s,"/tmp/cvsXXXXXX"); 554 mktemp(s); 555 return s; 556 }else{ 557 strcpy(value,"/tmp/cvsXXXXXX"); 558 mktemp(s); 559 return value; 560 } 561 } 562 #endif 563 564 /* Return non-zero iff FILENAME is absolute. 565 Trivial under Unix, but more complicated under other systems. */ 566 int 567 isabsolute (filename) 568 const char *filename; 569 { 570 return filename[0] == '/'; 571 } 572 573 574 /* Return a pointer into PATH's last component. */ 575 char * 576 last_component (path) 577 char *path; 578 { 579 char *last = strrchr (path, '/'); 580 581 if (last) 582 return last + 1; 583 else 584 return path; 585 } 586