1 /* 2 * Copyright (c) 1999-2002 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * 5 * By using this file, you agree to the terms and conditions set 6 * forth in the LICENSE file which can be found at the top level of 7 * the sendmail distribution. 8 * 9 * Contributed by Exactis.com, Inc. 10 * 11 */ 12 13 /* 14 ** This is in transition. Changed from the original bf_torek.c code 15 ** to use sm_io function calls directly rather than through stdio 16 ** translation layer. Will be made a built-in file type of libsm 17 ** next (once safeopen() linkable from libsm). 18 */ 19 20 #include <sm/gen.h> 21 SM_RCSID("@(#)$Id: bf.c,v 8.51 2002/03/04 21:51:25 gshapiro Exp $") 22 23 #include <sys/types.h> 24 #include <sys/stat.h> 25 #include <sys/uio.h> 26 #include <fcntl.h> 27 #include <unistd.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <errno.h> 31 #include "sendmail.h" 32 #include "bf.h" 33 34 #include <syslog.h> 35 36 /* bf io functions */ 37 static ssize_t sm_bfread __P((SM_FILE_T *, char *, size_t)); 38 static ssize_t sm_bfwrite __P((SM_FILE_T *, const char *, size_t)); 39 static off_t sm_bfseek __P((SM_FILE_T *, off_t, int)); 40 static int sm_bfclose __P((SM_FILE_T *)); 41 42 static int sm_bfopen __P((SM_FILE_T *, const void *, int, const void *)); 43 static int sm_bfsetinfo __P((SM_FILE_T *, int , void *)); 44 static int sm_bfgetinfo __P((SM_FILE_T *, int , void *)); 45 46 /* 47 ** Data structure for storing information about each buffered file 48 ** (Originally in sendmail/bf_torek.h for the curious.) 49 */ 50 51 struct bf 52 { 53 bool bf_committed; /* Has this buffered file been committed? */ 54 bool bf_ondisk; /* On disk: committed or buffer overflow */ 55 long bf_flags; 56 int bf_disk_fd; /* If on disk, associated file descriptor */ 57 char *bf_buf; /* Memory buffer */ 58 int bf_bufsize; /* Length of above buffer */ 59 int bf_buffilled; /* Bytes of buffer actually filled */ 60 char *bf_filename; /* Name of buffered file, if ever committed */ 61 MODE_T bf_filemode; /* Mode of buffered file, if ever committed */ 62 off_t bf_offset; /* Currect file offset */ 63 int bf_size; /* Total current size of file */ 64 }; 65 66 #ifdef BF_STANDALONE 67 # define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode) 68 #else /* BF_STANDALONE */ 69 # define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff) 70 #endif /* BF_STANDALONE */ 71 72 struct bf_info 73 { 74 char *bi_filename; 75 MODE_T bi_fmode; 76 size_t bi_bsize; 77 long bi_flags; 78 }; 79 80 /* 81 ** SM_BFOPEN -- the "base" open function called by sm_io_open() for the 82 ** internal, file-type-specific info setup. 83 ** 84 ** Parameters: 85 ** fp -- file pointer being filled-in for file being open'd 86 ** info -- information about file being opened 87 ** flags -- ignored 88 ** rpool -- ignored (currently) 89 ** 90 ** Returns: 91 ** Failure: -1 and sets errno 92 ** Success: 0 (zero) 93 */ 94 95 static int 96 sm_bfopen(fp, info, flags, rpool) 97 SM_FILE_T *fp; 98 const void *info; 99 int flags; 100 const void *rpool; 101 { 102 char *filename; 103 MODE_T fmode; 104 size_t bsize; 105 long sflags; 106 struct bf *bfp; 107 int l; 108 struct stat st; 109 110 filename = ((struct bf_info *) info)->bi_filename; 111 fmode = ((struct bf_info *) info)->bi_fmode; 112 bsize = ((struct bf_info *) info)->bi_bsize; 113 sflags = ((struct bf_info *) info)->bi_flags; 114 115 /* Sanity checks */ 116 if (*filename == '\0') 117 { 118 /* Empty filename string */ 119 errno = ENOENT; 120 return -1; 121 } 122 if (stat(filename, &st) == 0) 123 { 124 /* File already exists on disk */ 125 errno = EEXIST; 126 return -1; 127 } 128 129 /* Allocate memory */ 130 bfp = (struct bf *) sm_malloc(sizeof(struct bf)); 131 if (bfp == NULL) 132 { 133 errno = ENOMEM; 134 return -1; 135 } 136 137 /* Assign data buffer */ 138 /* A zero bsize is valid, just don't allocate memory */ 139 if (bsize > 0) 140 { 141 bfp->bf_buf = (char *) sm_malloc(bsize); 142 if (bfp->bf_buf == NULL) 143 { 144 bfp->bf_bufsize = 0; 145 sm_free(bfp); 146 errno = ENOMEM; 147 return -1; 148 } 149 } 150 else 151 bfp->bf_buf = NULL; 152 153 /* Nearly home free, just set all the parameters now */ 154 bfp->bf_committed = false; 155 bfp->bf_ondisk = false; 156 bfp->bf_flags = sflags; 157 bfp->bf_bufsize = bsize; 158 bfp->bf_buffilled = 0; 159 l = strlen(filename) + 1; 160 bfp->bf_filename = (char *) sm_malloc(l); 161 if (bfp->bf_filename == NULL) 162 { 163 if (bfp->bf_buf != NULL) 164 sm_free(bfp->bf_buf); 165 sm_free(bfp); 166 errno = ENOMEM; 167 return -1; 168 } 169 (void) sm_strlcpy(bfp->bf_filename, filename, l); 170 bfp->bf_filemode = fmode; 171 bfp->bf_offset = 0; 172 bfp->bf_size = 0; 173 bfp->bf_disk_fd = -1; 174 fp->f_cookie = bfp; 175 176 if (tTd(58, 8)) 177 sm_dprintf("sm_bfopen(%s)\n", filename); 178 179 return 0; 180 } 181 182 /* 183 ** BFOPEN -- create a new buffered file 184 ** 185 ** Parameters: 186 ** filename -- the file's name 187 ** fmode -- what mode the file should be created as 188 ** bsize -- amount of buffer space to allocate (may be 0) 189 ** flags -- if running under sendmail, passed directly to safeopen 190 ** 191 ** Returns: 192 ** a SM_FILE_T * which may then be used with stdio functions, 193 ** or NULL on failure. SM_FILE_T * is opened for writing 194 ** "SM_IO_WHAT_VECTORS"). 195 ** 196 ** Side Effects: 197 ** none. 198 ** 199 ** Sets errno: 200 ** any value of errno specified by sm_io_setinfo_type() 201 ** any value of errno specified by sm_io_open() 202 ** any value of errno specified by sm_io_setinfo() 203 */ 204 205 SM_FILE_T * 206 bfopen(filename, fmode, bsize, flags) 207 char *filename; 208 MODE_T fmode; 209 size_t bsize; 210 long flags; 211 { 212 MODE_T omask; 213 SM_FILE_T SM_IO_SET_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose, 214 sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo, 215 SM_TIME_FOREVER); 216 struct bf_info info; 217 218 /* 219 ** Apply current umask to fmode as it may change by the time 220 ** the file is actually created. fmode becomes the true 221 ** permissions of the file, which OPEN() must obey. 222 */ 223 224 omask = umask(0); 225 fmode &= ~omask; 226 (void) umask(omask); 227 228 SM_IO_INIT_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose, 229 sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo, 230 SM_TIME_FOREVER); 231 info.bi_filename = filename; 232 info.bi_fmode = fmode; 233 info.bi_bsize = bsize; 234 info.bi_flags = flags; 235 236 return sm_io_open(&vector, SM_TIME_DEFAULT, &info, SM_IO_RDWR, NULL); 237 } 238 239 /* 240 ** SM_BFGETINFO -- returns info about an open file pointer 241 ** 242 ** Parameters: 243 ** fp -- file pointer to get info about 244 ** what -- type of info to obtain 245 ** valp -- thing to return the info in 246 */ 247 248 static int 249 sm_bfgetinfo(fp, what, valp) 250 SM_FILE_T *fp; 251 int what; 252 void *valp; 253 { 254 struct bf *bfp; 255 256 bfp = (struct bf *) fp->f_cookie; 257 switch (what) 258 { 259 case SM_IO_WHAT_FD: 260 return bfp->bf_disk_fd; 261 case SM_IO_WHAT_SIZE: 262 return bfp->bf_size; 263 default: 264 return -1; 265 } 266 } 267 268 /* 269 ** SM_BFCLOSE -- close a buffered file 270 ** 271 ** Parameters: 272 ** fp -- cookie of file to close 273 ** 274 ** Returns: 275 ** 0 to indicate success 276 ** 277 ** Side Effects: 278 ** deletes backing file, sm_frees memory. 279 ** 280 ** Sets errno: 281 ** never. 282 */ 283 284 static int 285 sm_bfclose(fp) 286 SM_FILE_T *fp; 287 { 288 struct bf *bfp; 289 290 /* Cast cookie back to correct type */ 291 bfp = (struct bf *) fp->f_cookie; 292 293 /* Need to clean up the file */ 294 if (bfp->bf_ondisk && !bfp->bf_committed) 295 unlink(bfp->bf_filename); 296 sm_free(bfp->bf_filename); 297 298 if (bfp->bf_disk_fd != -1) 299 close(bfp->bf_disk_fd); 300 301 /* Need to sm_free the buffer */ 302 if (bfp->bf_bufsize > 0) 303 sm_free(bfp->bf_buf); 304 305 /* Finally, sm_free the structure */ 306 sm_free(bfp); 307 return 0; 308 } 309 310 /* 311 ** SM_BFREAD -- read a buffered file 312 ** 313 ** Parameters: 314 ** cookie -- cookie of file to read 315 ** buf -- buffer to fill 316 ** nbytes -- how many bytes to read 317 ** 318 ** Returns: 319 ** number of bytes read or -1 indicate failure 320 ** 321 ** Side Effects: 322 ** none. 323 ** 324 */ 325 326 static ssize_t 327 sm_bfread(fp, buf, nbytes) 328 SM_FILE_T *fp; 329 char *buf; 330 size_t nbytes; 331 { 332 struct bf *bfp; 333 ssize_t count = 0; /* Number of bytes put in buf so far */ 334 int retval; 335 336 /* Cast cookie back to correct type */ 337 bfp = (struct bf *) fp->f_cookie; 338 339 if (bfp->bf_offset < bfp->bf_buffilled) 340 { 341 /* Need to grab some from buffer */ 342 count = nbytes; 343 if ((bfp->bf_offset + count) > bfp->bf_buffilled) 344 count = bfp->bf_buffilled - bfp->bf_offset; 345 346 memcpy(buf, bfp->bf_buf + bfp->bf_offset, count); 347 } 348 349 if ((bfp->bf_offset + nbytes) > bfp->bf_buffilled) 350 { 351 /* Need to grab some from file */ 352 if (!bfp->bf_ondisk) 353 { 354 /* Oops, the file doesn't exist. EOF. */ 355 if (tTd(58, 8)) 356 sm_dprintf("sm_bfread(%s): to disk\n", 357 bfp->bf_filename); 358 goto finished; 359 } 360 361 /* Catch a read() on an earlier failed write to disk */ 362 if (bfp->bf_disk_fd < 0) 363 { 364 errno = EIO; 365 return -1; 366 } 367 368 if (lseek(bfp->bf_disk_fd, 369 bfp->bf_offset + count, SEEK_SET) < 0) 370 { 371 if ((errno == EINVAL) || (errno == ESPIPE)) 372 { 373 /* 374 ** stdio won't be expecting these 375 ** errnos from read()! Change them 376 ** into something it can understand. 377 */ 378 379 errno = EIO; 380 } 381 return -1; 382 } 383 384 while (count < nbytes) 385 { 386 retval = read(bfp->bf_disk_fd, 387 buf + count, 388 nbytes - count); 389 if (retval < 0) 390 { 391 /* errno is set implicitly by read() */ 392 return -1; 393 } 394 else if (retval == 0) 395 goto finished; 396 else 397 count += retval; 398 } 399 } 400 401 finished: 402 bfp->bf_offset += count; 403 return count; 404 } 405 406 /* 407 ** SM_BFSEEK -- seek to a position in a buffered file 408 ** 409 ** Parameters: 410 ** fp -- fp of file to seek 411 ** offset -- position to seek to 412 ** whence -- how to seek 413 ** 414 ** Returns: 415 ** new file offset or -1 indicate failure 416 ** 417 ** Side Effects: 418 ** none. 419 ** 420 */ 421 422 static off_t 423 sm_bfseek(fp, offset, whence) 424 SM_FILE_T *fp; 425 off_t offset; 426 int whence; 427 428 { 429 struct bf *bfp; 430 431 /* Cast cookie back to correct type */ 432 bfp = (struct bf *) fp->f_cookie; 433 434 switch (whence) 435 { 436 case SEEK_SET: 437 bfp->bf_offset = offset; 438 break; 439 440 case SEEK_CUR: 441 bfp->bf_offset += offset; 442 break; 443 444 case SEEK_END: 445 bfp->bf_offset = bfp->bf_size + offset; 446 break; 447 448 default: 449 errno = EINVAL; 450 return -1; 451 } 452 return bfp->bf_offset; 453 } 454 455 /* 456 ** SM_BFWRITE -- write to a buffered file 457 ** 458 ** Parameters: 459 ** fp -- fp of file to write 460 ** buf -- data buffer 461 ** nbytes -- how many bytes to write 462 ** 463 ** Returns: 464 ** number of bytes written or -1 indicate failure 465 ** 466 ** Side Effects: 467 ** may create backing file if over memory limit for file. 468 ** 469 */ 470 471 static ssize_t 472 sm_bfwrite(fp, buf, nbytes) 473 SM_FILE_T *fp; 474 const char *buf; 475 size_t nbytes; 476 { 477 struct bf *bfp; 478 ssize_t count = 0; /* Number of bytes written so far */ 479 int retval; 480 481 /* Cast cookie back to correct type */ 482 bfp = (struct bf *) fp->f_cookie; 483 484 /* If committed, go straight to disk */ 485 if (bfp->bf_committed) 486 { 487 if (lseek(bfp->bf_disk_fd, bfp->bf_offset, SEEK_SET) < 0) 488 { 489 if ((errno == EINVAL) || (errno == ESPIPE)) 490 { 491 /* 492 ** stdio won't be expecting these 493 ** errnos from write()! Change them 494 ** into something it can understand. 495 */ 496 497 errno = EIO; 498 } 499 return -1; 500 } 501 502 count = write(bfp->bf_disk_fd, buf, nbytes); 503 if (count < 0) 504 { 505 /* errno is set implicitly by write() */ 506 return -1; 507 } 508 goto finished; 509 } 510 511 if (bfp->bf_offset < bfp->bf_bufsize) 512 { 513 /* Need to put some in buffer */ 514 count = nbytes; 515 if ((bfp->bf_offset + count) > bfp->bf_bufsize) 516 count = bfp->bf_bufsize - bfp->bf_offset; 517 518 memcpy(bfp->bf_buf + bfp->bf_offset, buf, count); 519 if ((bfp->bf_offset + count) > bfp->bf_buffilled) 520 bfp->bf_buffilled = bfp->bf_offset + count; 521 } 522 523 if ((bfp->bf_offset + nbytes) > bfp->bf_bufsize) 524 { 525 /* Need to put some in file */ 526 if (!bfp->bf_ondisk) 527 { 528 MODE_T omask; 529 530 /* Clear umask as bf_filemode are the true perms */ 531 omask = umask(0); 532 retval = OPEN(bfp->bf_filename, 533 O_RDWR | O_CREAT | O_TRUNC, 534 bfp->bf_filemode, bfp->bf_flags); 535 (void) umask(omask); 536 537 /* Couldn't create file: failure */ 538 if (retval < 0) 539 { 540 /* 541 ** stdio may not be expecting these 542 ** errnos from write()! Change to 543 ** something which it can understand. 544 ** Note that ENOSPC and EDQUOT are saved 545 ** because they are actually valid for 546 ** write(). 547 */ 548 549 if (!(errno == ENOSPC 550 #ifdef EDQUOT 551 || errno == EDQUOT 552 #endif /* EDQUOT */ 553 )) 554 errno = EIO; 555 556 return -1; 557 } 558 bfp->bf_disk_fd = retval; 559 bfp->bf_ondisk = true; 560 } 561 562 /* Catch a write() on an earlier failed write to disk */ 563 if (bfp->bf_ondisk && bfp->bf_disk_fd < 0) 564 { 565 errno = EIO; 566 return -1; 567 } 568 569 if (lseek(bfp->bf_disk_fd, 570 bfp->bf_offset + count, SEEK_SET) < 0) 571 { 572 if ((errno == EINVAL) || (errno == ESPIPE)) 573 { 574 /* 575 ** stdio won't be expecting these 576 ** errnos from write()! Change them into 577 ** something which it can understand. 578 */ 579 580 errno = EIO; 581 } 582 return -1; 583 } 584 585 while (count < nbytes) 586 { 587 retval = write(bfp->bf_disk_fd, buf + count, 588 nbytes - count); 589 if (retval < 0) 590 { 591 /* errno is set implicitly by write() */ 592 return -1; 593 } 594 else 595 count += retval; 596 } 597 } 598 599 finished: 600 bfp->bf_offset += count; 601 if (bfp->bf_offset > bfp->bf_size) 602 bfp->bf_size = bfp->bf_offset; 603 return count; 604 } 605 606 /* 607 ** BFREWIND -- rewinds the SM_FILE_T * 608 ** 609 ** Parameters: 610 ** fp -- SM_FILE_T * to rewind 611 ** 612 ** Returns: 613 ** 0 on success, -1 on error 614 ** 615 ** Side Effects: 616 ** rewinds the SM_FILE_T * and puts it into read mode. Normally one 617 ** would bfopen() a file, write to it, then bfrewind() and 618 ** fread(). If fp is not a buffered file, this is equivalent to 619 ** rewind(). 620 ** 621 ** Sets errno: 622 ** any value of errno specified by sm_io_rewind() 623 */ 624 625 int 626 bfrewind(fp) 627 SM_FILE_T *fp; 628 { 629 (void) sm_io_flush(fp, SM_TIME_DEFAULT); 630 sm_io_clearerr(fp); /* quicker just to do it */ 631 return sm_io_seek(fp, SM_TIME_DEFAULT, 0, SM_IO_SEEK_SET); 632 } 633 634 /* 635 ** SM_BFCOMMIT -- "commits" the buffered file 636 ** 637 ** Parameters: 638 ** fp -- SM_FILE_T * to commit to disk 639 ** 640 ** Returns: 641 ** 0 on success, -1 on error 642 ** 643 ** Side Effects: 644 ** Forces the given SM_FILE_T * to be written to disk if it is not 645 ** already, and ensures that it will be kept after closing. If 646 ** fp is not a buffered file, this is a no-op. 647 ** 648 ** Sets errno: 649 ** any value of errno specified by open() 650 ** any value of errno specified by write() 651 ** any value of errno specified by lseek() 652 */ 653 654 static int 655 sm_bfcommit(fp) 656 SM_FILE_T *fp; 657 { 658 struct bf *bfp; 659 int retval; 660 int byteswritten; 661 662 /* Get associated bf structure */ 663 bfp = (struct bf *) fp->f_cookie; 664 665 /* If already committed, noop */ 666 if (bfp->bf_committed) 667 return 0; 668 669 /* Do we need to open a file? */ 670 if (!bfp->bf_ondisk) 671 { 672 MODE_T omask; 673 struct stat st; 674 675 if (tTd(58, 8)) 676 { 677 sm_dprintf("bfcommit(%s): to disk\n", bfp->bf_filename); 678 if (tTd(58, 32)) 679 sm_dprintf("bfcommit(): filemode %o flags %ld\n", 680 bfp->bf_filemode, bfp->bf_flags); 681 } 682 683 if (stat(bfp->bf_filename, &st) == 0) 684 { 685 errno = EEXIST; 686 return -1; 687 } 688 689 /* Clear umask as bf_filemode are the true perms */ 690 omask = umask(0); 691 retval = OPEN(bfp->bf_filename, O_RDWR | O_CREAT | O_TRUNC, 692 bfp->bf_filemode, bfp->bf_flags); 693 (void) umask(omask); 694 695 /* Couldn't create file: failure */ 696 if (retval < 0) 697 { 698 /* errno is set implicitly by open() */ 699 return -1; 700 } 701 702 bfp->bf_disk_fd = retval; 703 bfp->bf_ondisk = true; 704 } 705 706 /* Write out the contents of our buffer, if we have any */ 707 if (bfp->bf_buffilled > 0) 708 { 709 byteswritten = 0; 710 711 if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0) 712 { 713 /* errno is set implicitly by lseek() */ 714 return -1; 715 } 716 717 while (byteswritten < bfp->bf_buffilled) 718 { 719 retval = write(bfp->bf_disk_fd, 720 bfp->bf_buf + byteswritten, 721 bfp->bf_buffilled - byteswritten); 722 if (retval < 0) 723 { 724 /* errno is set implicitly by write() */ 725 return -1; 726 } 727 else 728 byteswritten += retval; 729 } 730 } 731 bfp->bf_committed = true; 732 733 /* Invalidate buf; all goes to file now */ 734 bfp->bf_buffilled = 0; 735 if (bfp->bf_bufsize > 0) 736 { 737 /* Don't need buffer anymore; free it */ 738 bfp->bf_bufsize = 0; 739 sm_free(bfp->bf_buf); 740 } 741 return 0; 742 } 743 744 /* 745 ** SM_BFTRUNCATE -- rewinds and truncates the SM_FILE_T * 746 ** 747 ** Parameters: 748 ** fp -- SM_FILE_T * to truncate 749 ** 750 ** Returns: 751 ** 0 on success, -1 on error 752 ** 753 ** Side Effects: 754 ** rewinds the SM_FILE_T *, truncates it to zero length, and puts 755 ** it into write mode. 756 ** 757 ** Sets errno: 758 ** any value of errno specified by fseek() 759 ** any value of errno specified by ftruncate() 760 */ 761 762 static int 763 sm_bftruncate(fp) 764 SM_FILE_T *fp; 765 { 766 struct bf *bfp; 767 768 if (bfrewind(fp) < 0) 769 return -1; 770 771 /* Get bf structure */ 772 bfp = (struct bf *) fp->f_cookie; 773 bfp->bf_buffilled = 0; 774 bfp->bf_size = 0; 775 776 /* Need to zero the buffer */ 777 if (bfp->bf_bufsize > 0) 778 memset(bfp->bf_buf, '\0', bfp->bf_bufsize); 779 if (bfp->bf_ondisk) 780 { 781 #if NOFTRUNCATE 782 /* XXX: Not much we can do except rewind it */ 783 errno = EINVAL; 784 return -1; 785 #else /* NOFTRUNCATE */ 786 return ftruncate(bfp->bf_disk_fd, 0); 787 #endif /* NOFTRUNCATE */ 788 } 789 else 790 return 0; 791 } 792 793 /* 794 ** SM_BFSETINFO -- set/change info for an open file pointer 795 ** 796 ** Parameters: 797 ** fp -- file pointer to get info about 798 ** what -- type of info to set/change 799 ** valp -- thing to set/change the info to 800 ** 801 */ 802 803 static int 804 sm_bfsetinfo(fp, what, valp) 805 SM_FILE_T *fp; 806 int what; 807 void *valp; 808 { 809 struct bf *bfp; 810 int bsize; 811 812 /* Get bf structure */ 813 bfp = (struct bf *) fp->f_cookie; 814 switch (what) 815 { 816 case SM_BF_SETBUFSIZE: 817 bsize = *((int *) valp); 818 bfp->bf_bufsize = bsize; 819 820 /* A zero bsize is valid, just don't allocate memory */ 821 if (bsize > 0) 822 { 823 bfp->bf_buf = (char *) sm_malloc(bsize); 824 if (bfp->bf_buf == NULL) 825 { 826 bfp->bf_bufsize = 0; 827 errno = ENOMEM; 828 return -1; 829 } 830 } 831 else 832 bfp->bf_buf = NULL; 833 return 0; 834 case SM_BF_COMMIT: 835 return sm_bfcommit(fp); 836 case SM_BF_TRUNCATE: 837 return sm_bftruncate(fp); 838 case SM_BF_TEST: 839 return 1; /* always */ 840 default: 841 errno = EINVAL; 842 return -1; 843 } 844 } 845