1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. 3*7c478bd9Sstevel@tonic-gate * All rights reserved. 4*7c478bd9Sstevel@tonic-gate * Copyright (c) 1990, 1993 5*7c478bd9Sstevel@tonic-gate * The Regents of the University of California. All rights reserved. 6*7c478bd9Sstevel@tonic-gate * 7*7c478bd9Sstevel@tonic-gate * This code is derived from software contributed to Berkeley by 8*7c478bd9Sstevel@tonic-gate * Chris Torek. 9*7c478bd9Sstevel@tonic-gate * 10*7c478bd9Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set 11*7c478bd9Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of 12*7c478bd9Sstevel@tonic-gate * the sendmail distribution. 13*7c478bd9Sstevel@tonic-gate */ 14*7c478bd9Sstevel@tonic-gate 15*7c478bd9Sstevel@tonic-gate #include <sm/gen.h> 16*7c478bd9Sstevel@tonic-gate SM_RCSID("@(#)$Id: fvwrite.c,v 1.47 2001/08/27 13:02:20 ca Exp $") 17*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 18*7c478bd9Sstevel@tonic-gate #include <unistd.h> 19*7c478bd9Sstevel@tonic-gate #include <string.h> 20*7c478bd9Sstevel@tonic-gate #include <errno.h> 21*7c478bd9Sstevel@tonic-gate #include <signal.h> 22*7c478bd9Sstevel@tonic-gate #include <fcntl.h> 23*7c478bd9Sstevel@tonic-gate #include <sm/io.h> 24*7c478bd9Sstevel@tonic-gate #include <sm/setjmp.h> 25*7c478bd9Sstevel@tonic-gate #include <sm/conf.h> 26*7c478bd9Sstevel@tonic-gate #include "local.h" 27*7c478bd9Sstevel@tonic-gate #include "fvwrite.h" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate /* 30*7c478bd9Sstevel@tonic-gate ** SM_FVWRITE -- write memory regions and buffer for file pointer 31*7c478bd9Sstevel@tonic-gate ** 32*7c478bd9Sstevel@tonic-gate ** Parameters: 33*7c478bd9Sstevel@tonic-gate ** fp -- the file pointer to write to 34*7c478bd9Sstevel@tonic-gate ** timeout -- time length for function to return by 35*7c478bd9Sstevel@tonic-gate ** uio -- the memory regions to write 36*7c478bd9Sstevel@tonic-gate ** 37*7c478bd9Sstevel@tonic-gate ** Returns: 38*7c478bd9Sstevel@tonic-gate ** Failure: returns SM_IO_EOF and sets errno 39*7c478bd9Sstevel@tonic-gate ** Success: returns 0 (zero) 40*7c478bd9Sstevel@tonic-gate ** 41*7c478bd9Sstevel@tonic-gate ** This routine is large and unsightly, but most of the ugliness due 42*7c478bd9Sstevel@tonic-gate ** to the different kinds of output buffering handled here. 43*7c478bd9Sstevel@tonic-gate */ 44*7c478bd9Sstevel@tonic-gate 45*7c478bd9Sstevel@tonic-gate #define COPY(n) (void)memcpy((void *)fp->f_p, (void *)p, (size_t)(n)) 46*7c478bd9Sstevel@tonic-gate #define GETIOV(extra_work) \ 47*7c478bd9Sstevel@tonic-gate while (len == 0) \ 48*7c478bd9Sstevel@tonic-gate { \ 49*7c478bd9Sstevel@tonic-gate extra_work; \ 50*7c478bd9Sstevel@tonic-gate p = iov->iov_base; \ 51*7c478bd9Sstevel@tonic-gate len = iov->iov_len; \ 52*7c478bd9Sstevel@tonic-gate iov++; \ 53*7c478bd9Sstevel@tonic-gate } 54*7c478bd9Sstevel@tonic-gate 55*7c478bd9Sstevel@tonic-gate int 56*7c478bd9Sstevel@tonic-gate sm_fvwrite(fp, timeout, uio) 57*7c478bd9Sstevel@tonic-gate register SM_FILE_T *fp; 58*7c478bd9Sstevel@tonic-gate int timeout; 59*7c478bd9Sstevel@tonic-gate register struct sm_uio *uio; 60*7c478bd9Sstevel@tonic-gate { 61*7c478bd9Sstevel@tonic-gate register size_t len; 62*7c478bd9Sstevel@tonic-gate register char *p; 63*7c478bd9Sstevel@tonic-gate register struct sm_iov *iov; 64*7c478bd9Sstevel@tonic-gate register int w, s; 65*7c478bd9Sstevel@tonic-gate char *nl; 66*7c478bd9Sstevel@tonic-gate int nlknown, nldist; 67*7c478bd9Sstevel@tonic-gate int fd; 68*7c478bd9Sstevel@tonic-gate struct timeval to; 69*7c478bd9Sstevel@tonic-gate 70*7c478bd9Sstevel@tonic-gate if (uio->uio_resid == 0) 71*7c478bd9Sstevel@tonic-gate return 0; 72*7c478bd9Sstevel@tonic-gate 73*7c478bd9Sstevel@tonic-gate /* make sure we can write */ 74*7c478bd9Sstevel@tonic-gate if (cantwrite(fp)) 75*7c478bd9Sstevel@tonic-gate { 76*7c478bd9Sstevel@tonic-gate errno = EBADF; 77*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; 78*7c478bd9Sstevel@tonic-gate } 79*7c478bd9Sstevel@tonic-gate 80*7c478bd9Sstevel@tonic-gate SM_CONVERT_TIME(fp, fd, timeout, &to); 81*7c478bd9Sstevel@tonic-gate 82*7c478bd9Sstevel@tonic-gate iov = uio->uio_iov; 83*7c478bd9Sstevel@tonic-gate p = iov->iov_base; 84*7c478bd9Sstevel@tonic-gate len = iov->iov_len; 85*7c478bd9Sstevel@tonic-gate iov++; 86*7c478bd9Sstevel@tonic-gate if (fp->f_flags & SMNBF) 87*7c478bd9Sstevel@tonic-gate { 88*7c478bd9Sstevel@tonic-gate /* Unbuffered: write up to BUFSIZ bytes at a time. */ 89*7c478bd9Sstevel@tonic-gate do 90*7c478bd9Sstevel@tonic-gate { 91*7c478bd9Sstevel@tonic-gate GETIOV(;); 92*7c478bd9Sstevel@tonic-gate errno = 0; /* needed to ensure EOF correctly found */ 93*7c478bd9Sstevel@tonic-gate w = (*fp->f_write)(fp, p, SM_MIN(len, SM_IO_BUFSIZ)); 94*7c478bd9Sstevel@tonic-gate if (w <= 0) 95*7c478bd9Sstevel@tonic-gate { 96*7c478bd9Sstevel@tonic-gate if (w == 0 && errno == 0) 97*7c478bd9Sstevel@tonic-gate break; /* EOF found */ 98*7c478bd9Sstevel@tonic-gate if (IS_IO_ERROR(fd, w, timeout)) 99*7c478bd9Sstevel@tonic-gate goto err; /* errno set */ 100*7c478bd9Sstevel@tonic-gate 101*7c478bd9Sstevel@tonic-gate /* write would block */ 102*7c478bd9Sstevel@tonic-gate SM_IO_WR_TIMEOUT(fp, fd, timeout); 103*7c478bd9Sstevel@tonic-gate w = 0; 104*7c478bd9Sstevel@tonic-gate } 105*7c478bd9Sstevel@tonic-gate else 106*7c478bd9Sstevel@tonic-gate { 107*7c478bd9Sstevel@tonic-gate p += w; 108*7c478bd9Sstevel@tonic-gate len -= w; 109*7c478bd9Sstevel@tonic-gate } 110*7c478bd9Sstevel@tonic-gate } while ((uio->uio_resid -= w) != 0); 111*7c478bd9Sstevel@tonic-gate } 112*7c478bd9Sstevel@tonic-gate else if ((fp->f_flags & SMLBF) == 0) 113*7c478bd9Sstevel@tonic-gate { 114*7c478bd9Sstevel@tonic-gate /* 115*7c478bd9Sstevel@tonic-gate ** Not SMLBF (line-buffered). Either SMFBF or SMNOW 116*7c478bd9Sstevel@tonic-gate ** buffered: fill partially full buffer, if any, 117*7c478bd9Sstevel@tonic-gate ** and then flush. If there is no partial buffer, write 118*7c478bd9Sstevel@tonic-gate ** one bf._size byte chunk directly (without copying). 119*7c478bd9Sstevel@tonic-gate ** 120*7c478bd9Sstevel@tonic-gate ** String output is a special case: write as many bytes 121*7c478bd9Sstevel@tonic-gate ** as fit, but pretend we wrote everything. This makes 122*7c478bd9Sstevel@tonic-gate ** snprintf() return the number of bytes needed, rather 123*7c478bd9Sstevel@tonic-gate ** than the number used, and avoids its write function 124*7c478bd9Sstevel@tonic-gate ** (so that the write function can be invalid). 125*7c478bd9Sstevel@tonic-gate */ 126*7c478bd9Sstevel@tonic-gate 127*7c478bd9Sstevel@tonic-gate do 128*7c478bd9Sstevel@tonic-gate { 129*7c478bd9Sstevel@tonic-gate GETIOV(;); 130*7c478bd9Sstevel@tonic-gate if ((((fp->f_flags & (SMALC | SMSTR)) == (SMALC | SMSTR)) 131*7c478bd9Sstevel@tonic-gate || ((fp->f_flags & SMNOW) != 0)) 132*7c478bd9Sstevel@tonic-gate && (size_t) fp->f_w < len) 133*7c478bd9Sstevel@tonic-gate { 134*7c478bd9Sstevel@tonic-gate size_t blen = fp->f_p - fp->f_bf.smb_base; 135*7c478bd9Sstevel@tonic-gate unsigned char *tbase; 136*7c478bd9Sstevel@tonic-gate int tsize; 137*7c478bd9Sstevel@tonic-gate 138*7c478bd9Sstevel@tonic-gate /* Allocate space exponentially. */ 139*7c478bd9Sstevel@tonic-gate tsize = fp->f_bf.smb_size; 140*7c478bd9Sstevel@tonic-gate do 141*7c478bd9Sstevel@tonic-gate { 142*7c478bd9Sstevel@tonic-gate tsize = (tsize << 1) + 1; 143*7c478bd9Sstevel@tonic-gate } while ((size_t) tsize < blen + len); 144*7c478bd9Sstevel@tonic-gate tbase = (unsigned char *) sm_realloc(fp->f_bf.smb_base, 145*7c478bd9Sstevel@tonic-gate tsize + 1); 146*7c478bd9Sstevel@tonic-gate if (tbase == NULL) 147*7c478bd9Sstevel@tonic-gate { 148*7c478bd9Sstevel@tonic-gate errno = ENOMEM; 149*7c478bd9Sstevel@tonic-gate goto err; /* errno set */ 150*7c478bd9Sstevel@tonic-gate } 151*7c478bd9Sstevel@tonic-gate fp->f_w += tsize - fp->f_bf.smb_size; 152*7c478bd9Sstevel@tonic-gate fp->f_bf.smb_base = tbase; 153*7c478bd9Sstevel@tonic-gate fp->f_bf.smb_size = tsize; 154*7c478bd9Sstevel@tonic-gate fp->f_p = tbase + blen; 155*7c478bd9Sstevel@tonic-gate } 156*7c478bd9Sstevel@tonic-gate w = fp->f_w; 157*7c478bd9Sstevel@tonic-gate errno = 0; /* needed to ensure EOF correctly found */ 158*7c478bd9Sstevel@tonic-gate if (fp->f_flags & SMSTR) 159*7c478bd9Sstevel@tonic-gate { 160*7c478bd9Sstevel@tonic-gate if (len < (size_t) w) 161*7c478bd9Sstevel@tonic-gate w = len; 162*7c478bd9Sstevel@tonic-gate COPY(w); /* copy SM_MIN(fp->f_w,len), */ 163*7c478bd9Sstevel@tonic-gate fp->f_w -= w; 164*7c478bd9Sstevel@tonic-gate fp->f_p += w; 165*7c478bd9Sstevel@tonic-gate w = len; /* but pretend copied all */ 166*7c478bd9Sstevel@tonic-gate } 167*7c478bd9Sstevel@tonic-gate else if (fp->f_p > fp->f_bf.smb_base 168*7c478bd9Sstevel@tonic-gate && len > (size_t) w) 169*7c478bd9Sstevel@tonic-gate { 170*7c478bd9Sstevel@tonic-gate /* fill and flush */ 171*7c478bd9Sstevel@tonic-gate COPY(w); 172*7c478bd9Sstevel@tonic-gate fp->f_p += w; 173*7c478bd9Sstevel@tonic-gate if (sm_flush(fp, &timeout)) 174*7c478bd9Sstevel@tonic-gate goto err; /* errno set */ 175*7c478bd9Sstevel@tonic-gate } 176*7c478bd9Sstevel@tonic-gate else if (len >= (size_t) (w = fp->f_bf.smb_size)) 177*7c478bd9Sstevel@tonic-gate { 178*7c478bd9Sstevel@tonic-gate /* write directly */ 179*7c478bd9Sstevel@tonic-gate w = (*fp->f_write)(fp, p, w); 180*7c478bd9Sstevel@tonic-gate if (w <= 0) 181*7c478bd9Sstevel@tonic-gate { 182*7c478bd9Sstevel@tonic-gate if (w == 0 && errno == 0) 183*7c478bd9Sstevel@tonic-gate break; /* EOF found */ 184*7c478bd9Sstevel@tonic-gate if (IS_IO_ERROR(fd, w, timeout)) 185*7c478bd9Sstevel@tonic-gate goto err; /* errno set */ 186*7c478bd9Sstevel@tonic-gate 187*7c478bd9Sstevel@tonic-gate /* write would block */ 188*7c478bd9Sstevel@tonic-gate SM_IO_WR_TIMEOUT(fp, fd, timeout); 189*7c478bd9Sstevel@tonic-gate w = 0; 190*7c478bd9Sstevel@tonic-gate } 191*7c478bd9Sstevel@tonic-gate } 192*7c478bd9Sstevel@tonic-gate else 193*7c478bd9Sstevel@tonic-gate { 194*7c478bd9Sstevel@tonic-gate /* fill and done */ 195*7c478bd9Sstevel@tonic-gate w = len; 196*7c478bd9Sstevel@tonic-gate COPY(w); 197*7c478bd9Sstevel@tonic-gate fp->f_w -= w; 198*7c478bd9Sstevel@tonic-gate fp->f_p += w; 199*7c478bd9Sstevel@tonic-gate } 200*7c478bd9Sstevel@tonic-gate p += w; 201*7c478bd9Sstevel@tonic-gate len -= w; 202*7c478bd9Sstevel@tonic-gate } while ((uio->uio_resid -= w) != 0); 203*7c478bd9Sstevel@tonic-gate 204*7c478bd9Sstevel@tonic-gate if ((fp->f_flags & SMNOW) != 0 && sm_flush(fp, &timeout)) 205*7c478bd9Sstevel@tonic-gate goto err; /* errno set */ 206*7c478bd9Sstevel@tonic-gate } 207*7c478bd9Sstevel@tonic-gate else 208*7c478bd9Sstevel@tonic-gate { 209*7c478bd9Sstevel@tonic-gate /* 210*7c478bd9Sstevel@tonic-gate ** Line buffered: like fully buffered, but we 211*7c478bd9Sstevel@tonic-gate ** must check for newlines. Compute the distance 212*7c478bd9Sstevel@tonic-gate ** to the first newline (including the newline), 213*7c478bd9Sstevel@tonic-gate ** or `infinity' if there is none, then pretend 214*7c478bd9Sstevel@tonic-gate ** that the amount to write is SM_MIN(len,nldist). 215*7c478bd9Sstevel@tonic-gate */ 216*7c478bd9Sstevel@tonic-gate 217*7c478bd9Sstevel@tonic-gate nlknown = 0; 218*7c478bd9Sstevel@tonic-gate nldist = 0; /* XXX just to keep gcc happy */ 219*7c478bd9Sstevel@tonic-gate do 220*7c478bd9Sstevel@tonic-gate { 221*7c478bd9Sstevel@tonic-gate GETIOV(nlknown = 0); 222*7c478bd9Sstevel@tonic-gate if (!nlknown) 223*7c478bd9Sstevel@tonic-gate { 224*7c478bd9Sstevel@tonic-gate nl = memchr((void *)p, '\n', len); 225*7c478bd9Sstevel@tonic-gate nldist = nl != NULL ? nl + 1 - p : len + 1; 226*7c478bd9Sstevel@tonic-gate nlknown = 1; 227*7c478bd9Sstevel@tonic-gate } 228*7c478bd9Sstevel@tonic-gate s = SM_MIN(len, ((size_t) nldist)); 229*7c478bd9Sstevel@tonic-gate w = fp->f_w + fp->f_bf.smb_size; 230*7c478bd9Sstevel@tonic-gate errno = 0; /* needed to ensure EOF correctly found */ 231*7c478bd9Sstevel@tonic-gate if (fp->f_p > fp->f_bf.smb_base && s > w) 232*7c478bd9Sstevel@tonic-gate { 233*7c478bd9Sstevel@tonic-gate COPY(w); 234*7c478bd9Sstevel@tonic-gate /* fp->f_w -= w; */ 235*7c478bd9Sstevel@tonic-gate fp->f_p += w; 236*7c478bd9Sstevel@tonic-gate if (sm_flush(fp, &timeout)) 237*7c478bd9Sstevel@tonic-gate goto err; /* errno set */ 238*7c478bd9Sstevel@tonic-gate } 239*7c478bd9Sstevel@tonic-gate else if (s >= (w = fp->f_bf.smb_size)) 240*7c478bd9Sstevel@tonic-gate { 241*7c478bd9Sstevel@tonic-gate w = (*fp->f_write)(fp, p, w); 242*7c478bd9Sstevel@tonic-gate if (w <= 0) 243*7c478bd9Sstevel@tonic-gate { 244*7c478bd9Sstevel@tonic-gate if (w == 0 && errno == 0) 245*7c478bd9Sstevel@tonic-gate break; /* EOF found */ 246*7c478bd9Sstevel@tonic-gate if (IS_IO_ERROR(fd, w, timeout)) 247*7c478bd9Sstevel@tonic-gate goto err; /* errno set */ 248*7c478bd9Sstevel@tonic-gate 249*7c478bd9Sstevel@tonic-gate /* write would block */ 250*7c478bd9Sstevel@tonic-gate SM_IO_WR_TIMEOUT(fp, fd, timeout); 251*7c478bd9Sstevel@tonic-gate w = 0; 252*7c478bd9Sstevel@tonic-gate } 253*7c478bd9Sstevel@tonic-gate } 254*7c478bd9Sstevel@tonic-gate else 255*7c478bd9Sstevel@tonic-gate { 256*7c478bd9Sstevel@tonic-gate w = s; 257*7c478bd9Sstevel@tonic-gate COPY(w); 258*7c478bd9Sstevel@tonic-gate fp->f_w -= w; 259*7c478bd9Sstevel@tonic-gate fp->f_p += w; 260*7c478bd9Sstevel@tonic-gate } 261*7c478bd9Sstevel@tonic-gate if ((nldist -= w) == 0) 262*7c478bd9Sstevel@tonic-gate { 263*7c478bd9Sstevel@tonic-gate /* copied the newline: flush and forget */ 264*7c478bd9Sstevel@tonic-gate if (sm_flush(fp, &timeout)) 265*7c478bd9Sstevel@tonic-gate goto err; /* errno set */ 266*7c478bd9Sstevel@tonic-gate nlknown = 0; 267*7c478bd9Sstevel@tonic-gate } 268*7c478bd9Sstevel@tonic-gate p += w; 269*7c478bd9Sstevel@tonic-gate len -= w; 270*7c478bd9Sstevel@tonic-gate } while ((uio->uio_resid -= w) != 0); 271*7c478bd9Sstevel@tonic-gate } 272*7c478bd9Sstevel@tonic-gate 273*7c478bd9Sstevel@tonic-gate return 0; 274*7c478bd9Sstevel@tonic-gate 275*7c478bd9Sstevel@tonic-gate err: 276*7c478bd9Sstevel@tonic-gate /* errno set before goto places us here */ 277*7c478bd9Sstevel@tonic-gate fp->f_flags |= SMERR; 278*7c478bd9Sstevel@tonic-gate return SM_IO_EOF; 279*7c478bd9Sstevel@tonic-gate } 280