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