xref: /titanic_53/usr/src/lib/libc/port/stdio/fmemopen.c (revision f65a228f854974d51b13928c21fa0ae281aa80af)
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