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