190792Sgshapiro/* 2261370Sgshapiro * Copyright (c) 1999-2002, 2004, 2006 Proofpoint, Inc. and its suppliers. 390792Sgshapiro * All rights reserved. 490792Sgshapiro * 590792Sgshapiro * By using this file, you agree to the terms and conditions set 690792Sgshapiro * forth in the LICENSE file which can be found at the top level of 790792Sgshapiro * the sendmail distribution. 890792Sgshapiro * 990792Sgshapiro * Contributed by Exactis.com, Inc. 1090792Sgshapiro * 1190792Sgshapiro */ 1290792Sgshapiro 1390792Sgshapiro/* 1490792Sgshapiro** This is in transition. Changed from the original bf_torek.c code 1590792Sgshapiro** to use sm_io function calls directly rather than through stdio 1690792Sgshapiro** translation layer. Will be made a built-in file type of libsm 1790792Sgshapiro** next (once safeopen() linkable from libsm). 1890792Sgshapiro*/ 1990792Sgshapiro 2090792Sgshapiro#include <sm/gen.h> 21266711SgshapiroSM_RCSID("@(#)$Id: bf.c,v 8.63 2013-11-22 20:51:55 ca Exp $") 2290792Sgshapiro 2390792Sgshapiro#include <sys/types.h> 2490792Sgshapiro#include <sys/stat.h> 2590792Sgshapiro#include <sys/uio.h> 2690792Sgshapiro#include <fcntl.h> 2790792Sgshapiro#include <unistd.h> 2890792Sgshapiro#include <stdlib.h> 2990792Sgshapiro#include <string.h> 3090792Sgshapiro#include <errno.h> 3190792Sgshapiro#include "sendmail.h" 3290792Sgshapiro#include "bf.h" 3390792Sgshapiro 3490792Sgshapiro#include <syslog.h> 3590792Sgshapiro 3690792Sgshapiro/* bf io functions */ 3790792Sgshapirostatic ssize_t sm_bfread __P((SM_FILE_T *, char *, size_t)); 3890792Sgshapirostatic ssize_t sm_bfwrite __P((SM_FILE_T *, const char *, size_t)); 3990792Sgshapirostatic off_t sm_bfseek __P((SM_FILE_T *, off_t, int)); 4090792Sgshapirostatic int sm_bfclose __P((SM_FILE_T *)); 41141858Sgshapirostatic int sm_bfcommit __P((SM_FILE_T *)); 42141858Sgshapirostatic int sm_bftruncate __P((SM_FILE_T *)); 4390792Sgshapiro 4490792Sgshapirostatic int sm_bfopen __P((SM_FILE_T *, const void *, int, const void *)); 4590792Sgshapirostatic int sm_bfsetinfo __P((SM_FILE_T *, int , void *)); 4690792Sgshapirostatic int sm_bfgetinfo __P((SM_FILE_T *, int , void *)); 4790792Sgshapiro 4890792Sgshapiro/* 4990792Sgshapiro** Data structure for storing information about each buffered file 5090792Sgshapiro** (Originally in sendmail/bf_torek.h for the curious.) 5190792Sgshapiro*/ 5290792Sgshapiro 5390792Sgshapirostruct bf 5490792Sgshapiro{ 5590792Sgshapiro bool bf_committed; /* Has this buffered file been committed? */ 5690792Sgshapiro bool bf_ondisk; /* On disk: committed or buffer overflow */ 5790792Sgshapiro long bf_flags; 5890792Sgshapiro int bf_disk_fd; /* If on disk, associated file descriptor */ 5990792Sgshapiro char *bf_buf; /* Memory buffer */ 6090792Sgshapiro int bf_bufsize; /* Length of above buffer */ 6190792Sgshapiro int bf_buffilled; /* Bytes of buffer actually filled */ 6290792Sgshapiro char *bf_filename; /* Name of buffered file, if ever committed */ 6390792Sgshapiro MODE_T bf_filemode; /* Mode of buffered file, if ever committed */ 6490792Sgshapiro off_t bf_offset; /* Currect file offset */ 6590792Sgshapiro int bf_size; /* Total current size of file */ 6690792Sgshapiro}; 6790792Sgshapiro 6890792Sgshapiro#ifdef BF_STANDALONE 6990792Sgshapiro# define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode) 7090792Sgshapiro#else /* BF_STANDALONE */ 7190792Sgshapiro# define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff) 7290792Sgshapiro#endif /* BF_STANDALONE */ 7390792Sgshapiro 7490792Sgshapirostruct bf_info 7590792Sgshapiro{ 7690792Sgshapiro char *bi_filename; 7790792Sgshapiro MODE_T bi_fmode; 7890792Sgshapiro size_t bi_bsize; 7990792Sgshapiro long bi_flags; 8090792Sgshapiro}; 8190792Sgshapiro 8290792Sgshapiro/* 8390792Sgshapiro** SM_BFOPEN -- the "base" open function called by sm_io_open() for the 8490792Sgshapiro** internal, file-type-specific info setup. 8590792Sgshapiro** 8690792Sgshapiro** Parameters: 8790792Sgshapiro** fp -- file pointer being filled-in for file being open'd 8894334Sgshapiro** info -- information about file being opened 8990792Sgshapiro** flags -- ignored 9090792Sgshapiro** rpool -- ignored (currently) 9190792Sgshapiro** 9290792Sgshapiro** Returns: 9390792Sgshapiro** Failure: -1 and sets errno 9490792Sgshapiro** Success: 0 (zero) 9590792Sgshapiro*/ 9690792Sgshapiro 9790792Sgshapirostatic int 9890792Sgshapirosm_bfopen(fp, info, flags, rpool) 9990792Sgshapiro SM_FILE_T *fp; 10090792Sgshapiro const void *info; 10190792Sgshapiro int flags; 10290792Sgshapiro const void *rpool; 10390792Sgshapiro{ 10490792Sgshapiro char *filename; 10590792Sgshapiro MODE_T fmode; 10690792Sgshapiro size_t bsize; 10790792Sgshapiro long sflags; 10890792Sgshapiro struct bf *bfp; 10990792Sgshapiro int l; 11090792Sgshapiro struct stat st; 11190792Sgshapiro 11290792Sgshapiro filename = ((struct bf_info *) info)->bi_filename; 11390792Sgshapiro fmode = ((struct bf_info *) info)->bi_fmode; 11490792Sgshapiro bsize = ((struct bf_info *) info)->bi_bsize; 11590792Sgshapiro sflags = ((struct bf_info *) info)->bi_flags; 11690792Sgshapiro 11790792Sgshapiro /* Sanity checks */ 11890792Sgshapiro if (*filename == '\0') 11990792Sgshapiro { 12090792Sgshapiro /* Empty filename string */ 12190792Sgshapiro errno = ENOENT; 12290792Sgshapiro return -1; 12390792Sgshapiro } 12490792Sgshapiro if (stat(filename, &st) == 0) 12590792Sgshapiro { 12690792Sgshapiro /* File already exists on disk */ 12790792Sgshapiro errno = EEXIST; 12890792Sgshapiro return -1; 12990792Sgshapiro } 13090792Sgshapiro 13190792Sgshapiro /* Allocate memory */ 13290792Sgshapiro bfp = (struct bf *) sm_malloc(sizeof(struct bf)); 13390792Sgshapiro if (bfp == NULL) 13490792Sgshapiro { 13590792Sgshapiro errno = ENOMEM; 13690792Sgshapiro return -1; 13790792Sgshapiro } 13890792Sgshapiro 13990792Sgshapiro /* Assign data buffer */ 14090792Sgshapiro /* A zero bsize is valid, just don't allocate memory */ 14190792Sgshapiro if (bsize > 0) 14290792Sgshapiro { 14390792Sgshapiro bfp->bf_buf = (char *) sm_malloc(bsize); 14490792Sgshapiro if (bfp->bf_buf == NULL) 14590792Sgshapiro { 14690792Sgshapiro bfp->bf_bufsize = 0; 14790792Sgshapiro sm_free(bfp); 14890792Sgshapiro errno = ENOMEM; 14990792Sgshapiro return -1; 15090792Sgshapiro } 15190792Sgshapiro } 15290792Sgshapiro else 15390792Sgshapiro bfp->bf_buf = NULL; 15490792Sgshapiro 15590792Sgshapiro /* Nearly home free, just set all the parameters now */ 15690792Sgshapiro bfp->bf_committed = false; 15790792Sgshapiro bfp->bf_ondisk = false; 15890792Sgshapiro bfp->bf_flags = sflags; 15990792Sgshapiro bfp->bf_bufsize = bsize; 16090792Sgshapiro bfp->bf_buffilled = 0; 16190792Sgshapiro l = strlen(filename) + 1; 16290792Sgshapiro bfp->bf_filename = (char *) sm_malloc(l); 16390792Sgshapiro if (bfp->bf_filename == NULL) 16490792Sgshapiro { 16590792Sgshapiro if (bfp->bf_buf != NULL) 16690792Sgshapiro sm_free(bfp->bf_buf); 16790792Sgshapiro sm_free(bfp); 16890792Sgshapiro errno = ENOMEM; 16990792Sgshapiro return -1; 17090792Sgshapiro } 17190792Sgshapiro (void) sm_strlcpy(bfp->bf_filename, filename, l); 17290792Sgshapiro bfp->bf_filemode = fmode; 17390792Sgshapiro bfp->bf_offset = 0; 17494334Sgshapiro bfp->bf_size = 0; 17590792Sgshapiro bfp->bf_disk_fd = -1; 17690792Sgshapiro fp->f_cookie = bfp; 17790792Sgshapiro 17890792Sgshapiro if (tTd(58, 8)) 17990792Sgshapiro sm_dprintf("sm_bfopen(%s)\n", filename); 18090792Sgshapiro 18190792Sgshapiro return 0; 18290792Sgshapiro} 18390792Sgshapiro 18490792Sgshapiro/* 18590792Sgshapiro** BFOPEN -- create a new buffered file 18690792Sgshapiro** 18790792Sgshapiro** Parameters: 18890792Sgshapiro** filename -- the file's name 18990792Sgshapiro** fmode -- what mode the file should be created as 19090792Sgshapiro** bsize -- amount of buffer space to allocate (may be 0) 19190792Sgshapiro** flags -- if running under sendmail, passed directly to safeopen 19290792Sgshapiro** 19390792Sgshapiro** Returns: 19490792Sgshapiro** a SM_FILE_T * which may then be used with stdio functions, 19590792Sgshapiro** or NULL on failure. SM_FILE_T * is opened for writing 19690792Sgshapiro** "SM_IO_WHAT_VECTORS"). 19790792Sgshapiro** 19890792Sgshapiro** Side Effects: 19990792Sgshapiro** none. 20090792Sgshapiro** 20190792Sgshapiro** Sets errno: 20290792Sgshapiro** any value of errno specified by sm_io_setinfo_type() 20390792Sgshapiro** any value of errno specified by sm_io_open() 20490792Sgshapiro** any value of errno specified by sm_io_setinfo() 20590792Sgshapiro*/ 20690792Sgshapiro 20798121Sgshapiro#ifdef __STDC__ 20898121Sgshapiro/* 20998121Sgshapiro** XXX This is a temporary hack since MODE_T on HP-UX 10.x is short. 21098121Sgshapiro** If we use K&R here, the compiler will complain about 21198121Sgshapiro** Inconsistent parameter list declaration 21298121Sgshapiro** due to the change from short to int. 21398121Sgshapiro*/ 21498121Sgshapiro 21590792SgshapiroSM_FILE_T * 21698121Sgshapirobfopen(char *filename, MODE_T fmode, size_t bsize, long flags) 21798121Sgshapiro#else /* __STDC__ */ 21898121SgshapiroSM_FILE_T * 21990792Sgshapirobfopen(filename, fmode, bsize, flags) 22090792Sgshapiro char *filename; 22190792Sgshapiro MODE_T fmode; 22290792Sgshapiro size_t bsize; 22390792Sgshapiro long flags; 22498121Sgshapiro#endif /* __STDC__ */ 22590792Sgshapiro{ 22690792Sgshapiro MODE_T omask; 22790792Sgshapiro SM_FILE_T SM_IO_SET_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose, 22890792Sgshapiro sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo, 22990792Sgshapiro SM_TIME_FOREVER); 23090792Sgshapiro struct bf_info info; 23190792Sgshapiro 23290792Sgshapiro /* 23390792Sgshapiro ** Apply current umask to fmode as it may change by the time 23490792Sgshapiro ** the file is actually created. fmode becomes the true 23590792Sgshapiro ** permissions of the file, which OPEN() must obey. 23690792Sgshapiro */ 23790792Sgshapiro 23890792Sgshapiro omask = umask(0); 23990792Sgshapiro fmode &= ~omask; 24090792Sgshapiro (void) umask(omask); 24190792Sgshapiro 24290792Sgshapiro SM_IO_INIT_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose, 24390792Sgshapiro sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo, 24490792Sgshapiro SM_TIME_FOREVER); 24590792Sgshapiro info.bi_filename = filename; 24690792Sgshapiro info.bi_fmode = fmode; 24790792Sgshapiro info.bi_bsize = bsize; 24890792Sgshapiro info.bi_flags = flags; 24990792Sgshapiro 25090792Sgshapiro return sm_io_open(&vector, SM_TIME_DEFAULT, &info, SM_IO_RDWR, NULL); 25190792Sgshapiro} 25290792Sgshapiro 25390792Sgshapiro/* 25490792Sgshapiro** SM_BFGETINFO -- returns info about an open file pointer 25590792Sgshapiro** 25690792Sgshapiro** Parameters: 25790792Sgshapiro** fp -- file pointer to get info about 25890792Sgshapiro** what -- type of info to obtain 25990792Sgshapiro** valp -- thing to return the info in 26090792Sgshapiro*/ 26190792Sgshapiro 26290792Sgshapirostatic int 26390792Sgshapirosm_bfgetinfo(fp, what, valp) 26490792Sgshapiro SM_FILE_T *fp; 26590792Sgshapiro int what; 26690792Sgshapiro void *valp; 26790792Sgshapiro{ 26890792Sgshapiro struct bf *bfp; 26990792Sgshapiro 27090792Sgshapiro bfp = (struct bf *) fp->f_cookie; 27190792Sgshapiro switch (what) 27290792Sgshapiro { 27390792Sgshapiro case SM_IO_WHAT_FD: 27490792Sgshapiro return bfp->bf_disk_fd; 27594334Sgshapiro case SM_IO_WHAT_SIZE: 27694334Sgshapiro return bfp->bf_size; 27790792Sgshapiro default: 27890792Sgshapiro return -1; 27990792Sgshapiro } 28090792Sgshapiro} 28190792Sgshapiro 28290792Sgshapiro/* 28390792Sgshapiro** SM_BFCLOSE -- close a buffered file 28490792Sgshapiro** 28590792Sgshapiro** Parameters: 28690792Sgshapiro** fp -- cookie of file to close 28790792Sgshapiro** 28890792Sgshapiro** Returns: 28990792Sgshapiro** 0 to indicate success 29090792Sgshapiro** 29190792Sgshapiro** Side Effects: 29290792Sgshapiro** deletes backing file, sm_frees memory. 29390792Sgshapiro** 29490792Sgshapiro** Sets errno: 29590792Sgshapiro** never. 29690792Sgshapiro*/ 29790792Sgshapiro 29890792Sgshapirostatic int 29990792Sgshapirosm_bfclose(fp) 30090792Sgshapiro SM_FILE_T *fp; 30190792Sgshapiro{ 30290792Sgshapiro struct bf *bfp; 30390792Sgshapiro 30490792Sgshapiro /* Cast cookie back to correct type */ 30590792Sgshapiro bfp = (struct bf *) fp->f_cookie; 30690792Sgshapiro 30790792Sgshapiro /* Need to clean up the file */ 30890792Sgshapiro if (bfp->bf_ondisk && !bfp->bf_committed) 30990792Sgshapiro unlink(bfp->bf_filename); 31090792Sgshapiro sm_free(bfp->bf_filename); 31190792Sgshapiro 31290792Sgshapiro if (bfp->bf_disk_fd != -1) 31390792Sgshapiro close(bfp->bf_disk_fd); 31490792Sgshapiro 31590792Sgshapiro /* Need to sm_free the buffer */ 31690792Sgshapiro if (bfp->bf_bufsize > 0) 31790792Sgshapiro sm_free(bfp->bf_buf); 31890792Sgshapiro 31990792Sgshapiro /* Finally, sm_free the structure */ 32090792Sgshapiro sm_free(bfp); 32190792Sgshapiro return 0; 32290792Sgshapiro} 32390792Sgshapiro 32490792Sgshapiro/* 32590792Sgshapiro** SM_BFREAD -- read a buffered file 32690792Sgshapiro** 32790792Sgshapiro** Parameters: 32890792Sgshapiro** cookie -- cookie of file to read 32990792Sgshapiro** buf -- buffer to fill 33090792Sgshapiro** nbytes -- how many bytes to read 33190792Sgshapiro** 33290792Sgshapiro** Returns: 33390792Sgshapiro** number of bytes read or -1 indicate failure 33490792Sgshapiro** 33590792Sgshapiro** Side Effects: 33690792Sgshapiro** none. 33790792Sgshapiro** 33890792Sgshapiro*/ 33990792Sgshapiro 34090792Sgshapirostatic ssize_t 34190792Sgshapirosm_bfread(fp, buf, nbytes) 34290792Sgshapiro SM_FILE_T *fp; 34390792Sgshapiro char *buf; 34490792Sgshapiro size_t nbytes; 34590792Sgshapiro{ 34690792Sgshapiro struct bf *bfp; 34790792Sgshapiro ssize_t count = 0; /* Number of bytes put in buf so far */ 34890792Sgshapiro int retval; 34990792Sgshapiro 35090792Sgshapiro /* Cast cookie back to correct type */ 35190792Sgshapiro bfp = (struct bf *) fp->f_cookie; 35290792Sgshapiro 35390792Sgshapiro if (bfp->bf_offset < bfp->bf_buffilled) 35490792Sgshapiro { 35590792Sgshapiro /* Need to grab some from buffer */ 35690792Sgshapiro count = nbytes; 35790792Sgshapiro if ((bfp->bf_offset + count) > bfp->bf_buffilled) 35890792Sgshapiro count = bfp->bf_buffilled - bfp->bf_offset; 35990792Sgshapiro 36090792Sgshapiro memcpy(buf, bfp->bf_buf + bfp->bf_offset, count); 36190792Sgshapiro } 36290792Sgshapiro 36390792Sgshapiro if ((bfp->bf_offset + nbytes) > bfp->bf_buffilled) 36490792Sgshapiro { 36590792Sgshapiro /* Need to grab some from file */ 36690792Sgshapiro if (!bfp->bf_ondisk) 36790792Sgshapiro { 36890792Sgshapiro /* Oops, the file doesn't exist. EOF. */ 36990792Sgshapiro if (tTd(58, 8)) 37090792Sgshapiro sm_dprintf("sm_bfread(%s): to disk\n", 37190792Sgshapiro bfp->bf_filename); 37290792Sgshapiro goto finished; 37390792Sgshapiro } 37490792Sgshapiro 37590792Sgshapiro /* Catch a read() on an earlier failed write to disk */ 37690792Sgshapiro if (bfp->bf_disk_fd < 0) 37790792Sgshapiro { 37890792Sgshapiro errno = EIO; 37990792Sgshapiro return -1; 38090792Sgshapiro } 38190792Sgshapiro 38290792Sgshapiro if (lseek(bfp->bf_disk_fd, 38390792Sgshapiro bfp->bf_offset + count, SEEK_SET) < 0) 38490792Sgshapiro { 38590792Sgshapiro if ((errno == EINVAL) || (errno == ESPIPE)) 38690792Sgshapiro { 38790792Sgshapiro /* 38890792Sgshapiro ** stdio won't be expecting these 38990792Sgshapiro ** errnos from read()! Change them 39090792Sgshapiro ** into something it can understand. 39190792Sgshapiro */ 39290792Sgshapiro 39390792Sgshapiro errno = EIO; 39490792Sgshapiro } 39590792Sgshapiro return -1; 39690792Sgshapiro } 39790792Sgshapiro 39890792Sgshapiro while (count < nbytes) 39990792Sgshapiro { 40090792Sgshapiro retval = read(bfp->bf_disk_fd, 40190792Sgshapiro buf + count, 40290792Sgshapiro nbytes - count); 40390792Sgshapiro if (retval < 0) 40490792Sgshapiro { 40590792Sgshapiro /* errno is set implicitly by read() */ 40690792Sgshapiro return -1; 40790792Sgshapiro } 40890792Sgshapiro else if (retval == 0) 40990792Sgshapiro goto finished; 41090792Sgshapiro else 41190792Sgshapiro count += retval; 41290792Sgshapiro } 41390792Sgshapiro } 41490792Sgshapiro 41590792Sgshapirofinished: 41690792Sgshapiro bfp->bf_offset += count; 41790792Sgshapiro return count; 41890792Sgshapiro} 41990792Sgshapiro 42090792Sgshapiro/* 42190792Sgshapiro** SM_BFSEEK -- seek to a position in a buffered file 42290792Sgshapiro** 42390792Sgshapiro** Parameters: 42490792Sgshapiro** fp -- fp of file to seek 42590792Sgshapiro** offset -- position to seek to 42690792Sgshapiro** whence -- how to seek 42790792Sgshapiro** 42890792Sgshapiro** Returns: 42990792Sgshapiro** new file offset or -1 indicate failure 43090792Sgshapiro** 43190792Sgshapiro** Side Effects: 43290792Sgshapiro** none. 43390792Sgshapiro** 43490792Sgshapiro*/ 43590792Sgshapiro 43690792Sgshapirostatic off_t 43790792Sgshapirosm_bfseek(fp, offset, whence) 43890792Sgshapiro SM_FILE_T *fp; 43990792Sgshapiro off_t offset; 44090792Sgshapiro int whence; 44190792Sgshapiro 44290792Sgshapiro{ 44390792Sgshapiro struct bf *bfp; 44490792Sgshapiro 44590792Sgshapiro /* Cast cookie back to correct type */ 44690792Sgshapiro bfp = (struct bf *) fp->f_cookie; 44790792Sgshapiro 44890792Sgshapiro switch (whence) 44990792Sgshapiro { 45090792Sgshapiro case SEEK_SET: 45190792Sgshapiro bfp->bf_offset = offset; 45290792Sgshapiro break; 45390792Sgshapiro 45490792Sgshapiro case SEEK_CUR: 45590792Sgshapiro bfp->bf_offset += offset; 45690792Sgshapiro break; 45790792Sgshapiro 45890792Sgshapiro case SEEK_END: 45990792Sgshapiro bfp->bf_offset = bfp->bf_size + offset; 46090792Sgshapiro break; 46190792Sgshapiro 46290792Sgshapiro default: 46390792Sgshapiro errno = EINVAL; 46490792Sgshapiro return -1; 46590792Sgshapiro } 46690792Sgshapiro return bfp->bf_offset; 46790792Sgshapiro} 46890792Sgshapiro 46990792Sgshapiro/* 47090792Sgshapiro** SM_BFWRITE -- write to a buffered file 47190792Sgshapiro** 47290792Sgshapiro** Parameters: 47390792Sgshapiro** fp -- fp of file to write 47490792Sgshapiro** buf -- data buffer 47590792Sgshapiro** nbytes -- how many bytes to write 47690792Sgshapiro** 47790792Sgshapiro** Returns: 47890792Sgshapiro** number of bytes written or -1 indicate failure 47990792Sgshapiro** 48090792Sgshapiro** Side Effects: 48190792Sgshapiro** may create backing file if over memory limit for file. 48290792Sgshapiro** 48390792Sgshapiro*/ 48490792Sgshapiro 48590792Sgshapirostatic ssize_t 48690792Sgshapirosm_bfwrite(fp, buf, nbytes) 48790792Sgshapiro SM_FILE_T *fp; 48890792Sgshapiro const char *buf; 48990792Sgshapiro size_t nbytes; 49090792Sgshapiro{ 49190792Sgshapiro struct bf *bfp; 49290792Sgshapiro ssize_t count = 0; /* Number of bytes written so far */ 49390792Sgshapiro int retval; 49490792Sgshapiro 49590792Sgshapiro /* Cast cookie back to correct type */ 49690792Sgshapiro bfp = (struct bf *) fp->f_cookie; 49790792Sgshapiro 49890792Sgshapiro /* If committed, go straight to disk */ 49990792Sgshapiro if (bfp->bf_committed) 50090792Sgshapiro { 50190792Sgshapiro if (lseek(bfp->bf_disk_fd, bfp->bf_offset, SEEK_SET) < 0) 50290792Sgshapiro { 50390792Sgshapiro if ((errno == EINVAL) || (errno == ESPIPE)) 50490792Sgshapiro { 50590792Sgshapiro /* 50690792Sgshapiro ** stdio won't be expecting these 50790792Sgshapiro ** errnos from write()! Change them 50890792Sgshapiro ** into something it can understand. 50990792Sgshapiro */ 51090792Sgshapiro 51190792Sgshapiro errno = EIO; 51290792Sgshapiro } 51390792Sgshapiro return -1; 51490792Sgshapiro } 51590792Sgshapiro 51690792Sgshapiro count = write(bfp->bf_disk_fd, buf, nbytes); 51790792Sgshapiro if (count < 0) 51890792Sgshapiro { 51990792Sgshapiro /* errno is set implicitly by write() */ 52090792Sgshapiro return -1; 52190792Sgshapiro } 52290792Sgshapiro goto finished; 52390792Sgshapiro } 52490792Sgshapiro 52590792Sgshapiro if (bfp->bf_offset < bfp->bf_bufsize) 52690792Sgshapiro { 52790792Sgshapiro /* Need to put some in buffer */ 52890792Sgshapiro count = nbytes; 52990792Sgshapiro if ((bfp->bf_offset + count) > bfp->bf_bufsize) 53090792Sgshapiro count = bfp->bf_bufsize - bfp->bf_offset; 53190792Sgshapiro 53290792Sgshapiro memcpy(bfp->bf_buf + bfp->bf_offset, buf, count); 53390792Sgshapiro if ((bfp->bf_offset + count) > bfp->bf_buffilled) 53490792Sgshapiro bfp->bf_buffilled = bfp->bf_offset + count; 53590792Sgshapiro } 53690792Sgshapiro 53790792Sgshapiro if ((bfp->bf_offset + nbytes) > bfp->bf_bufsize) 53890792Sgshapiro { 53990792Sgshapiro /* Need to put some in file */ 54090792Sgshapiro if (!bfp->bf_ondisk) 54190792Sgshapiro { 54290792Sgshapiro MODE_T omask; 543159609Sgshapiro int save_errno; 54490792Sgshapiro 54590792Sgshapiro /* Clear umask as bf_filemode are the true perms */ 54690792Sgshapiro omask = umask(0); 54790792Sgshapiro retval = OPEN(bfp->bf_filename, 548120256Sgshapiro O_RDWR | O_CREAT | O_TRUNC | QF_O_EXTRA, 54990792Sgshapiro bfp->bf_filemode, bfp->bf_flags); 550159609Sgshapiro save_errno = errno; 55190792Sgshapiro (void) umask(omask); 552159609Sgshapiro errno = save_errno; 55390792Sgshapiro 55490792Sgshapiro /* Couldn't create file: failure */ 55590792Sgshapiro if (retval < 0) 55690792Sgshapiro { 55790792Sgshapiro /* 55890792Sgshapiro ** stdio may not be expecting these 55990792Sgshapiro ** errnos from write()! Change to 56090792Sgshapiro ** something which it can understand. 56190792Sgshapiro ** Note that ENOSPC and EDQUOT are saved 56290792Sgshapiro ** because they are actually valid for 56390792Sgshapiro ** write(). 56490792Sgshapiro */ 56590792Sgshapiro 56690792Sgshapiro if (!(errno == ENOSPC 56790792Sgshapiro#ifdef EDQUOT 56890792Sgshapiro || errno == EDQUOT 56990792Sgshapiro#endif /* EDQUOT */ 57090792Sgshapiro )) 57190792Sgshapiro errno = EIO; 57290792Sgshapiro 57390792Sgshapiro return -1; 57490792Sgshapiro } 57590792Sgshapiro bfp->bf_disk_fd = retval; 57690792Sgshapiro bfp->bf_ondisk = true; 57790792Sgshapiro } 57890792Sgshapiro 57990792Sgshapiro /* Catch a write() on an earlier failed write to disk */ 58090792Sgshapiro if (bfp->bf_ondisk && bfp->bf_disk_fd < 0) 58190792Sgshapiro { 58290792Sgshapiro errno = EIO; 58390792Sgshapiro return -1; 58490792Sgshapiro } 58590792Sgshapiro 58690792Sgshapiro if (lseek(bfp->bf_disk_fd, 58790792Sgshapiro bfp->bf_offset + count, SEEK_SET) < 0) 58890792Sgshapiro { 58990792Sgshapiro if ((errno == EINVAL) || (errno == ESPIPE)) 59090792Sgshapiro { 59190792Sgshapiro /* 59290792Sgshapiro ** stdio won't be expecting these 59390792Sgshapiro ** errnos from write()! Change them into 59490792Sgshapiro ** something which it can understand. 59590792Sgshapiro */ 59690792Sgshapiro 59790792Sgshapiro errno = EIO; 59890792Sgshapiro } 59990792Sgshapiro return -1; 60090792Sgshapiro } 60190792Sgshapiro 60290792Sgshapiro while (count < nbytes) 60390792Sgshapiro { 60490792Sgshapiro retval = write(bfp->bf_disk_fd, buf + count, 60590792Sgshapiro nbytes - count); 60690792Sgshapiro if (retval < 0) 60790792Sgshapiro { 60890792Sgshapiro /* errno is set implicitly by write() */ 60990792Sgshapiro return -1; 61090792Sgshapiro } 61190792Sgshapiro else 61290792Sgshapiro count += retval; 61390792Sgshapiro } 61490792Sgshapiro } 61590792Sgshapiro 61690792Sgshapirofinished: 61790792Sgshapiro bfp->bf_offset += count; 61890792Sgshapiro if (bfp->bf_offset > bfp->bf_size) 61990792Sgshapiro bfp->bf_size = bfp->bf_offset; 62090792Sgshapiro return count; 62190792Sgshapiro} 62290792Sgshapiro 62390792Sgshapiro/* 62490792Sgshapiro** BFREWIND -- rewinds the SM_FILE_T * 62590792Sgshapiro** 62690792Sgshapiro** Parameters: 62790792Sgshapiro** fp -- SM_FILE_T * to rewind 62890792Sgshapiro** 62990792Sgshapiro** Returns: 63090792Sgshapiro** 0 on success, -1 on error 63190792Sgshapiro** 63290792Sgshapiro** Side Effects: 63398121Sgshapiro** rewinds the SM_FILE_T * and puts it into read mode. Normally 63498121Sgshapiro** one would bfopen() a file, write to it, then bfrewind() and 63590792Sgshapiro** fread(). If fp is not a buffered file, this is equivalent to 63690792Sgshapiro** rewind(). 63790792Sgshapiro** 63890792Sgshapiro** Sets errno: 63990792Sgshapiro** any value of errno specified by sm_io_rewind() 64090792Sgshapiro*/ 64190792Sgshapiro 64290792Sgshapiroint 64390792Sgshapirobfrewind(fp) 64490792Sgshapiro SM_FILE_T *fp; 64590792Sgshapiro{ 64690792Sgshapiro (void) sm_io_flush(fp, SM_TIME_DEFAULT); 64790792Sgshapiro sm_io_clearerr(fp); /* quicker just to do it */ 64890792Sgshapiro return sm_io_seek(fp, SM_TIME_DEFAULT, 0, SM_IO_SEEK_SET); 64990792Sgshapiro} 65090792Sgshapiro 65190792Sgshapiro/* 65290792Sgshapiro** SM_BFCOMMIT -- "commits" the buffered file 65390792Sgshapiro** 65490792Sgshapiro** Parameters: 65590792Sgshapiro** fp -- SM_FILE_T * to commit to disk 65690792Sgshapiro** 65790792Sgshapiro** Returns: 65890792Sgshapiro** 0 on success, -1 on error 65990792Sgshapiro** 66090792Sgshapiro** Side Effects: 66190792Sgshapiro** Forces the given SM_FILE_T * to be written to disk if it is not 66290792Sgshapiro** already, and ensures that it will be kept after closing. If 66390792Sgshapiro** fp is not a buffered file, this is a no-op. 66490792Sgshapiro** 66590792Sgshapiro** Sets errno: 66690792Sgshapiro** any value of errno specified by open() 66790792Sgshapiro** any value of errno specified by write() 66890792Sgshapiro** any value of errno specified by lseek() 66990792Sgshapiro*/ 67090792Sgshapiro 67190792Sgshapirostatic int 67290792Sgshapirosm_bfcommit(fp) 67390792Sgshapiro SM_FILE_T *fp; 67490792Sgshapiro{ 67590792Sgshapiro struct bf *bfp; 67690792Sgshapiro int retval; 67790792Sgshapiro int byteswritten; 67890792Sgshapiro 67990792Sgshapiro /* Get associated bf structure */ 68090792Sgshapiro bfp = (struct bf *) fp->f_cookie; 68190792Sgshapiro 68290792Sgshapiro /* If already committed, noop */ 68390792Sgshapiro if (bfp->bf_committed) 68490792Sgshapiro return 0; 68590792Sgshapiro 68690792Sgshapiro /* Do we need to open a file? */ 68790792Sgshapiro if (!bfp->bf_ondisk) 68890792Sgshapiro { 68998841Sgshapiro int save_errno; 69090792Sgshapiro MODE_T omask; 69190792Sgshapiro struct stat st; 69290792Sgshapiro 69390792Sgshapiro if (tTd(58, 8)) 69490792Sgshapiro { 69590792Sgshapiro sm_dprintf("bfcommit(%s): to disk\n", bfp->bf_filename); 69690792Sgshapiro if (tTd(58, 32)) 69790792Sgshapiro sm_dprintf("bfcommit(): filemode %o flags %ld\n", 69890792Sgshapiro bfp->bf_filemode, bfp->bf_flags); 69990792Sgshapiro } 70090792Sgshapiro 70190792Sgshapiro if (stat(bfp->bf_filename, &st) == 0) 70290792Sgshapiro { 70390792Sgshapiro errno = EEXIST; 70490792Sgshapiro return -1; 70590792Sgshapiro } 70690792Sgshapiro 70790792Sgshapiro /* Clear umask as bf_filemode are the true perms */ 70890792Sgshapiro omask = umask(0); 709132943Sgshapiro retval = OPEN(bfp->bf_filename, 710132943Sgshapiro O_RDWR | O_CREAT | O_EXCL | QF_O_EXTRA, 71190792Sgshapiro bfp->bf_filemode, bfp->bf_flags); 71298841Sgshapiro save_errno = errno; 71390792Sgshapiro (void) umask(omask); 71490792Sgshapiro 71590792Sgshapiro /* Couldn't create file: failure */ 71690792Sgshapiro if (retval < 0) 71790792Sgshapiro { 71890792Sgshapiro /* errno is set implicitly by open() */ 71998841Sgshapiro errno = save_errno; 72090792Sgshapiro return -1; 72190792Sgshapiro } 72290792Sgshapiro 72390792Sgshapiro bfp->bf_disk_fd = retval; 72490792Sgshapiro bfp->bf_ondisk = true; 72590792Sgshapiro } 72690792Sgshapiro 72790792Sgshapiro /* Write out the contents of our buffer, if we have any */ 72890792Sgshapiro if (bfp->bf_buffilled > 0) 72990792Sgshapiro { 73090792Sgshapiro byteswritten = 0; 73190792Sgshapiro 73290792Sgshapiro if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0) 73390792Sgshapiro { 73490792Sgshapiro /* errno is set implicitly by lseek() */ 73590792Sgshapiro return -1; 73690792Sgshapiro } 73790792Sgshapiro 73890792Sgshapiro while (byteswritten < bfp->bf_buffilled) 73990792Sgshapiro { 74090792Sgshapiro retval = write(bfp->bf_disk_fd, 74190792Sgshapiro bfp->bf_buf + byteswritten, 74290792Sgshapiro bfp->bf_buffilled - byteswritten); 74390792Sgshapiro if (retval < 0) 74490792Sgshapiro { 74590792Sgshapiro /* errno is set implicitly by write() */ 74690792Sgshapiro return -1; 74790792Sgshapiro } 74890792Sgshapiro else 74990792Sgshapiro byteswritten += retval; 75090792Sgshapiro } 75190792Sgshapiro } 75290792Sgshapiro bfp->bf_committed = true; 75390792Sgshapiro 75490792Sgshapiro /* Invalidate buf; all goes to file now */ 75590792Sgshapiro bfp->bf_buffilled = 0; 75690792Sgshapiro if (bfp->bf_bufsize > 0) 75790792Sgshapiro { 75890792Sgshapiro /* Don't need buffer anymore; free it */ 75990792Sgshapiro bfp->bf_bufsize = 0; 76090792Sgshapiro sm_free(bfp->bf_buf); 76190792Sgshapiro } 76290792Sgshapiro return 0; 76390792Sgshapiro} 76490792Sgshapiro 76590792Sgshapiro/* 76690792Sgshapiro** SM_BFTRUNCATE -- rewinds and truncates the SM_FILE_T * 76790792Sgshapiro** 76890792Sgshapiro** Parameters: 76990792Sgshapiro** fp -- SM_FILE_T * to truncate 77090792Sgshapiro** 77190792Sgshapiro** Returns: 77290792Sgshapiro** 0 on success, -1 on error 77390792Sgshapiro** 77490792Sgshapiro** Side Effects: 77590792Sgshapiro** rewinds the SM_FILE_T *, truncates it to zero length, and puts 77690792Sgshapiro** it into write mode. 77790792Sgshapiro** 77890792Sgshapiro** Sets errno: 77990792Sgshapiro** any value of errno specified by fseek() 78090792Sgshapiro** any value of errno specified by ftruncate() 78190792Sgshapiro*/ 78290792Sgshapiro 78390792Sgshapirostatic int 78490792Sgshapirosm_bftruncate(fp) 78590792Sgshapiro SM_FILE_T *fp; 78690792Sgshapiro{ 78790792Sgshapiro struct bf *bfp; 78890792Sgshapiro 78990792Sgshapiro if (bfrewind(fp) < 0) 79090792Sgshapiro return -1; 79190792Sgshapiro 79290792Sgshapiro /* Get bf structure */ 79390792Sgshapiro bfp = (struct bf *) fp->f_cookie; 79490792Sgshapiro bfp->bf_buffilled = 0; 79590792Sgshapiro bfp->bf_size = 0; 79690792Sgshapiro 79790792Sgshapiro /* Need to zero the buffer */ 79890792Sgshapiro if (bfp->bf_bufsize > 0) 79990792Sgshapiro memset(bfp->bf_buf, '\0', bfp->bf_bufsize); 80090792Sgshapiro if (bfp->bf_ondisk) 80190792Sgshapiro { 80290792Sgshapiro#if NOFTRUNCATE 80390792Sgshapiro /* XXX: Not much we can do except rewind it */ 80490792Sgshapiro errno = EINVAL; 80590792Sgshapiro return -1; 80690792Sgshapiro#else /* NOFTRUNCATE */ 80790792Sgshapiro return ftruncate(bfp->bf_disk_fd, 0); 80890792Sgshapiro#endif /* NOFTRUNCATE */ 80990792Sgshapiro } 81098121Sgshapiro return 0; 81190792Sgshapiro} 81290792Sgshapiro 81390792Sgshapiro/* 81490792Sgshapiro** SM_BFSETINFO -- set/change info for an open file pointer 81590792Sgshapiro** 81690792Sgshapiro** Parameters: 81790792Sgshapiro** fp -- file pointer to get info about 81890792Sgshapiro** what -- type of info to set/change 81990792Sgshapiro** valp -- thing to set/change the info to 82090792Sgshapiro** 82190792Sgshapiro*/ 82290792Sgshapiro 82390792Sgshapirostatic int 82490792Sgshapirosm_bfsetinfo(fp, what, valp) 82590792Sgshapiro SM_FILE_T *fp; 82690792Sgshapiro int what; 82790792Sgshapiro void *valp; 82890792Sgshapiro{ 82990792Sgshapiro struct bf *bfp; 83090792Sgshapiro int bsize; 83190792Sgshapiro 83290792Sgshapiro /* Get bf structure */ 83390792Sgshapiro bfp = (struct bf *) fp->f_cookie; 83490792Sgshapiro switch (what) 83590792Sgshapiro { 83690792Sgshapiro case SM_BF_SETBUFSIZE: 83790792Sgshapiro bsize = *((int *) valp); 83890792Sgshapiro bfp->bf_bufsize = bsize; 83990792Sgshapiro 84090792Sgshapiro /* A zero bsize is valid, just don't allocate memory */ 84190792Sgshapiro if (bsize > 0) 84290792Sgshapiro { 84390792Sgshapiro bfp->bf_buf = (char *) sm_malloc(bsize); 84490792Sgshapiro if (bfp->bf_buf == NULL) 84590792Sgshapiro { 84690792Sgshapiro bfp->bf_bufsize = 0; 84790792Sgshapiro errno = ENOMEM; 84890792Sgshapiro return -1; 84990792Sgshapiro } 85090792Sgshapiro } 85190792Sgshapiro else 85290792Sgshapiro bfp->bf_buf = NULL; 85390792Sgshapiro return 0; 85490792Sgshapiro case SM_BF_COMMIT: 85590792Sgshapiro return sm_bfcommit(fp); 85690792Sgshapiro case SM_BF_TRUNCATE: 85790792Sgshapiro return sm_bftruncate(fp); 85890792Sgshapiro case SM_BF_TEST: 85990792Sgshapiro return 1; /* always */ 86090792Sgshapiro default: 86190792Sgshapiro errno = EINVAL; 86290792Sgshapiro return -1; 86390792Sgshapiro } 86490792Sgshapiro} 865