196c95412SPietro Cerutti /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3d915a14eSPedro F. Giffuni * 471796d33SPietro Cerutti * Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org> 571796d33SPietro Cerutti * 671796d33SPietro Cerutti * Redistribution and use in source and binary forms, with or without 771796d33SPietro Cerutti * modification, are permitted provided that the following conditions 871796d33SPietro Cerutti * are met: 971796d33SPietro Cerutti * 1. Redistributions of source code must retain the above copyright 1071796d33SPietro Cerutti * notice, this list of conditions and the following disclaimer. 1171796d33SPietro Cerutti * 2. Redistributions in binary form must reproduce the above copyright 1271796d33SPietro Cerutti * notice, this list of conditions and the following disclaimer in the 1371796d33SPietro Cerutti * documentation and/or other materials provided with the distribution. 1471796d33SPietro Cerutti * 1571796d33SPietro Cerutti * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1671796d33SPietro Cerutti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1771796d33SPietro Cerutti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1871796d33SPietro Cerutti * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 1971796d33SPietro Cerutti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2071796d33SPietro Cerutti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2171796d33SPietro Cerutti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2271796d33SPietro Cerutti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2371796d33SPietro Cerutti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2471796d33SPietro Cerutti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2571796d33SPietro Cerutti * SUCH DAMAGE. 2696c95412SPietro Cerutti */ 2796c95412SPietro Cerutti 28646b68f0SPietro Cerutti #include <fcntl.h> 2971796d33SPietro Cerutti #include <stdbool.h> 3096c95412SPietro Cerutti #include <stdio.h> 3196c95412SPietro Cerutti #include <stdlib.h> 3296c95412SPietro Cerutti #include <string.h> 3396c95412SPietro Cerutti #include <errno.h> 34646b68f0SPietro Cerutti #include "local.h" 3596c95412SPietro Cerutti 36646b68f0SPietro Cerutti struct fmemopen_cookie 3796c95412SPietro Cerutti { 3896c95412SPietro Cerutti char *buf; /* pointer to the memory region */ 3971796d33SPietro Cerutti bool own; /* did we allocate the buffer ourselves? */ 40646b68f0SPietro Cerutti char bin; /* is this a binary buffer? */ 41646b68f0SPietro Cerutti size_t size; /* buffer length in bytes */ 42646b68f0SPietro Cerutti size_t len; /* data length in bytes */ 43646b68f0SPietro Cerutti size_t off; /* current offset into the buffer */ 4496c95412SPietro Cerutti }; 4596c95412SPietro Cerutti 4696c95412SPietro Cerutti static int fmemopen_read(void *cookie, char *buf, int nbytes); 4796c95412SPietro Cerutti static int fmemopen_write(void *cookie, const char *buf, int nbytes); 4896c95412SPietro Cerutti static fpos_t fmemopen_seek(void *cookie, fpos_t offset, int whence); 4996c95412SPietro Cerutti static int fmemopen_close(void *cookie); 5096c95412SPietro Cerutti 5196c95412SPietro Cerutti FILE * 5296c95412SPietro Cerutti fmemopen(void * __restrict buf, size_t size, const char * __restrict mode) 5396c95412SPietro Cerutti { 54646b68f0SPietro Cerutti struct fmemopen_cookie *ck; 55646b68f0SPietro Cerutti FILE *f; 56646b68f0SPietro Cerutti int flags, rc; 57646b68f0SPietro Cerutti 58646b68f0SPietro Cerutti /* 594c524a42SPietro Cerutti * POSIX says we shall return EINVAL if size is 0. 604c524a42SPietro Cerutti */ 614c524a42SPietro Cerutti if (size == 0) { 624c524a42SPietro Cerutti errno = EINVAL; 634c524a42SPietro Cerutti return (NULL); 644c524a42SPietro Cerutti } 654c524a42SPietro Cerutti 664c524a42SPietro Cerutti /* 67646b68f0SPietro Cerutti * Retrieve the flags as used by open(2) from the mode argument, and 68646b68f0SPietro Cerutti * validate them. 6971796d33SPietro Cerutti */ 70646b68f0SPietro Cerutti rc = __sflags(mode, &flags); 71646b68f0SPietro Cerutti if (rc == 0) { 72646b68f0SPietro Cerutti errno = EINVAL; 73646b68f0SPietro Cerutti return (NULL); 74646b68f0SPietro Cerutti } 75646b68f0SPietro Cerutti 76646b68f0SPietro Cerutti /* 77*0953460cSEd Maste * An automatically allocated buffer is only allowed in read-write mode. 78646b68f0SPietro Cerutti */ 79*0953460cSEd Maste if ((flags & O_ACCMODE) != O_RDWR && buf == NULL) { 80646b68f0SPietro Cerutti errno = EINVAL; 81646b68f0SPietro Cerutti return (NULL); 82646b68f0SPietro Cerutti } 83646b68f0SPietro Cerutti 84646b68f0SPietro Cerutti ck = malloc(sizeof(struct fmemopen_cookie)); 8596c95412SPietro Cerutti if (ck == NULL) { 8696c95412SPietro Cerutti return (NULL); 8796c95412SPietro Cerutti } 8896c95412SPietro Cerutti 8996c95412SPietro Cerutti ck->off = 0; 90646b68f0SPietro Cerutti ck->size = size; 9196c95412SPietro Cerutti 92646b68f0SPietro Cerutti /* Check whether we have to allocate the buffer ourselves. */ 9396c95412SPietro Cerutti ck->own = ((ck->buf = buf) == NULL); 9496c95412SPietro Cerutti if (ck->own) { 9596c95412SPietro Cerutti ck->buf = malloc(size); 9696c95412SPietro Cerutti if (ck->buf == NULL) { 9796c95412SPietro Cerutti free(ck); 9896c95412SPietro Cerutti return (NULL); 9996c95412SPietro Cerutti } 100646b68f0SPietro Cerutti } 101646b68f0SPietro Cerutti 102646b68f0SPietro Cerutti /* 103646b68f0SPietro Cerutti * POSIX distinguishes between w+ and r+, in that w+ is supposed to 104646b68f0SPietro Cerutti * truncate the buffer. 105646b68f0SPietro Cerutti */ 106646b68f0SPietro Cerutti if (ck->own || mode[0] == 'w') { 10796c95412SPietro Cerutti ck->buf[0] = '\0'; 10896c95412SPietro Cerutti } 10996c95412SPietro Cerutti 110646b68f0SPietro Cerutti /* Check for binary mode. */ 111646b68f0SPietro Cerutti ck->bin = strchr(mode, 'b') != NULL; 11296c95412SPietro Cerutti 113646b68f0SPietro Cerutti /* 114646b68f0SPietro Cerutti * The size of the current buffer contents is set depending on the 115646b68f0SPietro Cerutti * mode: 116646b68f0SPietro Cerutti * 117646b68f0SPietro Cerutti * for append (text-mode), the position of the first NULL byte, or the 118646b68f0SPietro Cerutti * size of the buffer if none is found 119646b68f0SPietro Cerutti * 120646b68f0SPietro Cerutti * for append (binary-mode), the size of the buffer 121646b68f0SPietro Cerutti * 122646b68f0SPietro Cerutti * for read, the size of the buffer 123646b68f0SPietro Cerutti * 124646b68f0SPietro Cerutti * for write, 0 125646b68f0SPietro Cerutti */ 126646b68f0SPietro Cerutti switch (mode[0]) { 127646b68f0SPietro Cerutti case 'a': 128646b68f0SPietro Cerutti ck->off = ck->len = strnlen(ck->buf, ck->size); 129646b68f0SPietro Cerutti break; 130646b68f0SPietro Cerutti case 'r': 131646b68f0SPietro Cerutti ck->len = size; 132646b68f0SPietro Cerutti break; 133646b68f0SPietro Cerutti case 'w': 134646b68f0SPietro Cerutti ck->len = 0; 135646b68f0SPietro Cerutti break; 136646b68f0SPietro Cerutti } 137646b68f0SPietro Cerutti 138*0953460cSEd Maste /* Disable read in O_WRONLY mode, and write in O_RDONLY mode. */ 13971796d33SPietro Cerutti f = funopen(ck, 140*0953460cSEd Maste (flags & O_ACCMODE) == O_WRONLY ? NULL : fmemopen_read, 141*0953460cSEd Maste (flags & O_ACCMODE) == O_RDONLY ? NULL : fmemopen_write, 14296c95412SPietro Cerutti fmemopen_seek, fmemopen_close); 14396c95412SPietro Cerutti 14496c95412SPietro Cerutti if (f == NULL) { 14596c95412SPietro Cerutti if (ck->own) 14696c95412SPietro Cerutti free(ck->buf); 14796c95412SPietro Cerutti free(ck); 14896c95412SPietro Cerutti return (NULL); 14996c95412SPietro Cerutti } 15096c95412SPietro Cerutti 151f6d1992dSAndrey A. Chernov if (mode[0] == 'a') 152f6d1992dSAndrey A. Chernov f->_flags |= __SAPP; 153f6d1992dSAndrey A. Chernov 154646b68f0SPietro Cerutti /* 155646b68f0SPietro Cerutti * Turn off buffering, so a write past the end of the buffer 156646b68f0SPietro Cerutti * correctly returns a short object count. 157646b68f0SPietro Cerutti */ 15871796d33SPietro Cerutti setvbuf(f, NULL, _IONBF, 0); 15996c95412SPietro Cerutti 16096c95412SPietro Cerutti return (f); 16196c95412SPietro Cerutti } 16296c95412SPietro Cerutti 16396c95412SPietro Cerutti static int 16496c95412SPietro Cerutti fmemopen_read(void *cookie, char *buf, int nbytes) 16596c95412SPietro Cerutti { 166646b68f0SPietro Cerutti struct fmemopen_cookie *ck = cookie; 16796c95412SPietro Cerutti 16896c95412SPietro Cerutti if (nbytes > ck->len - ck->off) 16996c95412SPietro Cerutti nbytes = ck->len - ck->off; 17096c95412SPietro Cerutti 17196c95412SPietro Cerutti if (nbytes == 0) 17296c95412SPietro Cerutti return (0); 17396c95412SPietro Cerutti 17496c95412SPietro Cerutti memcpy(buf, ck->buf + ck->off, nbytes); 17596c95412SPietro Cerutti 17696c95412SPietro Cerutti ck->off += nbytes; 17796c95412SPietro Cerutti 17896c95412SPietro Cerutti return (nbytes); 17996c95412SPietro Cerutti } 18096c95412SPietro Cerutti 18196c95412SPietro Cerutti static int 18296c95412SPietro Cerutti fmemopen_write(void *cookie, const char *buf, int nbytes) 18396c95412SPietro Cerutti { 184646b68f0SPietro Cerutti struct fmemopen_cookie *ck = cookie; 18596c95412SPietro Cerutti 186646b68f0SPietro Cerutti if (nbytes > ck->size - ck->off) 187646b68f0SPietro Cerutti nbytes = ck->size - ck->off; 18896c95412SPietro Cerutti 18996c95412SPietro Cerutti if (nbytes == 0) 19096c95412SPietro Cerutti return (0); 19196c95412SPietro Cerutti 19296c95412SPietro Cerutti memcpy(ck->buf + ck->off, buf, nbytes); 19396c95412SPietro Cerutti 19496c95412SPietro Cerutti ck->off += nbytes; 19596c95412SPietro Cerutti 196646b68f0SPietro Cerutti if (ck->off > ck->len) 197646b68f0SPietro Cerutti ck->len = ck->off; 198646b68f0SPietro Cerutti 199646b68f0SPietro Cerutti /* 200646b68f0SPietro Cerutti * We append a NULL byte if all these conditions are met: 201646b68f0SPietro Cerutti * - the buffer is not binary 202646b68f0SPietro Cerutti * - the buffer is not full 203646b68f0SPietro Cerutti * - the data just written doesn't already end with a NULL byte 204646b68f0SPietro Cerutti */ 205646b68f0SPietro Cerutti if (!ck->bin && ck->off < ck->size && ck->buf[ck->off - 1] != '\0') 20696c95412SPietro Cerutti ck->buf[ck->off] = '\0'; 20796c95412SPietro Cerutti 20896c95412SPietro Cerutti return (nbytes); 20996c95412SPietro Cerutti } 21096c95412SPietro Cerutti 21196c95412SPietro Cerutti static fpos_t 21296c95412SPietro Cerutti fmemopen_seek(void *cookie, fpos_t offset, int whence) 21396c95412SPietro Cerutti { 214646b68f0SPietro Cerutti struct fmemopen_cookie *ck = cookie; 21596c95412SPietro Cerutti 21696c95412SPietro Cerutti 21796c95412SPietro Cerutti switch (whence) { 21896c95412SPietro Cerutti case SEEK_SET: 219646b68f0SPietro Cerutti if (offset > ck->size) { 22096c95412SPietro Cerutti errno = EINVAL; 22196c95412SPietro Cerutti return (-1); 22296c95412SPietro Cerutti } 22396c95412SPietro Cerutti ck->off = offset; 22496c95412SPietro Cerutti break; 22596c95412SPietro Cerutti 22696c95412SPietro Cerutti case SEEK_CUR: 227646b68f0SPietro Cerutti if (ck->off + offset > ck->size) { 22896c95412SPietro Cerutti errno = EINVAL; 22996c95412SPietro Cerutti return (-1); 23096c95412SPietro Cerutti } 23196c95412SPietro Cerutti ck->off += offset; 23296c95412SPietro Cerutti break; 23396c95412SPietro Cerutti 23496c95412SPietro Cerutti case SEEK_END: 23596c95412SPietro Cerutti if (offset > 0 || -offset > ck->len) { 23696c95412SPietro Cerutti errno = EINVAL; 23796c95412SPietro Cerutti return (-1); 23896c95412SPietro Cerutti } 23996c95412SPietro Cerutti ck->off = ck->len + offset; 24096c95412SPietro Cerutti break; 24196c95412SPietro Cerutti 24296c95412SPietro Cerutti default: 24396c95412SPietro Cerutti errno = EINVAL; 24496c95412SPietro Cerutti return (-1); 24596c95412SPietro Cerutti } 24696c95412SPietro Cerutti 24796c95412SPietro Cerutti return (ck->off); 24896c95412SPietro Cerutti } 24996c95412SPietro Cerutti 25096c95412SPietro Cerutti static int 25196c95412SPietro Cerutti fmemopen_close(void *cookie) 25296c95412SPietro Cerutti { 253646b68f0SPietro Cerutti struct fmemopen_cookie *ck = cookie; 25496c95412SPietro Cerutti 25596c95412SPietro Cerutti if (ck->own) 25696c95412SPietro Cerutti free(ck->buf); 25796c95412SPietro Cerutti 25896c95412SPietro Cerutti free(ck); 25996c95412SPietro Cerutti 26096c95412SPietro Cerutti return (0); 26196c95412SPietro Cerutti } 262