2 * Copyright (c) 1999-2002, 2004, 2006 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.62 2006/03/31 18:45:56 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 *));
41 static int sm_bfcommit __P((SM_FILE_T *));
42 static int sm_bftruncate __P((SM_FILE_T *));
44 static int sm_bfopen __P((SM_FILE_T *, const void *, int, const void *));
45 static int sm_bfsetinfo __P((SM_FILE_T *, int , void *));
46 static int sm_bfgetinfo __P((SM_FILE_T *, int , void *));
49 ** Data structure for storing information about each buffered file
50 ** (Originally in sendmail/bf_torek.h for the curious.)
55 bool bf_committed; /* Has this buffered file been committed? */
56 bool bf_ondisk; /* On disk: committed or buffer overflow */
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 */
69 # define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode)
70 #else /* BF_STANDALONE */
71 # define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff)
72 #endif /* BF_STANDALONE */
83 ** SM_BFOPEN -- the "base" open function called by sm_io_open() for the
84 ** internal, file-type-specific info setup.
87 ** fp -- file pointer being filled-in for file being open'd
88 ** info -- information about file being opened
90 ** rpool -- ignored (currently)
93 ** Failure: -1 and sets errno
98 sm_bfopen(fp, info, flags, rpool)
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;
118 if (*filename == '\0')
120 /* Empty filename string */
124 if (stat(filename, &st) == 0)
126 /* File already exists on disk */
131 /* Allocate memory */
132 bfp = (struct bf *) sm_malloc(sizeof(struct bf));
139 /* Assign data buffer */
140 /* A zero bsize is valid, just don't allocate memory */
143 bfp->bf_buf = (char *) sm_malloc(bsize);
144 if (bfp->bf_buf == NULL)
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)
165 if (bfp->bf_buf != NULL)
166 sm_free(bfp->bf_buf);
171 (void) sm_strlcpy(bfp->bf_filename, filename, l);
172 bfp->bf_filemode = fmode;
175 bfp->bf_disk_fd = -1;
179 sm_dprintf("sm_bfopen(%s)\n", filename);
185 ** BFOPEN -- create a new buffered file
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
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").
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()
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.
216 bfopen(char *filename, MODE_T fmode, size_t bsize, long flags)
219 bfopen(filename, fmode, bsize, flags)
224 #endif /* __STDC__ */
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,
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.
242 SM_IO_INIT_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
243 sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
245 info.bi_filename = filename;
246 info.bi_fmode = fmode;
247 info.bi_bsize = bsize;
248 info.bi_flags = flags;
250 return sm_io_open(&vector, SM_TIME_DEFAULT, &info, SM_IO_RDWR, NULL);
254 ** SM_BFGETINFO -- returns info about an open file pointer
257 ** fp -- file pointer to get info about
258 ** what -- type of info to obtain
259 ** valp -- thing to return the info in
263 sm_bfgetinfo(fp, what, valp)
270 bfp = (struct bf *) fp->f_cookie;
274 return bfp->bf_disk_fd;
275 case SM_IO_WHAT_SIZE:
283 ** SM_BFCLOSE -- close a buffered file
286 ** fp -- cookie of file to close
289 ** 0 to indicate success
292 ** deletes backing file, sm_frees memory.
304 /* Cast cookie back to correct type */
305 bfp = (struct bf *) fp->f_cookie;
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);
312 if (bfp->bf_disk_fd != -1)
313 close(bfp->bf_disk_fd);
315 /* Need to sm_free the buffer */
316 if (bfp->bf_bufsize > 0)
317 sm_free(bfp->bf_buf);
319 /* Finally, sm_free the structure */
325 ** SM_BFREAD -- read a buffered file
328 ** cookie -- cookie of file to read
329 ** buf -- buffer to fill
330 ** nbytes -- how many bytes to read
333 ** number of bytes read or -1 indicate failure
341 sm_bfread(fp, buf, nbytes)
347 ssize_t count = 0; /* Number of bytes put in buf so far */
350 /* Cast cookie back to correct type */
351 bfp = (struct bf *) fp->f_cookie;
353 if (bfp->bf_offset < bfp->bf_buffilled)
355 /* Need to grab some from buffer */
357 if ((bfp->bf_offset + count) > bfp->bf_buffilled)
358 count = bfp->bf_buffilled - bfp->bf_offset;
360 memcpy(buf, bfp->bf_buf + bfp->bf_offset, count);
363 if ((bfp->bf_offset + nbytes) > bfp->bf_buffilled)
365 /* Need to grab some from file */
368 /* Oops, the file doesn't exist. EOF. */
370 sm_dprintf("sm_bfread(%s): to disk\n",
375 /* Catch a read() on an earlier failed write to disk */
376 if (bfp->bf_disk_fd < 0)
382 if (lseek(bfp->bf_disk_fd,
383 bfp->bf_offset + count, SEEK_SET) < 0)
385 if ((errno == EINVAL) || (errno == ESPIPE))
388 ** stdio won't be expecting these
389 ** errnos from read()! Change them
390 ** into something it can understand.
398 while (count < nbytes)
400 retval = read(bfp->bf_disk_fd,
405 /* errno is set implicitly by read() */
408 else if (retval == 0)
416 bfp->bf_offset += count;
421 ** SM_BFSEEK -- seek to a position in a buffered file
424 ** fp -- fp of file to seek
425 ** offset -- position to seek to
426 ** whence -- how to seek
429 ** new file offset or -1 indicate failure
437 sm_bfseek(fp, offset, whence)
445 /* Cast cookie back to correct type */
446 bfp = (struct bf *) fp->f_cookie;
451 bfp->bf_offset = offset;
455 bfp->bf_offset += offset;
459 bfp->bf_offset = bfp->bf_size + offset;
466 return bfp->bf_offset;
470 ** SM_BFWRITE -- write to a buffered file
473 ** fp -- fp of file to write
474 ** buf -- data buffer
475 ** nbytes -- how many bytes to write
478 ** number of bytes written or -1 indicate failure
481 ** may create backing file if over memory limit for file.
486 sm_bfwrite(fp, buf, nbytes)
492 ssize_t count = 0; /* Number of bytes written so far */
495 /* Cast cookie back to correct type */
496 bfp = (struct bf *) fp->f_cookie;
498 /* If committed, go straight to disk */
499 if (bfp->bf_committed)
501 if (lseek(bfp->bf_disk_fd, bfp->bf_offset, SEEK_SET) < 0)
503 if ((errno == EINVAL) || (errno == ESPIPE))
506 ** stdio won't be expecting these
507 ** errnos from write()! Change them
508 ** into something it can understand.
516 count = write(bfp->bf_disk_fd, buf, nbytes);
519 /* errno is set implicitly by write() */
525 if (bfp->bf_offset < bfp->bf_bufsize)
527 /* Need to put some in buffer */
529 if ((bfp->bf_offset + count) > bfp->bf_bufsize)
530 count = bfp->bf_bufsize - bfp->bf_offset;
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;
537 if ((bfp->bf_offset + nbytes) > bfp->bf_bufsize)
539 /* Need to put some in file */
545 /* Clear umask as bf_filemode are the true perms */
547 retval = OPEN(bfp->bf_filename,
548 O_RDWR | O_CREAT | O_TRUNC | QF_O_EXTRA,
549 bfp->bf_filemode, bfp->bf_flags);
554 /* Couldn't create file: failure */
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
566 if (!(errno == ENOSPC
575 bfp->bf_disk_fd = retval;
576 bfp->bf_ondisk = true;
579 /* Catch a write() on an earlier failed write to disk */
580 if (bfp->bf_ondisk && bfp->bf_disk_fd < 0)
586 if (lseek(bfp->bf_disk_fd,
587 bfp->bf_offset + count, SEEK_SET) < 0)
589 if ((errno == EINVAL) || (errno == ESPIPE))
592 ** stdio won't be expecting these
593 ** errnos from write()! Change them into
594 ** something which it can understand.
602 while (count < nbytes)
604 retval = write(bfp->bf_disk_fd, buf + count,
608 /* errno is set implicitly by write() */
617 bfp->bf_offset += count;
618 if (bfp->bf_offset > bfp->bf_size)
619 bfp->bf_size = bfp->bf_offset;
624 ** BFREWIND -- rewinds the SM_FILE_T *
627 ** fp -- SM_FILE_T * to rewind
630 ** 0 on success, -1 on error
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
639 ** any value of errno specified by sm_io_rewind()
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);
652 ** SM_BFCOMMIT -- "commits" the buffered file
655 ** fp -- SM_FILE_T * to commit to disk
658 ** 0 on success, -1 on error
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.
666 ** any value of errno specified by open()
667 ** any value of errno specified by write()
668 ** any value of errno specified by lseek()
679 /* Get associated bf structure */
680 bfp = (struct bf *) fp->f_cookie;
682 /* If already committed, noop */
683 if (bfp->bf_committed)
686 /* Do we need to open a file? */
695 sm_dprintf("bfcommit(%s): to disk\n", bfp->bf_filename);
697 sm_dprintf("bfcommit(): filemode %o flags %ld\n",
698 bfp->bf_filemode, bfp->bf_flags);
701 if (stat(bfp->bf_filename, &st) == 0)
707 /* Clear umask as bf_filemode are the true perms */
709 retval = OPEN(bfp->bf_filename,
710 O_RDWR | O_CREAT | O_EXCL | QF_O_EXTRA,
711 bfp->bf_filemode, bfp->bf_flags);
715 /* Couldn't create file: failure */
718 /* errno is set implicitly by open() */
723 bfp->bf_disk_fd = retval;
724 bfp->bf_ondisk = true;
727 /* Write out the contents of our buffer, if we have any */
728 if (bfp->bf_buffilled > 0)
732 if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0)
734 /* errno is set implicitly by lseek() */
738 while (byteswritten < bfp->bf_buffilled)
740 retval = write(bfp->bf_disk_fd,
741 bfp->bf_buf + byteswritten,
742 bfp->bf_buffilled - byteswritten);
745 /* errno is set implicitly by write() */
749 byteswritten += retval;
752 bfp->bf_committed = true;
754 /* Invalidate buf; all goes to file now */
755 bfp->bf_buffilled = 0;
756 if (bfp->bf_bufsize > 0)
758 /* Don't need buffer anymore; free it */
760 sm_free(bfp->bf_buf);
766 ** SM_BFTRUNCATE -- rewinds and truncates the SM_FILE_T *
769 ** fp -- SM_FILE_T * to truncate
772 ** 0 on success, -1 on error
775 ** rewinds the SM_FILE_T *, truncates it to zero length, and puts
776 ** it into write mode.
779 ** any value of errno specified by fseek()
780 ** any value of errno specified by ftruncate()
789 if (bfrewind(fp) < 0)
792 /* Get bf structure */
793 bfp = (struct bf *) fp->f_cookie;
794 bfp->bf_buffilled = 0;
797 /* Need to zero the buffer */
798 if (bfp->bf_bufsize > 0)
799 memset(bfp->bf_buf, '\0', bfp->bf_bufsize);
803 /* XXX: Not much we can do except rewind it */
806 #else /* NOFTRUNCATE */
807 return ftruncate(bfp->bf_disk_fd, 0);
808 #endif /* NOFTRUNCATE */
814 ** SM_BFSETINFO -- set/change info for an open file pointer
817 ** fp -- file pointer to get info about
818 ** what -- type of info to set/change
819 ** valp -- thing to set/change the info to
824 sm_bfsetinfo(fp, what, valp)
832 /* Get bf structure */
833 bfp = (struct bf *) fp->f_cookie;
836 case SM_BF_SETBUFSIZE:
837 bsize = *((int *) valp);
838 bfp->bf_bufsize = bsize;
840 /* A zero bsize is valid, just don't allocate memory */
843 bfp->bf_buf = (char *) sm_malloc(bsize);
844 if (bfp->bf_buf == NULL)
855 return sm_bfcommit(fp);
857 return sm_bftruncate(fp);
859 return 1; /* always */