1*f65a228fSRobert Mustacchi /*
2*f65a228fSRobert Mustacchi * This file and its contents are supplied under the terms of the
3*f65a228fSRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0.
4*f65a228fSRobert Mustacchi * You may only use this file in accordance with the terms of version
5*f65a228fSRobert Mustacchi * 1.0 of the CDDL.
6*f65a228fSRobert Mustacchi *
7*f65a228fSRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this
8*f65a228fSRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at
9*f65a228fSRobert Mustacchi * http://www.illumos.org/license/CDDL.
10*f65a228fSRobert Mustacchi */
11*f65a228fSRobert Mustacchi
12*f65a228fSRobert Mustacchi /*
13*f65a228fSRobert Mustacchi * Copyright 2020 Robert Mustacchi
14*f65a228fSRobert Mustacchi */
15*f65a228fSRobert Mustacchi
16*f65a228fSRobert Mustacchi /*
17*f65a228fSRobert Mustacchi * Implements fmemopen(3C).
18*f65a228fSRobert Mustacchi */
19*f65a228fSRobert Mustacchi
20*f65a228fSRobert Mustacchi #include "mtlib.h"
21*f65a228fSRobert Mustacchi #include "file64.h"
22*f65a228fSRobert Mustacchi #include <stdio.h>
23*f65a228fSRobert Mustacchi #include "stdiom.h"
24*f65a228fSRobert Mustacchi #include <errno.h>
25*f65a228fSRobert Mustacchi #include <stdlib.h>
26*f65a228fSRobert Mustacchi #include <fcntl.h>
27*f65a228fSRobert Mustacchi #include <sys/sysmacros.h>
28*f65a228fSRobert Mustacchi #include <limits.h>
29*f65a228fSRobert Mustacchi
30*f65a228fSRobert Mustacchi typedef enum fmemopen_flags {
31*f65a228fSRobert Mustacchi /*
32*f65a228fSRobert Mustacchi * Indicates that the user gave us the buffer and so we shouldn't free
33*f65a228fSRobert Mustacchi * it.
34*f65a228fSRobert Mustacchi */
35*f65a228fSRobert Mustacchi FMO_F_USER_BUFFER = 1 << 0,
36*f65a228fSRobert Mustacchi /*
37*f65a228fSRobert Mustacchi * When the stream is open for update (a, a+) then we have to have
38*f65a228fSRobert Mustacchi * slightly different behavior on write and zeroing the buffer.
39*f65a228fSRobert Mustacchi */
40*f65a228fSRobert Mustacchi FMO_F_APPEND = 1 << 1
41*f65a228fSRobert Mustacchi } fmemopen_flags_t;
42*f65a228fSRobert Mustacchi
43*f65a228fSRobert Mustacchi typedef struct fmemopen {
44*f65a228fSRobert Mustacchi /*
45*f65a228fSRobert Mustacchi * Pointer to the underlying memory stream.
46*f65a228fSRobert Mustacchi */
47*f65a228fSRobert Mustacchi char *fmo_buf;
48*f65a228fSRobert Mustacchi /*
49*f65a228fSRobert Mustacchi * Allocated length of the buffer.
50*f65a228fSRobert Mustacchi */
51*f65a228fSRobert Mustacchi size_t fmo_alloc;
52*f65a228fSRobert Mustacchi /*
53*f65a228fSRobert Mustacchi * Current position of the buffer.
54*f65a228fSRobert Mustacchi */
55*f65a228fSRobert Mustacchi size_t fmo_pos;
56*f65a228fSRobert Mustacchi /*
57*f65a228fSRobert Mustacchi * Current 'size' of the buffer. POSIX describes a size that the buffer
58*f65a228fSRobert Mustacchi * has which is separate from the allocated size, but cannot exceed it.
59*f65a228fSRobert Mustacchi */
60*f65a228fSRobert Mustacchi size_t fmo_lsize;
61*f65a228fSRobert Mustacchi fmemopen_flags_t fmo_flags;
62*f65a228fSRobert Mustacchi } fmemopen_t;
63*f65a228fSRobert Mustacchi
64*f65a228fSRobert Mustacchi static ssize_t
fmemopen_read(FILE * iop,char * buf,size_t nbytes)65*f65a228fSRobert Mustacchi fmemopen_read(FILE *iop, char *buf, size_t nbytes)
66*f65a228fSRobert Mustacchi {
67*f65a228fSRobert Mustacchi fmemopen_t *fmp = _xdata(iop);
68*f65a228fSRobert Mustacchi
69*f65a228fSRobert Mustacchi nbytes = MIN(nbytes, fmp->fmo_lsize - fmp->fmo_pos);
70*f65a228fSRobert Mustacchi if (nbytes == 0) {
71*f65a228fSRobert Mustacchi return (0);
72*f65a228fSRobert Mustacchi }
73*f65a228fSRobert Mustacchi
74*f65a228fSRobert Mustacchi (void) memcpy(buf, fmp->fmo_buf, nbytes);
75*f65a228fSRobert Mustacchi fmp->fmo_pos += nbytes;
76*f65a228fSRobert Mustacchi
77*f65a228fSRobert Mustacchi return (nbytes);
78*f65a228fSRobert Mustacchi }
79*f65a228fSRobert Mustacchi
80*f65a228fSRobert Mustacchi static ssize_t
fmemopen_write(FILE * iop,const char * buf,size_t nbytes)81*f65a228fSRobert Mustacchi fmemopen_write(FILE *iop, const char *buf, size_t nbytes)
82*f65a228fSRobert Mustacchi {
83*f65a228fSRobert Mustacchi size_t npos;
84*f65a228fSRobert Mustacchi fmemopen_t *fmp = _xdata(iop);
85*f65a228fSRobert Mustacchi
86*f65a228fSRobert Mustacchi if ((fmp->fmo_flags & FMO_F_APPEND) != 0) {
87*f65a228fSRobert Mustacchi /*
88*f65a228fSRobert Mustacchi * POSIX says that if append mode is in effect, we must always
89*f65a228fSRobert Mustacchi * seek to the logical size. This effectively is mimicking the
90*f65a228fSRobert Mustacchi * O_APPEND behavior.
91*f65a228fSRobert Mustacchi */
92*f65a228fSRobert Mustacchi fmp->fmo_pos = fmp->fmo_lsize;
93*f65a228fSRobert Mustacchi }
94*f65a228fSRobert Mustacchi
95*f65a228fSRobert Mustacchi if (nbytes == 0) {
96*f65a228fSRobert Mustacchi return (0);
97*f65a228fSRobert Mustacchi } else if (nbytes >= SSIZE_MAX) {
98*f65a228fSRobert Mustacchi errno = EINVAL;
99*f65a228fSRobert Mustacchi return (-1);
100*f65a228fSRobert Mustacchi }
101*f65a228fSRobert Mustacchi
102*f65a228fSRobert Mustacchi npos = fmp->fmo_pos + nbytes;
103*f65a228fSRobert Mustacchi if (npos < nbytes) {
104*f65a228fSRobert Mustacchi errno = EOVERFLOW;
105*f65a228fSRobert Mustacchi return (-1);
106*f65a228fSRobert Mustacchi } else if (npos > fmp->fmo_alloc) {
107*f65a228fSRobert Mustacchi nbytes = fmp->fmo_alloc - fmp->fmo_pos;
108*f65a228fSRobert Mustacchi }
109*f65a228fSRobert Mustacchi
110*f65a228fSRobert Mustacchi (void) memcpy(&fmp->fmo_buf[fmp->fmo_pos], buf, nbytes);
111*f65a228fSRobert Mustacchi fmp->fmo_pos += nbytes;
112*f65a228fSRobert Mustacchi
113*f65a228fSRobert Mustacchi if (fmp->fmo_pos > fmp->fmo_lsize) {
114*f65a228fSRobert Mustacchi fmp->fmo_lsize = fmp->fmo_pos;
115*f65a228fSRobert Mustacchi
116*f65a228fSRobert Mustacchi /*
117*f65a228fSRobert Mustacchi * POSIX distinguishes behavior for writing a NUL in these
118*f65a228fSRobert Mustacchi * streams. Basically if we are open for update and we are at
119*f65a228fSRobert Mustacchi * the end of the buffer, we don't place a NUL. Otherwise, we
120*f65a228fSRobert Mustacchi * always place one at the current position (or the end if we
121*f65a228fSRobert Mustacchi * were over the edge).
122*f65a228fSRobert Mustacchi */
123*f65a228fSRobert Mustacchi if (fmp->fmo_lsize < fmp->fmo_alloc) {
124*f65a228fSRobert Mustacchi fmp->fmo_buf[fmp->fmo_lsize] = '\0';
125*f65a228fSRobert Mustacchi } else if ((fmp->fmo_flags & FMO_F_APPEND) == 0) {
126*f65a228fSRobert Mustacchi fmp->fmo_buf[fmp->fmo_alloc - 1] = '\0';
127*f65a228fSRobert Mustacchi }
128*f65a228fSRobert Mustacchi }
129*f65a228fSRobert Mustacchi
130*f65a228fSRobert Mustacchi return (nbytes);
131*f65a228fSRobert Mustacchi }
132*f65a228fSRobert Mustacchi
133*f65a228fSRobert Mustacchi static off_t
fmemopen_seek(FILE * iop,off_t off,int whence)134*f65a228fSRobert Mustacchi fmemopen_seek(FILE *iop, off_t off, int whence)
135*f65a228fSRobert Mustacchi {
136*f65a228fSRobert Mustacchi fmemopen_t *fmp = _xdata(iop);
137*f65a228fSRobert Mustacchi size_t base, npos;
138*f65a228fSRobert Mustacchi
139*f65a228fSRobert Mustacchi switch (whence) {
140*f65a228fSRobert Mustacchi case SEEK_SET:
141*f65a228fSRobert Mustacchi base = 0;
142*f65a228fSRobert Mustacchi break;
143*f65a228fSRobert Mustacchi case SEEK_CUR:
144*f65a228fSRobert Mustacchi base = fmp->fmo_pos;
145*f65a228fSRobert Mustacchi break;
146*f65a228fSRobert Mustacchi case SEEK_END:
147*f65a228fSRobert Mustacchi base = fmp->fmo_lsize;
148*f65a228fSRobert Mustacchi break;
149*f65a228fSRobert Mustacchi default:
150*f65a228fSRobert Mustacchi errno = EINVAL;
151*f65a228fSRobert Mustacchi return (-1);
152*f65a228fSRobert Mustacchi }
153*f65a228fSRobert Mustacchi
154*f65a228fSRobert Mustacchi if (!memstream_seek(base, off, fmp->fmo_alloc, &npos)) {
155*f65a228fSRobert Mustacchi errno = EINVAL;
156*f65a228fSRobert Mustacchi return (-1);
157*f65a228fSRobert Mustacchi }
158*f65a228fSRobert Mustacchi fmp->fmo_pos = npos;
159*f65a228fSRobert Mustacchi
160*f65a228fSRobert Mustacchi return ((off_t)fmp->fmo_pos);
161*f65a228fSRobert Mustacchi }
162*f65a228fSRobert Mustacchi
163*f65a228fSRobert Mustacchi static void
fmemopen_free(fmemopen_t * fmp)164*f65a228fSRobert Mustacchi fmemopen_free(fmemopen_t *fmp)
165*f65a228fSRobert Mustacchi {
166*f65a228fSRobert Mustacchi if (fmp->fmo_buf != NULL &&
167*f65a228fSRobert Mustacchi (fmp->fmo_flags & FMO_F_USER_BUFFER) == 0) {
168*f65a228fSRobert Mustacchi free(fmp->fmo_buf);
169*f65a228fSRobert Mustacchi }
170*f65a228fSRobert Mustacchi
171*f65a228fSRobert Mustacchi free(fmp);
172*f65a228fSRobert Mustacchi }
173*f65a228fSRobert Mustacchi
174*f65a228fSRobert Mustacchi static int
fmemopen_close(FILE * iop)175*f65a228fSRobert Mustacchi fmemopen_close(FILE *iop)
176*f65a228fSRobert Mustacchi {
177*f65a228fSRobert Mustacchi fmemopen_t *fmp = _xdata(iop);
178*f65a228fSRobert Mustacchi fmemopen_free(fmp);
179*f65a228fSRobert Mustacchi _xunassoc(iop);
180*f65a228fSRobert Mustacchi return (0);
181*f65a228fSRobert Mustacchi }
182*f65a228fSRobert Mustacchi
183*f65a228fSRobert Mustacchi FILE *
fmemopen(void * _RESTRICT_KYWD buf,size_t size,const char * _RESTRICT_KYWD mode)184*f65a228fSRobert Mustacchi fmemopen(void *_RESTRICT_KYWD buf, size_t size,
185*f65a228fSRobert Mustacchi const char *_RESTRICT_KYWD mode)
186*f65a228fSRobert Mustacchi {
187*f65a228fSRobert Mustacchi int oflags, fflags, err;
188*f65a228fSRobert Mustacchi fmemopen_t *fmp;
189*f65a228fSRobert Mustacchi FILE *iop;
190*f65a228fSRobert Mustacchi
191*f65a228fSRobert Mustacchi if (size == 0 || mode == NULL) {
192*f65a228fSRobert Mustacchi errno = EINVAL;
193*f65a228fSRobert Mustacchi return (NULL);
194*f65a228fSRobert Mustacchi }
195*f65a228fSRobert Mustacchi
196*f65a228fSRobert Mustacchi if (_stdio_flags(mode, &oflags, &fflags) != 0) {
197*f65a228fSRobert Mustacchi /* errno set for us */
198*f65a228fSRobert Mustacchi return (NULL);
199*f65a228fSRobert Mustacchi }
200*f65a228fSRobert Mustacchi
201*f65a228fSRobert Mustacchi /*
202*f65a228fSRobert Mustacchi * buf is only allowed to be NULL if the '+' is specified. If the '+'
203*f65a228fSRobert Mustacchi * mode was specified, then we'll have fflags set to _IORW.
204*f65a228fSRobert Mustacchi */
205*f65a228fSRobert Mustacchi if (buf == NULL && fflags != _IORW) {
206*f65a228fSRobert Mustacchi errno = EINVAL;
207*f65a228fSRobert Mustacchi return (NULL);
208*f65a228fSRobert Mustacchi }
209*f65a228fSRobert Mustacchi
210*f65a228fSRobert Mustacchi if ((fmp = calloc(1, sizeof (fmemopen_t))) == NULL) {
211*f65a228fSRobert Mustacchi errno = ENOMEM;
212*f65a228fSRobert Mustacchi return (NULL);
213*f65a228fSRobert Mustacchi }
214*f65a228fSRobert Mustacchi
215*f65a228fSRobert Mustacchi if (buf == NULL) {
216*f65a228fSRobert Mustacchi fmp->fmo_buf = calloc(size, sizeof (uint8_t));
217*f65a228fSRobert Mustacchi if (fmp->fmo_buf == NULL) {
218*f65a228fSRobert Mustacchi errno = ENOMEM;
219*f65a228fSRobert Mustacchi goto cleanup;
220*f65a228fSRobert Mustacchi }
221*f65a228fSRobert Mustacchi } else {
222*f65a228fSRobert Mustacchi fmp->fmo_buf = buf;
223*f65a228fSRobert Mustacchi fmp->fmo_flags |= FMO_F_USER_BUFFER;
224*f65a228fSRobert Mustacchi }
225*f65a228fSRobert Mustacchi
226*f65a228fSRobert Mustacchi fmp->fmo_alloc = size;
227*f65a228fSRobert Mustacchi
228*f65a228fSRobert Mustacchi /*
229*f65a228fSRobert Mustacchi * Set the initial logical size and position depending on whether we're
230*f65a228fSRobert Mustacchi * using r(+), w(+), and a(+). The latter two are identified by O_TRUNC
231*f65a228fSRobert Mustacchi * and O_APPEND in oflags.
232*f65a228fSRobert Mustacchi */
233*f65a228fSRobert Mustacchi if ((oflags & O_APPEND) != 0) {
234*f65a228fSRobert Mustacchi fmp->fmo_pos = strnlen(fmp->fmo_buf, fmp->fmo_alloc);
235*f65a228fSRobert Mustacchi fmp->fmo_lsize = fmp->fmo_pos;
236*f65a228fSRobert Mustacchi fmp->fmo_flags |= FMO_F_APPEND;
237*f65a228fSRobert Mustacchi } else if ((oflags & O_TRUNC) != 0) {
238*f65a228fSRobert Mustacchi fmp->fmo_buf[0] = '\0';
239*f65a228fSRobert Mustacchi fmp->fmo_pos = 0;
240*f65a228fSRobert Mustacchi fmp->fmo_lsize = 0;
241*f65a228fSRobert Mustacchi } else {
242*f65a228fSRobert Mustacchi fmp->fmo_pos = 0;
243*f65a228fSRobert Mustacchi fmp->fmo_lsize = size;
244*f65a228fSRobert Mustacchi }
245*f65a228fSRobert Mustacchi
246*f65a228fSRobert Mustacchi iop = _findiop();
247*f65a228fSRobert Mustacchi if (iop == NULL) {
248*f65a228fSRobert Mustacchi goto cleanup;
249*f65a228fSRobert Mustacchi }
250*f65a228fSRobert Mustacchi
251*f65a228fSRobert Mustacchi #ifdef _LP64
252*f65a228fSRobert Mustacchi iop->_flag = (iop->_flag & ~_DEF_FLAG_MASK) | fflags;
253*f65a228fSRobert Mustacchi #else
254*f65a228fSRobert Mustacchi iop->_flag = fflags;
255*f65a228fSRobert Mustacchi #endif
256*f65a228fSRobert Mustacchi if (_xassoc(iop, fmemopen_read, fmemopen_write, fmemopen_seek,
257*f65a228fSRobert Mustacchi fmemopen_close, fmp) != 0) {
258*f65a228fSRobert Mustacchi goto cleanup;
259*f65a228fSRobert Mustacchi }
260*f65a228fSRobert Mustacchi
261*f65a228fSRobert Mustacchi SET_SEEKABLE(iop);
262*f65a228fSRobert Mustacchi
263*f65a228fSRobert Mustacchi return (iop);
264*f65a228fSRobert Mustacchi
265*f65a228fSRobert Mustacchi cleanup:
266*f65a228fSRobert Mustacchi err = errno;
267*f65a228fSRobert Mustacchi fmemopen_free(fmp);
268*f65a228fSRobert Mustacchi errno = err;
269*f65a228fSRobert Mustacchi return (NULL);
270*f65a228fSRobert Mustacchi }
271