1 /*
2 * Copyright (c) 2016, EMC / Isilon Storage Division
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
15 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <sys/fcntl.h>
28
29 #include <errno.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32
33 #include "local.h"
34
35 struct fopencookie_thunk {
36 void *foc_cookie;
37 cookie_io_functions_t foc_io;
38 };
39
40 static int _fopencookie_read(void *, char *, int);
41 static int _fopencookie_write(void *, const char *, int);
42 static fpos_t _fopencookie_seek(void *, fpos_t, int);
43 static int _fopencookie_close(void *);
44
45 FILE *
fopencookie(void * cookie,const char * mode,cookie_io_functions_t io_funcs)46 fopencookie(void *cookie, const char *mode, cookie_io_functions_t io_funcs)
47 {
48 int (*readfn)(void *, char *, int);
49 int (*writefn)(void *, const char *, int);
50 struct fopencookie_thunk *thunk;
51 FILE *fp;
52 int flags, oflags;
53
54 if ((flags = __sflags(mode, &oflags)) == 0)
55 return (NULL);
56
57 thunk = malloc(sizeof(*thunk));
58 if (thunk == NULL)
59 return (NULL);
60
61 thunk->foc_cookie = cookie;
62 thunk->foc_io = io_funcs;
63
64 readfn = _fopencookie_read;
65 writefn = _fopencookie_write;
66 if (flags == __SWR)
67 readfn = NULL;
68 else if (flags == __SRD)
69 writefn = NULL;
70
71 fp = funopen(thunk, readfn, writefn, _fopencookie_seek,
72 _fopencookie_close);
73 if (fp == NULL) {
74 free(thunk);
75 return (NULL);
76 }
77
78 if ((oflags & O_APPEND) != 0)
79 fp->_flags |= __SAPP;
80
81 return (fp);
82 }
83
84 static int
_fopencookie_read(void * cookie,char * buf,int size)85 _fopencookie_read(void *cookie, char *buf, int size)
86 {
87 struct fopencookie_thunk *thunk;
88
89 thunk = cookie;
90
91 /* Reads from a stream with NULL read return EOF. */
92 if (thunk->foc_io.read == NULL)
93 return (0);
94
95 return ((int)thunk->foc_io.read(thunk->foc_cookie, buf, (size_t)size));
96 }
97
98 static int
_fopencookie_write(void * cookie,const char * buf,int size)99 _fopencookie_write(void *cookie, const char *buf, int size)
100 {
101 struct fopencookie_thunk *thunk;
102
103 thunk = cookie;
104
105 /* Writes to a stream with NULL write discard data. */
106 if (thunk->foc_io.write == NULL)
107 return (size);
108
109 return ((int)thunk->foc_io.write(thunk->foc_cookie, buf,
110 (size_t)size));
111 }
112
113 static fpos_t
_fopencookie_seek(void * cookie,fpos_t offset,int whence)114 _fopencookie_seek(void *cookie, fpos_t offset, int whence)
115 {
116 struct fopencookie_thunk *thunk;
117 off64_t off64;
118 int res;
119
120 switch (whence) {
121 case SEEK_SET:
122 case SEEK_CUR:
123 case SEEK_END:
124 break;
125 default:
126 /* fopencookie(3) only allows these three seek modes. */
127 errno = EINVAL;
128 return (-1);
129 }
130
131 thunk = cookie;
132
133 /*
134 * If seek is NULL, it is not possible to perform seek operations on
135 * the stream.
136 */
137 if (thunk->foc_io.seek == NULL) {
138 errno = ENOTSUP;
139 return (-1);
140 }
141
142 off64 = (off64_t)offset;
143 res = thunk->foc_io.seek(thunk->foc_cookie, &off64, whence);
144 if (res < 0)
145 return (res);
146
147 return ((fpos_t)off64);
148 }
149
150 static int
_fopencookie_close(void * cookie)151 _fopencookie_close(void *cookie)
152 {
153 struct fopencookie_thunk *thunk;
154 int ret, serrno;
155
156 ret = 0;
157 thunk = cookie;
158 if (thunk->foc_io.close != NULL)
159 ret = thunk->foc_io.close(thunk->foc_cookie);
160
161 serrno = errno;
162 free(thunk);
163 errno = serrno;
164 return (ret);
165 }
166