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 */