1 /*
2 * Copyright (c) 1999-2002, 2004, 2006 Proofpoint, 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.63 2013-11-22 20:51:55 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; /* Current 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
71 # define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff)
72 #endif
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
sm_bfopen(fp,info,flags,rpool)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 *
bfopen(char * filename,MODE_T fmode,size_t bsize,long flags)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
sm_bfgetinfo(fp,what,valp)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
sm_bfclose(fp)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
sm_bfread(fp,buf,nbytes)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
sm_bfseek(fp,offset,whence)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
sm_bfwrite(fp,buf,nbytes)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
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
bfrewind(fp)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
sm_bfcommit(fp)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 (unsigned int) bfp->bf_filemode,
699 bfp->bf_flags);
700 }
701
702 if (stat(bfp->bf_filename, &st) == 0)
703 {
704 errno = EEXIST;
705 return -1;
706 }
707
708 /* Clear umask as bf_filemode are the true perms */
709 omask = umask(0);
710 retval = OPEN(bfp->bf_filename,
711 O_RDWR | O_CREAT | O_EXCL | QF_O_EXTRA,
712 bfp->bf_filemode, bfp->bf_flags);
713 save_errno = errno;
714 (void) umask(omask);
715
716 /* Couldn't create file: failure */
717 if (retval < 0)
718 {
719 /* errno is set implicitly by open() */
720 errno = save_errno;
721 return -1;
722 }
723
724 bfp->bf_disk_fd = retval;
725 bfp->bf_ondisk = true;
726 }
727
728 /* Write out the contents of our buffer, if we have any */
729 if (bfp->bf_buffilled > 0)
730 {
731 byteswritten = 0;
732
733 if (lseek(bfp->bf_disk_fd, 0, SEEK_SET) < 0)
734 {
735 /* errno is set implicitly by lseek() */
736 return -1;
737 }
738
739 while (byteswritten < bfp->bf_buffilled)
740 {
741 retval = write(bfp->bf_disk_fd,
742 bfp->bf_buf + byteswritten,
743 bfp->bf_buffilled - byteswritten);
744 if (retval < 0)
745 {
746 /* errno is set implicitly by write() */
747 return -1;
748 }
749 else
750 byteswritten += retval;
751 }
752 }
753 bfp->bf_committed = true;
754
755 /* Invalidate buf; all goes to file now */
756 bfp->bf_buffilled = 0;
757 if (bfp->bf_bufsize > 0)
758 {
759 /* Don't need buffer anymore; free it */
760 bfp->bf_bufsize = 0;
761 sm_free(bfp->bf_buf);
762 }
763 return 0;
764 }
765
766 /*
767 ** SM_BFTRUNCATE -- rewinds and truncates the SM_FILE_T *
768 **
769 ** Parameters:
770 ** fp -- SM_FILE_T * to truncate
771 **
772 ** Returns:
773 ** 0 on success, -1 on error
774 **
775 ** Side Effects:
776 ** rewinds the SM_FILE_T *, truncates it to zero length, and puts
777 ** it into write mode.
778 **
779 ** Sets errno:
780 ** any value of errno specified by fseek()
781 ** any value of errno specified by ftruncate()
782 */
783
784 static int
sm_bftruncate(fp)785 sm_bftruncate(fp)
786 SM_FILE_T *fp;
787 {
788 struct bf *bfp;
789
790 if (bfrewind(fp) < 0)
791 return -1;
792
793 /* Get bf structure */
794 bfp = (struct bf *) fp->f_cookie;
795 bfp->bf_buffilled = 0;
796 bfp->bf_size = 0;
797
798 /* Need to zero the buffer */
799 if (bfp->bf_bufsize > 0)
800 memset(bfp->bf_buf, '\0', bfp->bf_bufsize);
801 if (bfp->bf_ondisk)
802 {
803 #if NOFTRUNCATE
804 /* XXX: Not much we can do except rewind it */
805 errno = EINVAL;
806 return -1;
807 #else
808 return ftruncate(bfp->bf_disk_fd, 0);
809 #endif
810 }
811 return 0;
812 }
813
814 /*
815 ** SM_BFSETINFO -- set/change info for an open file pointer
816 **
817 ** Parameters:
818 ** fp -- file pointer to get info about
819 ** what -- type of info to set/change
820 ** valp -- thing to set/change the info to
821 **
822 */
823
824 static int
sm_bfsetinfo(fp,what,valp)825 sm_bfsetinfo(fp, what, valp)
826 SM_FILE_T *fp;
827 int what;
828 void *valp;
829 {
830 struct bf *bfp;
831 int bsize;
832
833 /* Get bf structure */
834 bfp = (struct bf *) fp->f_cookie;
835 switch (what)
836 {
837 case SM_BF_SETBUFSIZE:
838 bsize = *((int *) valp);
839 bfp->bf_bufsize = bsize;
840
841 /* A zero bsize is valid, just don't allocate memory */
842 if (bsize > 0)
843 {
844 bfp->bf_buf = (char *) sm_malloc(bsize);
845 if (bfp->bf_buf == NULL)
846 {
847 bfp->bf_bufsize = 0;
848 errno = ENOMEM;
849 return -1;
850 }
851 }
852 else
853 bfp->bf_buf = NULL;
854 return 0;
855 case SM_BF_COMMIT:
856 return sm_bfcommit(fp);
857 case SM_BF_TRUNCATE:
858 return sm_bftruncate(fp);
859 case SM_BF_TEST:
860 return 1; /* always */
861 default:
862 errno = EINVAL;
863 return -1;
864 }
865 }
866