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