196c95412SPietro Cerutti /*- 271796d33SPietro Cerutti * Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org> 371796d33SPietro Cerutti * 471796d33SPietro Cerutti * Redistribution and use in source and binary forms, with or without 571796d33SPietro Cerutti * modification, are permitted provided that the following conditions 671796d33SPietro Cerutti * are met: 771796d33SPietro Cerutti * 1. Redistributions of source code must retain the above copyright 871796d33SPietro Cerutti * notice, this list of conditions and the following disclaimer. 971796d33SPietro Cerutti * 2. Redistributions in binary form must reproduce the above copyright 1071796d33SPietro Cerutti * notice, this list of conditions and the following disclaimer in the 1171796d33SPietro Cerutti * documentation and/or other materials provided with the distribution. 1271796d33SPietro Cerutti * 1371796d33SPietro Cerutti * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1471796d33SPietro Cerutti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1571796d33SPietro Cerutti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1671796d33SPietro Cerutti * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 1771796d33SPietro Cerutti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1871796d33SPietro Cerutti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 1971796d33SPietro Cerutti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2071796d33SPietro Cerutti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2171796d33SPietro Cerutti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2271796d33SPietro Cerutti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2371796d33SPietro Cerutti * SUCH DAMAGE. 2496c95412SPietro Cerutti */ 2596c95412SPietro Cerutti 2696c95412SPietro Cerutti #include <sys/cdefs.h> 2796c95412SPietro Cerutti __FBSDID("$FreeBSD$"); 2896c95412SPietro Cerutti 29646b68f0SPietro Cerutti #include <fcntl.h> 3071796d33SPietro Cerutti #include <stdbool.h> 3196c95412SPietro Cerutti #include <stdio.h> 3296c95412SPietro Cerutti #include <stdlib.h> 3396c95412SPietro Cerutti #include <string.h> 3496c95412SPietro Cerutti #include <errno.h> 35646b68f0SPietro Cerutti #include "local.h" 3696c95412SPietro Cerutti 37646b68f0SPietro Cerutti struct fmemopen_cookie 3896c95412SPietro Cerutti { 3996c95412SPietro Cerutti char *buf; /* pointer to the memory region */ 4071796d33SPietro Cerutti bool own; /* did we allocate the buffer ourselves? */ 41646b68f0SPietro Cerutti char bin; /* is this a binary buffer? */ 42646b68f0SPietro Cerutti size_t size; /* buffer length in bytes */ 43646b68f0SPietro Cerutti size_t len; /* data length in bytes */ 44646b68f0SPietro Cerutti size_t off; /* current offset into the buffer */ 4596c95412SPietro Cerutti }; 4696c95412SPietro Cerutti 4796c95412SPietro Cerutti static int fmemopen_read(void *cookie, char *buf, int nbytes); 4896c95412SPietro Cerutti static int fmemopen_write(void *cookie, const char *buf, int nbytes); 4996c95412SPietro Cerutti static fpos_t fmemopen_seek(void *cookie, fpos_t offset, int whence); 5096c95412SPietro Cerutti static int fmemopen_close(void *cookie); 5196c95412SPietro Cerutti 5296c95412SPietro Cerutti FILE * 5396c95412SPietro Cerutti fmemopen(void * __restrict buf, size_t size, const char * __restrict mode) 5496c95412SPietro Cerutti { 55646b68f0SPietro Cerutti struct fmemopen_cookie *ck; 56646b68f0SPietro Cerutti FILE *f; 57646b68f0SPietro Cerutti int flags, rc; 58646b68f0SPietro Cerutti 59646b68f0SPietro Cerutti /* 604c524a42SPietro Cerutti * POSIX says we shall return EINVAL if size is 0. 614c524a42SPietro Cerutti */ 624c524a42SPietro Cerutti if (size == 0) { 634c524a42SPietro Cerutti errno = EINVAL; 644c524a42SPietro Cerutti return (NULL); 654c524a42SPietro Cerutti } 664c524a42SPietro Cerutti 674c524a42SPietro Cerutti /* 68646b68f0SPietro Cerutti * Retrieve the flags as used by open(2) from the mode argument, and 69646b68f0SPietro Cerutti * validate them. 7071796d33SPietro Cerutti */ 71646b68f0SPietro Cerutti rc = __sflags(mode, &flags); 72646b68f0SPietro Cerutti if (rc == 0) { 73646b68f0SPietro Cerutti errno = EINVAL; 74646b68f0SPietro Cerutti return (NULL); 75646b68f0SPietro Cerutti } 76646b68f0SPietro Cerutti 77646b68f0SPietro Cerutti /* 78646b68f0SPietro Cerutti * There's no point in requiring an automatically allocated buffer 79646b68f0SPietro Cerutti * in write-only mode. 80646b68f0SPietro Cerutti */ 81646b68f0SPietro Cerutti if (!(flags & O_RDWR) && buf == NULL) { 82646b68f0SPietro Cerutti errno = EINVAL; 83646b68f0SPietro Cerutti return (NULL); 84646b68f0SPietro Cerutti } 85646b68f0SPietro Cerutti 86646b68f0SPietro Cerutti ck = malloc(sizeof(struct fmemopen_cookie)); 8796c95412SPietro Cerutti if (ck == NULL) { 8896c95412SPietro Cerutti return (NULL); 8996c95412SPietro Cerutti } 9096c95412SPietro Cerutti 9196c95412SPietro Cerutti ck->off = 0; 92646b68f0SPietro Cerutti ck->size = size; 9396c95412SPietro Cerutti 94646b68f0SPietro Cerutti /* Check whether we have to allocate the buffer ourselves. */ 9596c95412SPietro Cerutti ck->own = ((ck->buf = buf) == NULL); 9696c95412SPietro Cerutti if (ck->own) { 9796c95412SPietro Cerutti ck->buf = malloc(size); 9896c95412SPietro Cerutti if (ck->buf == NULL) { 9996c95412SPietro Cerutti free(ck); 10096c95412SPietro Cerutti return (NULL); 10196c95412SPietro Cerutti } 102646b68f0SPietro Cerutti } 103646b68f0SPietro Cerutti 104646b68f0SPietro Cerutti /* 105646b68f0SPietro Cerutti * POSIX distinguishes between w+ and r+, in that w+ is supposed to 106646b68f0SPietro Cerutti * truncate the buffer. 107646b68f0SPietro Cerutti */ 108646b68f0SPietro Cerutti if (ck->own || mode[0] == 'w') { 10996c95412SPietro Cerutti ck->buf[0] = '\0'; 11096c95412SPietro Cerutti } 11196c95412SPietro Cerutti 112646b68f0SPietro Cerutti /* Check for binary mode. */ 113646b68f0SPietro Cerutti ck->bin = strchr(mode, 'b') != NULL; 11496c95412SPietro Cerutti 115646b68f0SPietro Cerutti /* 116646b68f0SPietro Cerutti * The size of the current buffer contents is set depending on the 117646b68f0SPietro Cerutti * mode: 118646b68f0SPietro Cerutti * 119646b68f0SPietro Cerutti * for append (text-mode), the position of the first NULL byte, or the 120646b68f0SPietro Cerutti * size of the buffer if none is found 121646b68f0SPietro Cerutti * 122646b68f0SPietro Cerutti * for append (binary-mode), the size of the buffer 123646b68f0SPietro Cerutti * 124646b68f0SPietro Cerutti * for read, the size of the buffer 125646b68f0SPietro Cerutti * 126646b68f0SPietro Cerutti * for write, 0 127646b68f0SPietro Cerutti */ 128646b68f0SPietro Cerutti switch (mode[0]) { 129646b68f0SPietro Cerutti case 'a': 130646b68f0SPietro Cerutti ck->off = ck->len = strnlen(ck->buf, ck->size); 131646b68f0SPietro Cerutti break; 132646b68f0SPietro Cerutti case 'r': 133646b68f0SPietro Cerutti ck->len = size; 134646b68f0SPietro Cerutti break; 135646b68f0SPietro Cerutti case 'w': 136646b68f0SPietro Cerutti ck->len = 0; 137646b68f0SPietro Cerutti break; 138646b68f0SPietro Cerutti } 139646b68f0SPietro Cerutti 14071796d33SPietro Cerutti f = funopen(ck, 141646b68f0SPietro Cerutti flags & O_WRONLY ? NULL : fmemopen_read, 142646b68f0SPietro Cerutti flags & O_RDONLY ? NULL : fmemopen_write, 14396c95412SPietro Cerutti fmemopen_seek, fmemopen_close); 14496c95412SPietro Cerutti 14596c95412SPietro Cerutti if (f == NULL) { 14696c95412SPietro Cerutti if (ck->own) 14796c95412SPietro Cerutti free(ck->buf); 14896c95412SPietro Cerutti free(ck); 14996c95412SPietro Cerutti return (NULL); 15096c95412SPietro Cerutti } 15196c95412SPietro Cerutti 152*f6d1992dSAndrey A. Chernov if (mode[0] == 'a') 153*f6d1992dSAndrey A. Chernov f->_flags |= __SAPP; 154*f6d1992dSAndrey A. Chernov 155646b68f0SPietro Cerutti /* 156646b68f0SPietro Cerutti * Turn off buffering, so a write past the end of the buffer 157646b68f0SPietro Cerutti * correctly returns a short object count. 158646b68f0SPietro Cerutti */ 15971796d33SPietro Cerutti setvbuf(f, NULL, _IONBF, 0); 16096c95412SPietro Cerutti 16196c95412SPietro Cerutti return (f); 16296c95412SPietro Cerutti } 16396c95412SPietro Cerutti 16496c95412SPietro Cerutti static int 16596c95412SPietro Cerutti fmemopen_read(void *cookie, char *buf, int nbytes) 16696c95412SPietro Cerutti { 167646b68f0SPietro Cerutti struct fmemopen_cookie *ck = cookie; 16896c95412SPietro Cerutti 16996c95412SPietro Cerutti if (nbytes > ck->len - ck->off) 17096c95412SPietro Cerutti nbytes = ck->len - ck->off; 17196c95412SPietro Cerutti 17296c95412SPietro Cerutti if (nbytes == 0) 17396c95412SPietro Cerutti return (0); 17496c95412SPietro Cerutti 17596c95412SPietro Cerutti memcpy(buf, ck->buf + ck->off, nbytes); 17696c95412SPietro Cerutti 17796c95412SPietro Cerutti ck->off += nbytes; 17896c95412SPietro Cerutti 17996c95412SPietro Cerutti return (nbytes); 18096c95412SPietro Cerutti } 18196c95412SPietro Cerutti 18296c95412SPietro Cerutti static int 18396c95412SPietro Cerutti fmemopen_write(void *cookie, const char *buf, int nbytes) 18496c95412SPietro Cerutti { 185646b68f0SPietro Cerutti struct fmemopen_cookie *ck = cookie; 18696c95412SPietro Cerutti 187646b68f0SPietro Cerutti if (nbytes > ck->size - ck->off) 188646b68f0SPietro Cerutti nbytes = ck->size - ck->off; 18996c95412SPietro Cerutti 19096c95412SPietro Cerutti if (nbytes == 0) 19196c95412SPietro Cerutti return (0); 19296c95412SPietro Cerutti 19396c95412SPietro Cerutti memcpy(ck->buf + ck->off, buf, nbytes); 19496c95412SPietro Cerutti 19596c95412SPietro Cerutti ck->off += nbytes; 19696c95412SPietro Cerutti 197646b68f0SPietro Cerutti if (ck->off > ck->len) 198646b68f0SPietro Cerutti ck->len = ck->off; 199646b68f0SPietro Cerutti 200646b68f0SPietro Cerutti /* 201646b68f0SPietro Cerutti * We append a NULL byte if all these conditions are met: 202646b68f0SPietro Cerutti * - the buffer is not binary 203646b68f0SPietro Cerutti * - the buffer is not full 204646b68f0SPietro Cerutti * - the data just written doesn't already end with a NULL byte 205646b68f0SPietro Cerutti */ 206646b68f0SPietro Cerutti if (!ck->bin && ck->off < ck->size && ck->buf[ck->off - 1] != '\0') 20796c95412SPietro Cerutti ck->buf[ck->off] = '\0'; 20896c95412SPietro Cerutti 20996c95412SPietro Cerutti return (nbytes); 21096c95412SPietro Cerutti } 21196c95412SPietro Cerutti 21296c95412SPietro Cerutti static fpos_t 21396c95412SPietro Cerutti fmemopen_seek(void *cookie, fpos_t offset, int whence) 21496c95412SPietro Cerutti { 215646b68f0SPietro Cerutti struct fmemopen_cookie *ck = cookie; 21696c95412SPietro Cerutti 21796c95412SPietro Cerutti 21896c95412SPietro Cerutti switch (whence) { 21996c95412SPietro Cerutti case SEEK_SET: 220646b68f0SPietro Cerutti if (offset > ck->size) { 22196c95412SPietro Cerutti errno = EINVAL; 22296c95412SPietro Cerutti return (-1); 22396c95412SPietro Cerutti } 22496c95412SPietro Cerutti ck->off = offset; 22596c95412SPietro Cerutti break; 22696c95412SPietro Cerutti 22796c95412SPietro Cerutti case SEEK_CUR: 228646b68f0SPietro Cerutti if (ck->off + offset > ck->size) { 22996c95412SPietro Cerutti errno = EINVAL; 23096c95412SPietro Cerutti return (-1); 23196c95412SPietro Cerutti } 23296c95412SPietro Cerutti ck->off += offset; 23396c95412SPietro Cerutti break; 23496c95412SPietro Cerutti 23596c95412SPietro Cerutti case SEEK_END: 23696c95412SPietro Cerutti if (offset > 0 || -offset > ck->len) { 23796c95412SPietro Cerutti errno = EINVAL; 23896c95412SPietro Cerutti return (-1); 23996c95412SPietro Cerutti } 24096c95412SPietro Cerutti ck->off = ck->len + offset; 24196c95412SPietro Cerutti break; 24296c95412SPietro Cerutti 24396c95412SPietro Cerutti default: 24496c95412SPietro Cerutti errno = EINVAL; 24596c95412SPietro Cerutti return (-1); 24696c95412SPietro Cerutti } 24796c95412SPietro Cerutti 24896c95412SPietro Cerutti return (ck->off); 24996c95412SPietro Cerutti } 25096c95412SPietro Cerutti 25196c95412SPietro Cerutti static int 25296c95412SPietro Cerutti fmemopen_close(void *cookie) 25396c95412SPietro Cerutti { 254646b68f0SPietro Cerutti struct fmemopen_cookie *ck = cookie; 25596c95412SPietro Cerutti 25696c95412SPietro Cerutti if (ck->own) 25796c95412SPietro Cerutti free(ck->buf); 25896c95412SPietro Cerutti 25996c95412SPietro Cerutti free(ck); 26096c95412SPietro Cerutti 26196c95412SPietro Cerutti return (0); 26296c95412SPietro Cerutti } 263