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