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