xref: /freebsd/contrib/sendmail/src/bf.c (revision e92d3f3ffe83a6ed7eaafac70da9cf4fafe13243)
1 /*
2  * Copyright (c) 1999-2002, 2004 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  *
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.
8  *
9  * Contributed by Exactis.com, Inc.
10  *
11  */
12 
13 /*
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).
18 */
19 
20 #include <sm/gen.h>
21 SM_RCSID("@(#)$Id: bf.c,v 8.60 2004/04/14 18:12:49 ca Exp $")
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/uio.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include "sendmail.h"
32 #include "bf.h"
33 
34 #include <syslog.h>
35 
36 /* bf io functions */
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 
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 *));
45 
46 /*
47 **  Data structure for storing information about each buffered file
48 **  (Originally in sendmail/bf_torek.h for the curious.)
49 */
50 
51 struct bf
52 {
53 	bool	bf_committed;	/* Has this buffered file been committed? */
54 	bool	bf_ondisk;	/* On disk: committed or buffer overflow */
55 	long	bf_flags;
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 */
64 };
65 
66 #ifdef BF_STANDALONE
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 */
71 
72 struct bf_info
73 {
74 	char	*bi_filename;
75 	MODE_T	bi_fmode;
76 	size_t	bi_bsize;
77 	long	bi_flags;
78 };
79 
80 /*
81 **  SM_BFOPEN -- the "base" open function called by sm_io_open() for the
82 **		internal, file-type-specific info setup.
83 **
84 **	Parameters:
85 **		fp -- file pointer being filled-in for file being open'd
86 **		info -- information about file being opened
87 **		flags -- ignored
88 **		rpool -- ignored (currently)
89 **
90 **	Returns:
91 **		Failure: -1 and sets errno
92 **		Success: 0 (zero)
93 */
94 
95 static int
96 sm_bfopen(fp, info, flags, rpool)
97 	SM_FILE_T *fp;
98 	const void *info;
99 	int flags;
100 	const void *rpool;
101 {
102 	char *filename;
103 	MODE_T fmode;
104 	size_t bsize;
105 	long sflags;
106 	struct bf *bfp;
107 	int l;
108 	struct stat st;
109 
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;
114 
115 	/* Sanity checks */
116 	if (*filename == '\0')
117 	{
118 		/* Empty filename string */
119 		errno = ENOENT;
120 		return -1;
121 	}
122 	if (stat(filename, &st) == 0)
123 	{
124 		/* File already exists on disk */
125 		errno = EEXIST;
126 		return -1;
127 	}
128 
129 	/* Allocate memory */
130 	bfp = (struct bf *) sm_malloc(sizeof(struct bf));
131 	if (bfp == NULL)
132 	{
133 		errno = ENOMEM;
134 		return -1;
135 	}
136 
137 	/* Assign data buffer */
138 	/* A zero bsize is valid, just don't allocate memory */
139 	if (bsize > 0)
140 	{
141 		bfp->bf_buf = (char *) sm_malloc(bsize);
142 		if (bfp->bf_buf == NULL)
143 		{
144 			bfp->bf_bufsize = 0;
145 			sm_free(bfp);
146 			errno = ENOMEM;
147 			return -1;
148 		}
149 	}
150 	else
151 		bfp->bf_buf = NULL;
152 
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)
162 	{
163 		if (bfp->bf_buf != NULL)
164 			sm_free(bfp->bf_buf);
165 		sm_free(bfp);
166 		errno = ENOMEM;
167 		return -1;
168 	}
169 	(void) sm_strlcpy(bfp->bf_filename, filename, l);
170 	bfp->bf_filemode = fmode;
171 	bfp->bf_offset = 0;
172 	bfp->bf_size = 0;
173 	bfp->bf_disk_fd = -1;
174 	fp->f_cookie = bfp;
175 
176 	if (tTd(58, 8))
177 		sm_dprintf("sm_bfopen(%s)\n", filename);
178 
179 	return 0;
180 }
181 
182 /*
183 **  BFOPEN -- create a new buffered file
184 **
185 **	Parameters:
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
190 **
191 **	Returns:
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").
195 **
196 **	Side Effects:
197 **		none.
198 **
199 **	Sets errno:
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()
203 */
204 
205 #ifdef __STDC__
206 /*
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.
211 */
212 
213 SM_FILE_T *
214 bfopen(char *filename, MODE_T fmode, size_t bsize, long flags)
215 #else /* __STDC__ */
216 SM_FILE_T *
217 bfopen(filename, fmode, bsize, flags)
218 	char *filename;
219 	MODE_T fmode;
220 	size_t bsize;
221 	long flags;
222 #endif /* __STDC__ */
223 {
224 	MODE_T omask;
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,
227 		SM_TIME_FOREVER);
228 	struct bf_info info;
229 
230 	/*
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.
234 	*/
235 
236 	omask = umask(0);
237 	fmode &= ~omask;
238 	(void) umask(omask);
239 
240 	SM_IO_INIT_TYPE(vector, BF_FILE_TYPE, sm_bfopen, sm_bfclose,
241 		sm_bfread, sm_bfwrite, sm_bfseek, sm_bfgetinfo, sm_bfsetinfo,
242 		SM_TIME_FOREVER);
243 	info.bi_filename = filename;
244 	info.bi_fmode = fmode;
245 	info.bi_bsize = bsize;
246 	info.bi_flags = flags;
247 
248 	return sm_io_open(&vector, SM_TIME_DEFAULT, &info, SM_IO_RDWR, NULL);
249 }
250 
251 /*
252 **  SM_BFGETINFO -- returns info about an open file pointer
253 **
254 **	Parameters:
255 **		fp -- file pointer to get info about
256 **		what -- type of info to obtain
257 **		valp -- thing to return the info in
258 */
259 
260 static int
261 sm_bfgetinfo(fp, what, valp)
262 	SM_FILE_T *fp;
263 	int what;
264 	void *valp;
265 {
266 	struct bf *bfp;
267 
268 	bfp = (struct bf *) fp->f_cookie;
269 	switch (what)
270 	{
271 	  case SM_IO_WHAT_FD:
272 		return bfp->bf_disk_fd;
273 	  case SM_IO_WHAT_SIZE:
274 		return bfp->bf_size;
275 	  default:
276 		return -1;
277 	}
278 }
279 
280 /*
281 **  SM_BFCLOSE -- close a buffered file
282 **
283 **	Parameters:
284 **		fp -- cookie of file to close
285 **
286 **	Returns:
287 **		0 to indicate success
288 **
289 **	Side Effects:
290 **		deletes backing file, sm_frees memory.
291 **
292 **	Sets errno:
293 **		never.
294 */
295 
296 static int
297 sm_bfclose(fp)
298 	SM_FILE_T *fp;
299 {
300 	struct bf *bfp;
301 
302 	/* Cast cookie back to correct type */
303 	bfp = (struct bf *) fp->f_cookie;
304 
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);
309 
310 	if (bfp->bf_disk_fd != -1)
311 		close(bfp->bf_disk_fd);
312 
313 	/* Need to sm_free the buffer */
314 	if (bfp->bf_bufsize > 0)
315 		sm_free(bfp->bf_buf);
316 
317 	/* Finally, sm_free the structure */
318 	sm_free(bfp);
319 	return 0;
320 }
321 
322 /*
323 **  SM_BFREAD -- read a buffered file
324 **
325 **	Parameters:
326 **		cookie -- cookie of file to read
327 **		buf -- buffer to fill
328 **		nbytes -- how many bytes to read
329 **
330 **	Returns:
331 **		number of bytes read or -1 indicate failure
332 **
333 **	Side Effects:
334 **		none.
335 **
336 */
337 
338 static ssize_t
339 sm_bfread(fp, buf, nbytes)
340 	SM_FILE_T *fp;
341 	char *buf;
342 	size_t nbytes;
343 {
344 	struct bf *bfp;
345 	ssize_t count = 0;	/* Number of bytes put in buf so far */
346 	int retval;
347 
348 	/* Cast cookie back to correct type */
349 	bfp = (struct bf *) fp->f_cookie;
350 
351 	if (bfp->bf_offset < bfp->bf_buffilled)
352 	{
353 		/* Need to grab some from buffer */
354 		count = nbytes;
355 		if ((bfp->bf_offset + count) > bfp->bf_buffilled)
356 			count = bfp->bf_buffilled - bfp->bf_offset;
357 
358 		memcpy(buf, bfp->bf_buf + bfp->bf_offset, count);
359 	}
360 
361 	if ((bfp->bf_offset + nbytes) > bfp->bf_buffilled)
362 	{
363 		/* Need to grab some from file */
364 		if (!bfp->bf_ondisk)
365 		{
366 			/* Oops, the file doesn't exist. EOF. */
367 			if (tTd(58, 8))
368 				sm_dprintf("sm_bfread(%s): to disk\n",
369 					   bfp->bf_filename);
370 			goto finished;
371 		}
372 
373 		/* Catch a read() on an earlier failed write to disk */
374 		if (bfp->bf_disk_fd < 0)
375 		{
376 			errno = EIO;
377 			return -1;
378 		}
379 
380 		if (lseek(bfp->bf_disk_fd,
381 			  bfp->bf_offset + count, SEEK_SET) < 0)
382 		{
383 			if ((errno == EINVAL) || (errno == ESPIPE))
384 			{
385 				/*
386 				**  stdio won't be expecting these
387 				**  errnos from read()! Change them
388 				**  into something it can understand.
389 				*/
390 
391 				errno = EIO;
392 			}
393 			return -1;
394 		}
395 
396 		while (count < nbytes)
397 		{
398 			retval = read(bfp->bf_disk_fd,
399 				      buf + count,
400 				      nbytes - count);
401 			if (retval < 0)
402 			{
403 				/* errno is set implicitly by read() */
404 				return -1;
405 			}
406 			else if (retval == 0)
407 				goto finished;
408 			else
409 				count += retval;
410 		}
411 	}
412 
413 finished:
414 	bfp->bf_offset += count;
415 	return count;
416 }
417 
418 /*
419 **  SM_BFSEEK -- seek to a position in a buffered file
420 **
421 **	Parameters:
422 **		fp     -- fp of file to seek
423 **		offset -- position to seek to
424 **		whence -- how to seek
425 **
426 **	Returns:
427 **		new file offset or -1 indicate failure
428 **
429 **	Side Effects:
430 **		none.
431 **
432 */
433 
434 static off_t
435 sm_bfseek(fp, offset, whence)
436 	SM_FILE_T *fp;
437 	off_t offset;
438 	int whence;
439 
440 {
441 	struct bf *bfp;
442 
443 	/* Cast cookie back to correct type */
444 	bfp = (struct bf *) fp->f_cookie;
445 
446 	switch (whence)
447 	{
448 	  case SEEK_SET:
449 		bfp->bf_offset = offset;
450 		break;
451 
452 	  case SEEK_CUR:
453 		bfp->bf_offset += offset;
454 		break;
455 
456 	  case SEEK_END:
457 		bfp->bf_offset = bfp->bf_size + offset;
458 		break;
459 
460 	  default:
461 		errno = EINVAL;
462 		return -1;
463 	}
464 	return bfp->bf_offset;
465 }
466 
467 /*
468 **  SM_BFWRITE -- write to a buffered file
469 **
470 **	Parameters:
471 **		fp -- fp of file to write
472 **		buf -- data buffer
473 **		nbytes -- how many bytes to write
474 **
475 **	Returns:
476 **		number of bytes written or -1 indicate failure
477 **
478 **	Side Effects:
479 **		may create backing file if over memory limit for file.
480 **
481 */
482 
483 static ssize_t
484 sm_bfwrite(fp, buf, nbytes)
485 	SM_FILE_T *fp;
486 	const char *buf;
487 	size_t nbytes;
488 {
489 	struct bf *bfp;
490 	ssize_t count = 0;	/* Number of bytes written so far */
491 	int retval;
492 
493 	/* Cast cookie back to correct type */
494 	bfp = (struct bf *) fp->f_cookie;
495 
496 	/* If committed, go straight to disk */
497 	if (bfp->bf_committed)
498 	{
499 		if (lseek(bfp->bf_disk_fd, bfp->bf_offset, SEEK_SET) < 0)
500 		{
501 			if ((errno == EINVAL) || (errno == ESPIPE))
502 			{
503 				/*
504 				**  stdio won't be expecting these
505 				**  errnos from write()! Change them
506 				**  into something it can understand.
507 				*/
508 
509 				errno = EIO;
510 			}
511 			return -1;
512 		}
513 
514 		count = write(bfp->bf_disk_fd, buf, nbytes);
515 		if (count < 0)
516 		{
517 			/* errno is set implicitly by write() */
518 			return -1;
519 		}
520 		goto finished;
521 	}
522 
523 	if (bfp->bf_offset < bfp->bf_bufsize)
524 	{
525 		/* Need to put some in buffer */
526 		count = nbytes;
527 		if ((bfp->bf_offset + count) > bfp->bf_bufsize)
528 			count = bfp->bf_bufsize - bfp->bf_offset;
529 
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;
533 	}
534 
535 	if ((bfp->bf_offset + nbytes) > bfp->bf_bufsize)
536 	{
537 		/* Need to put some in file */
538 		if (!bfp->bf_ondisk)
539 		{
540 			MODE_T omask;
541 
542 			/* Clear umask as bf_filemode are the true perms */
543 			omask = umask(0);
544 			retval = OPEN(bfp->bf_filename,
545 				      O_RDWR | O_CREAT | O_TRUNC | QF_O_EXTRA,
546 				      bfp->bf_filemode, bfp->bf_flags);
547 			(void) umask(omask);
548 
549 			/* Couldn't create file: failure */
550 			if (retval < 0)
551 			{
552 				/*
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
558 				**  write().
559 				*/
560 
561 				if (!(errno == ENOSPC
562 #ifdef EDQUOT
563 				      || errno == EDQUOT
564 #endif /* EDQUOT */
565 				     ))
566 					errno = EIO;
567 
568 				return -1;
569 			}
570 			bfp->bf_disk_fd = retval;
571 			bfp->bf_ondisk = true;
572 		}
573 
574 		/* Catch a write() on an earlier failed write to disk */
575 		if (bfp->bf_ondisk && bfp->bf_disk_fd < 0)
576 		{
577 			errno = EIO;
578 			return -1;
579 		}
580 
581 		if (lseek(bfp->bf_disk_fd,
582 			  bfp->bf_offset + count, SEEK_SET) < 0)
583 		{
584 			if ((errno == EINVAL) || (errno == ESPIPE))
585 			{
586 				/*
587 				**  stdio won't be expecting these
588 				**  errnos from write()! Change them into
589 				**  something which it can understand.
590 				*/
591 
592 				errno = EIO;
593 			}
594 			return -1;
595 		}
596 
597 		while (count < nbytes)
598 		{
599 			retval = write(bfp->bf_disk_fd, buf + count,
600 				       nbytes - count);
601 			if (retval < 0)
602 			{
603 				/* errno is set implicitly by write() */
604 				return -1;
605 			}
606 			else
607 				count += retval;
608 		}
609 	}
610 
611 finished:
612 	bfp->bf_offset += count;
613 	if (bfp->bf_offset > bfp->bf_size)
614 		bfp->bf_size = bfp->bf_offset;
615 	return count;
616 }
617 
618 /*
619 **  BFREWIND -- rewinds the SM_FILE_T *
620 **
621 **	Parameters:
622 **		fp -- SM_FILE_T * to rewind
623 **
624 **	Returns:
625 **		0 on success, -1 on error
626 **
627 **	Side Effects:
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
631 **		rewind().
632 **
633 **	Sets errno:
634 **		any value of errno specified by sm_io_rewind()
635 */
636 
637 int
638 bfrewind(fp)
639 	SM_FILE_T *fp;
640 {
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);
644 }
645 
646 /*
647 **  SM_BFCOMMIT -- "commits" the buffered file
648 **
649 **	Parameters:
650 **		fp -- SM_FILE_T * to commit to disk
651 **
652 **	Returns:
653 **		0 on success, -1 on error
654 **
655 **	Side Effects:
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.
659 **
660 **	Sets errno:
661 **		any value of errno specified by open()
662 **		any value of errno specified by write()
663 **		any value of errno specified by lseek()
664 */
665 
666 static int
667 sm_bfcommit(fp)
668 	SM_FILE_T *fp;
669 {
670 	struct bf *bfp;
671 	int retval;
672 	int byteswritten;
673 
674 	/* Get associated bf structure */
675 	bfp = (struct bf *) fp->f_cookie;
676 
677 	/* If already committed, noop */
678 	if (bfp->bf_committed)
679 		return 0;
680 
681 	/* Do we need to open a file? */
682 	if (!bfp->bf_ondisk)
683 	{
684 		int save_errno;
685 		MODE_T omask;
686 		struct stat st;
687 
688 		if (tTd(58, 8))
689 		{
690 			sm_dprintf("bfcommit(%s): to disk\n", bfp->bf_filename);
691 			if (tTd(58, 32))
692 				sm_dprintf("bfcommit(): filemode %o flags %ld\n",
693 					   bfp->bf_filemode, bfp->bf_flags);
694 		}
695 
696 		if (stat(bfp->bf_filename, &st) == 0)
697 		{
698 			errno = EEXIST;
699 			return -1;
700 		}
701 
702 		/* Clear umask as bf_filemode are the true perms */
703 		omask = umask(0);
704 		retval = OPEN(bfp->bf_filename,
705 			      O_RDWR | O_CREAT | O_EXCL | QF_O_EXTRA,
706 			      bfp->bf_filemode, bfp->bf_flags);
707 		save_errno = errno;
708 		(void) umask(omask);
709 
710 		/* Couldn't create file: failure */
711 		if (retval < 0)
712 		{
713 			/* errno is set implicitly by open() */
714 			errno = save_errno;
715 			return -1;
716 		}
717 
718 		bfp->bf_disk_fd = retval;
719 		bfp->bf_ondisk = true;
720 	}
721 
722 	/* Write out the contents of our buffer, if we have any */
723 	if (bfp->bf_buffilled > 0)
724 	{
725 		byteswritten = 0;
726 
727 		if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0)
728 		{
729 			/* errno is set implicitly by lseek() */
730 			return -1;
731 		}
732 
733 		while (byteswritten < bfp->bf_buffilled)
734 		{
735 			retval = write(bfp->bf_disk_fd,
736 				       bfp->bf_buf + byteswritten,
737 				       bfp->bf_buffilled - byteswritten);
738 			if (retval < 0)
739 			{
740 				/* errno is set implicitly by write() */
741 				return -1;
742 			}
743 			else
744 				byteswritten += retval;
745 		}
746 	}
747 	bfp->bf_committed = true;
748 
749 	/* Invalidate buf; all goes to file now */
750 	bfp->bf_buffilled = 0;
751 	if (bfp->bf_bufsize > 0)
752 	{
753 		/* Don't need buffer anymore; free it */
754 		bfp->bf_bufsize = 0;
755 		sm_free(bfp->bf_buf);
756 	}
757 	return 0;
758 }
759 
760 /*
761 **  SM_BFTRUNCATE -- rewinds and truncates the SM_FILE_T *
762 **
763 **	Parameters:
764 **		fp -- SM_FILE_T * to truncate
765 **
766 **	Returns:
767 **		0 on success, -1 on error
768 **
769 **	Side Effects:
770 **		rewinds the SM_FILE_T *, truncates it to zero length, and puts
771 **		it into write mode.
772 **
773 **	Sets errno:
774 **		any value of errno specified by fseek()
775 **		any value of errno specified by ftruncate()
776 */
777 
778 static int
779 sm_bftruncate(fp)
780 	SM_FILE_T *fp;
781 {
782 	struct bf *bfp;
783 
784 	if (bfrewind(fp) < 0)
785 		return -1;
786 
787 	/* Get bf structure */
788 	bfp = (struct bf *) fp->f_cookie;
789 	bfp->bf_buffilled = 0;
790 	bfp->bf_size = 0;
791 
792 	/* Need to zero the buffer */
793 	if (bfp->bf_bufsize > 0)
794 		memset(bfp->bf_buf, '\0', bfp->bf_bufsize);
795 	if (bfp->bf_ondisk)
796 	{
797 #if NOFTRUNCATE
798 		/* XXX: Not much we can do except rewind it */
799 		errno = EINVAL;
800 		return -1;
801 #else /* NOFTRUNCATE */
802 		return ftruncate(bfp->bf_disk_fd, 0);
803 #endif /* NOFTRUNCATE */
804 	}
805 	return 0;
806 }
807 
808 /*
809 **  SM_BFSETINFO -- set/change info for an open file pointer
810 **
811 **	Parameters:
812 **		fp -- file pointer to get info about
813 **		what -- type of info to set/change
814 **		valp -- thing to set/change the info to
815 **
816 */
817 
818 static int
819 sm_bfsetinfo(fp, what, valp)
820 	SM_FILE_T *fp;
821 	int what;
822 	void *valp;
823 {
824 	struct bf *bfp;
825 	int bsize;
826 
827 	/* Get bf structure */
828 	bfp = (struct bf *) fp->f_cookie;
829 	switch (what)
830 	{
831 	  case SM_BF_SETBUFSIZE:
832 		bsize = *((int *) valp);
833 		bfp->bf_bufsize = bsize;
834 
835 		/* A zero bsize is valid, just don't allocate memory */
836 		if (bsize > 0)
837 		{
838 			bfp->bf_buf = (char *) sm_malloc(bsize);
839 			if (bfp->bf_buf == NULL)
840 			{
841 				bfp->bf_bufsize = 0;
842 				errno = ENOMEM;
843 				return -1;
844 			}
845 		}
846 		else
847 			bfp->bf_buf = NULL;
848 		return 0;
849 	  case SM_BF_COMMIT:
850 		return sm_bfcommit(fp);
851 	  case SM_BF_TRUNCATE:
852 		return sm_bftruncate(fp);
853 	  case SM_BF_TEST:
854 		return 1; /* always */
855 	  default:
856 		errno = EINVAL;
857 		return -1;
858 	}
859 }
860