2 * Copyright (c) 1999-2002 Sendmail, Inc. and its suppliers.
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.
9 * Contributed by Exactis.com, Inc.
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).
21 SM_RCSID("@(#)$Id: bf.c,v 8.54.2.3 2003/09/03 19:58:26 ca Exp $")
23 #include <sys/types.h>
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 *));
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 *));
47 ** Data structure for storing information about each buffered file
48 ** (Originally in sendmail/bf_torek.h for the curious.)
53 bool bf_committed; /* Has this buffered file been committed? */
54 bool bf_ondisk; /* On disk: committed or buffer overflow */
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 */
67 # define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode)
68 #else /* BF_STANDALONE */
69 # define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff)
70 #endif /* BF_STANDALONE */
81 ** SM_BFOPEN -- the "base" open function called by sm_io_open() for the
82 ** internal, file-type-specific info setup.
85 ** fp -- file pointer being filled-in for file being open'd
86 ** info -- information about file being opened
88 ** rpool -- ignored (currently)
91 ** Failure: -1 and sets errno
96 sm_bfopen(fp, info, flags, rpool)
110 filename = ((struct bf_info *) info)->bi_filename;
111 fmode = ((struct bf_info *) info)->bi_fmode;
112 bsize = ((struct bf_info *) info)->bi_bsize;
113 sflags = ((struct bf_info *) info)->bi_flags;
116 if (*filename == '\0')
118 /* Empty filename string */
122 if (stat(filename, &st) == 0)
124 /* File already exists on disk */
129 /* Allocate memory */
130 bfp = (struct bf *) sm_malloc(sizeof(struct bf));
137 /* Assign data buffer */
138 /* A zero bsize is valid, just don't allocate memory */
141 bfp->bf_buf = (char *) sm_malloc(bsize);
142 if (bfp->bf_buf == NULL)
153 /* Nearly home free, just set all the parameters now */
154 bfp->bf_committed = false;
155 bfp->bf_ondisk = false;
156 bfp->bf_flags = sflags;
157 bfp->bf_bufsize = bsize;
158 bfp->bf_buffilled = 0;
159 l = strlen(filename) + 1;
160 bfp->bf_filename = (char *) sm_malloc(l);
161 if (bfp->bf_filename == NULL)
163 if (bfp->bf_buf != NULL)
164 sm_free(bfp->bf_buf);
169 (void) sm_strlcpy(bfp->bf_filename, filename, l);
170 bfp->bf_filemode = fmode;
173 bfp->bf_disk_fd = -1;
177 sm_dprintf("sm_bfopen(%s)\n", filename);
183 ** BFOPEN -- create a new buffered file
186 ** filename -- the file's name
187 ** fmode -- what mode the file should be created as
188 ** bsize -- amount of buffer space to allocate (may be 0)
189 ** flags -- if running under sendmail, passed directly to safeopen
192 ** a SM_FILE_T * which may then be used with stdio functions,
193 ** or NULL on failure. SM_FILE_T * is opened for writing
194 ** "SM_IO_WHAT_VECTORS").
200 ** any value of errno specified by sm_io_setinfo_type()
201 ** any value of errno specified by sm_io_open()
202 ** any value of errno specified by sm_io_setinfo()
207 ** XXX This is a temporary hack since MODE_T on HP-UX 10.x is short.
208 ** If we use K&R here, the compiler will complain about
209 ** Inconsistent parameter list declaration
210 ** due to the change from short to int.
214 bfopen(char *filename, MODE_T fmode, size_t bsize, long flags)
217 bfopen(filename, fmode, bsize, flags)
222 #endif /* __STDC__ */
225 SM_FILE_T SM_IO_SET_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
226 sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
231 ** Apply current umask to fmode as it may change by the time
232 ** the file is actually created. fmode becomes the true
233 ** permissions of the file, which OPEN() must obey.
240 SM_IO_INIT_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
241 sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
243 info.bi_filename = filename;
244 info.bi_fmode = fmode;
245 info.bi_bsize = bsize;
246 info.bi_flags = flags;
248 return sm_io_open(&vector, SM_TIME_DEFAULT, &info, SM_IO_RDWR, NULL);
252 ** SM_BFGETINFO -- returns info about an open file pointer
255 ** fp -- file pointer to get info about
256 ** what -- type of info to obtain
257 ** valp -- thing to return the info in
261 sm_bfgetinfo(fp, what, valp)
268 bfp = (struct bf *) fp->f_cookie;
272 return bfp->bf_disk_fd;
273 case SM_IO_WHAT_SIZE:
281 ** SM_BFCLOSE -- close a buffered file
284 ** fp -- cookie of file to close
287 ** 0 to indicate success
290 ** deletes backing file, sm_frees memory.
302 /* Cast cookie back to correct type */
303 bfp = (struct bf *) fp->f_cookie;
305 /* Need to clean up the file */
306 if (bfp->bf_ondisk && !bfp->bf_committed)
307 unlink(bfp->bf_filename);
308 sm_free(bfp->bf_filename);
310 if (bfp->bf_disk_fd != -1)
311 close(bfp->bf_disk_fd);
313 /* Need to sm_free the buffer */
314 if (bfp->bf_bufsize > 0)
315 sm_free(bfp->bf_buf);
317 /* Finally, sm_free the structure */
323 ** SM_BFREAD -- read a buffered file
326 ** cookie -- cookie of file to read
327 ** buf -- buffer to fill
328 ** nbytes -- how many bytes to read
331 ** number of bytes read or -1 indicate failure
339 sm_bfread(fp, buf, nbytes)
345 ssize_t count = 0; /* Number of bytes put in buf so far */
348 /* Cast cookie back to correct type */
349 bfp = (struct bf *) fp->f_cookie;
351 if (bfp->bf_offset < bfp->bf_buffilled)
353 /* Need to grab some from buffer */
355 if ((bfp->bf_offset + count) > bfp->bf_buffilled)
356 count = bfp->bf_buffilled - bfp->bf_offset;
358 memcpy(buf, bfp->bf_buf + bfp->bf_offset, count);
361 if ((bfp->bf_offset + nbytes) > bfp->bf_buffilled)
363 /* Need to grab some from file */
366 /* Oops, the file doesn't exist. EOF. */
368 sm_dprintf("sm_bfread(%s): to disk\n",
373 /* Catch a read() on an earlier failed write to disk */
374 if (bfp->bf_disk_fd < 0)
380 if (lseek(bfp->bf_disk_fd,
381 bfp->bf_offset + count, SEEK_SET) < 0)
383 if ((errno == EINVAL) || (errno == ESPIPE))
386 ** stdio won't be expecting these
387 ** errnos from read()! Change them
388 ** into something it can understand.
396 while (count < nbytes)
398 retval = read(bfp->bf_disk_fd,
403 /* errno is set implicitly by read() */
406 else if (retval == 0)
414 bfp->bf_offset += count;
419 ** SM_BFSEEK -- seek to a position in a buffered file
422 ** fp -- fp of file to seek
423 ** offset -- position to seek to
424 ** whence -- how to seek
427 ** new file offset or -1 indicate failure
435 sm_bfseek(fp, offset, whence)
443 /* Cast cookie back to correct type */
444 bfp = (struct bf *) fp->f_cookie;
449 bfp->bf_offset = offset;
453 bfp->bf_offset += offset;
457 bfp->bf_offset = bfp->bf_size + offset;
464 return bfp->bf_offset;
468 ** SM_BFWRITE -- write to a buffered file
471 ** fp -- fp of file to write
472 ** buf -- data buffer
473 ** nbytes -- how many bytes to write
476 ** number of bytes written or -1 indicate failure
479 ** may create backing file if over memory limit for file.
484 sm_bfwrite(fp, buf, nbytes)
490 ssize_t count = 0; /* Number of bytes written so far */
493 /* Cast cookie back to correct type */
494 bfp = (struct bf *) fp->f_cookie;
496 /* If committed, go straight to disk */
497 if (bfp->bf_committed)
499 if (lseek(bfp->bf_disk_fd, bfp->bf_offset, SEEK_SET) < 0)
501 if ((errno == EINVAL) || (errno == ESPIPE))
504 ** stdio won't be expecting these
505 ** errnos from write()! Change them
506 ** into something it can understand.
514 count = write(bfp->bf_disk_fd, buf, nbytes);
517 /* errno is set implicitly by write() */
523 if (bfp->bf_offset < bfp->bf_bufsize)
525 /* Need to put some in buffer */
527 if ((bfp->bf_offset + count) > bfp->bf_bufsize)
528 count = bfp->bf_bufsize - bfp->bf_offset;
530 memcpy(bfp->bf_buf + bfp->bf_offset, buf, count);
531 if ((bfp->bf_offset + count) > bfp->bf_buffilled)
532 bfp->bf_buffilled = bfp->bf_offset + count;
535 if ((bfp->bf_offset + nbytes) > bfp->bf_bufsize)
537 /* Need to put some in file */
542 /* Clear umask as bf_filemode are the true perms */
544 retval = OPEN(bfp->bf_filename,
545 O_RDWR | O_CREAT | O_TRUNC | QF_O_EXTRA,
546 bfp->bf_filemode, bfp->bf_flags);
549 /* Couldn't create file: failure */
553 ** stdio may not be expecting these
554 ** errnos from write()! Change to
555 ** something which it can understand.
556 ** Note that ENOSPC and EDQUOT are saved
557 ** because they are actually valid for
561 if (!(errno == ENOSPC
570 bfp->bf_disk_fd = retval;
571 bfp->bf_ondisk = true;
574 /* Catch a write() on an earlier failed write to disk */
575 if (bfp->bf_ondisk && bfp->bf_disk_fd < 0)
581 if (lseek(bfp->bf_disk_fd,
582 bfp->bf_offset + count, SEEK_SET) < 0)
584 if ((errno == EINVAL) || (errno == ESPIPE))
587 ** stdio won't be expecting these
588 ** errnos from write()! Change them into
589 ** something which it can understand.
597 while (count < nbytes)
599 retval = write(bfp->bf_disk_fd, buf + count,
603 /* errno is set implicitly by write() */
612 bfp->bf_offset += count;
613 if (bfp->bf_offset > bfp->bf_size)
614 bfp->bf_size = bfp->bf_offset;
619 ** BFREWIND -- rewinds the SM_FILE_T *
622 ** fp -- SM_FILE_T * to rewind
625 ** 0 on success, -1 on error
628 ** rewinds the SM_FILE_T * and puts it into read mode. Normally
629 ** one would bfopen() a file, write to it, then bfrewind() and
630 ** fread(). If fp is not a buffered file, this is equivalent to
634 ** any value of errno specified by sm_io_rewind()
641 (void) sm_io_flush(fp, SM_TIME_DEFAULT);
642 sm_io_clearerr(fp); /* quicker just to do it */
643 return sm_io_seek(fp, SM_TIME_DEFAULT, 0, SM_IO_SEEK_SET);
647 ** SM_BFCOMMIT -- "commits" the buffered file
650 ** fp -- SM_FILE_T * to commit to disk
653 ** 0 on success, -1 on error
656 ** Forces the given SM_FILE_T * to be written to disk if it is not
657 ** already, and ensures that it will be kept after closing. If
658 ** fp is not a buffered file, this is a no-op.
661 ** any value of errno specified by open()
662 ** any value of errno specified by write()
663 ** any value of errno specified by lseek()
674 /* Get associated bf structure */
675 bfp = (struct bf *) fp->f_cookie;
677 /* If already committed, noop */
678 if (bfp->bf_committed)
681 /* Do we need to open a file? */
690 sm_dprintf("bfcommit(%s): to disk\n", bfp->bf_filename);
692 sm_dprintf("bfcommit(): filemode %o flags %ld\n",
693 bfp->bf_filemode, bfp->bf_flags);
696 if (stat(bfp->bf_filename, &st) == 0)
702 /* Clear umask as bf_filemode are the true perms */
704 retval = OPEN(bfp->bf_filename, O_RDWR | O_CREAT | O_EXCL,
705 bfp->bf_filemode, bfp->bf_flags);
709 /* Couldn't create file: failure */
712 /* errno is set implicitly by open() */
717 bfp->bf_disk_fd = retval;
718 bfp->bf_ondisk = true;
721 /* Write out the contents of our buffer, if we have any */
722 if (bfp->bf_buffilled > 0)
726 if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0)
728 /* errno is set implicitly by lseek() */
732 while (byteswritten < bfp->bf_buffilled)
734 retval = write(bfp->bf_disk_fd,
735 bfp->bf_buf + byteswritten,
736 bfp->bf_buffilled - byteswritten);
739 /* errno is set implicitly by write() */
743 byteswritten += retval;
746 bfp->bf_committed = true;
748 /* Invalidate buf; all goes to file now */
749 bfp->bf_buffilled = 0;
750 if (bfp->bf_bufsize > 0)
752 /* Don't need buffer anymore; free it */
754 sm_free(bfp->bf_buf);
760 ** SM_BFTRUNCATE -- rewinds and truncates the SM_FILE_T *
763 ** fp -- SM_FILE_T * to truncate
766 ** 0 on success, -1 on error
769 ** rewinds the SM_FILE_T *, truncates it to zero length, and puts
770 ** it into write mode.
773 ** any value of errno specified by fseek()
774 ** any value of errno specified by ftruncate()
783 if (bfrewind(fp) < 0)
786 /* Get bf structure */
787 bfp = (struct bf *) fp->f_cookie;
788 bfp->bf_buffilled = 0;
791 /* Need to zero the buffer */
792 if (bfp->bf_bufsize > 0)
793 memset(bfp->bf_buf, '\0', bfp->bf_bufsize);
797 /* XXX: Not much we can do except rewind it */
800 #else /* NOFTRUNCATE */
801 return ftruncate(bfp->bf_disk_fd, 0);
802 #endif /* NOFTRUNCATE */
808 ** SM_BFSETINFO -- set/change info for an open file pointer
811 ** fp -- file pointer to get info about
812 ** what -- type of info to set/change
813 ** valp -- thing to set/change the info to
818 sm_bfsetinfo(fp, what, valp)
826 /* Get bf structure */
827 bfp = (struct bf *) fp->f_cookie;
830 case SM_BF_SETBUFSIZE:
831 bsize = *((int *) valp);
832 bfp->bf_bufsize = bsize;
834 /* A zero bsize is valid, just don't allocate memory */
837 bfp->bf_buf = (char *) sm_malloc(bsize);
838 if (bfp->bf_buf == NULL)
849 return sm_bfcommit(fp);
851 return sm_bftruncate(fp);
853 return 1; /* always */