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