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