1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <string.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25
26 #include <mailutils/types.h>
27 #include <mailutils/alloc.h>
28 #include <mailutils/error.h>
29 #include <mailutils/errno.h>
30 #include <mailutils/nls.h>
31 #include <mailutils/stream.h>
32 #include <mailutils/sys/stream.h>
33 #include <mailutils/sys/mapfile_stream.h>
34
35 #ifdef _POSIX_MAPPED_FILES
36 #include <sys/mman.h>
37
38 #ifndef MAP_FAILED
39 # define MAP_FAILED (void*)-1
40 #endif
41
42 static void
_mapfile_done(mu_stream_t stream)43 _mapfile_done (mu_stream_t stream)
44 {
45 struct _mu_mapfile_stream *mfs = (struct _mu_mapfile_stream *)stream;
46
47 if (mfs->ptr != MAP_FAILED)
48 {
49 if (mfs->ptr)
50 munmap (mfs->ptr, mfs->size);
51 close (mfs->fd);
52 }
53 free (mfs->filename);
54 }
55
56 static int
_mapfile_read(mu_stream_t stream,char * optr,size_t osize,size_t * nbytes)57 _mapfile_read (mu_stream_t stream, char *optr, size_t osize,
58 size_t *nbytes)
59 {
60 struct _mu_mapfile_stream *mfs = (struct _mu_mapfile_stream *) stream;
61 size_t n = 0;
62
63 if (mfs->ptr == MAP_FAILED)
64 return EINVAL;
65
66 if (mfs->offset < (mu_off_t) mfs->size)
67 {
68 n = ((mfs->offset + osize) > mfs->size) ?
69 mfs->size - mfs->offset : osize;
70 memcpy (optr, mfs->ptr + mfs->offset, n);
71 mfs->offset += n;
72 }
73
74 if (nbytes)
75 *nbytes = n;
76 return 0;
77 }
78
79 static int
_mapfile_write(mu_stream_t stream,const char * iptr,size_t isize,size_t * nbytes)80 _mapfile_write (mu_stream_t stream, const char *iptr, size_t isize,
81 size_t *nbytes)
82 {
83 struct _mu_mapfile_stream *mfs = (struct _mu_mapfile_stream *) stream;
84
85 if (mfs->ptr == MAP_FAILED)
86 return EINVAL;
87
88 if (!(mfs->flags & PROT_WRITE))
89 return EACCES;
90
91 if (mfs->size < (mfs->offset + isize))
92 {
93 if (mfs->ptr && munmap (mfs->ptr, mfs->size) != 0)
94 {
95 int err = errno;
96 mfs->ptr = MAP_FAILED;
97 close (mfs->fd);
98 return err;
99 }
100 if (ftruncate (mfs->fd, mfs->offset + isize) != 0)
101 return errno;
102 mfs->ptr = mmap (NULL, mfs->offset + isize, mfs->flags,
103 MAP_SHARED, mfs->fd, 0);
104 if (mfs->ptr == MAP_FAILED)
105 {
106 int err = errno;
107 close (mfs->fd);
108 return err;
109 }
110 mfs->size = mfs->offset + isize;
111 }
112
113 if (isize)
114 {
115 memcpy (mfs->ptr + mfs->offset, iptr, isize);
116 mfs->offset += isize;
117 }
118
119 if (nbytes)
120 *nbytes = isize;
121 return 0;
122 }
123
124 static int
_mapfile_truncate(mu_stream_t stream,mu_off_t len)125 _mapfile_truncate (mu_stream_t stream, mu_off_t len)
126 {
127 struct _mu_mapfile_stream *mfs = (struct _mu_mapfile_stream *) stream;
128
129 if (mfs->ptr == MAP_FAILED)
130 return EINVAL;
131 /* Remap. */
132 if (mfs->ptr && munmap (mfs->ptr, mfs->size) != 0)
133 {
134 int err = errno;
135 mfs->ptr = MAP_FAILED;
136 close (mfs->fd);
137 return err;
138 }
139 if (ftruncate (mfs->fd, len) != 0)
140 return errno;
141 mfs->ptr = len ? mmap (NULL, len, mfs->flags, MAP_SHARED, mfs->fd, 0) : NULL;
142 if (mfs->ptr == MAP_FAILED)
143 {
144 int err = errno;
145 close (mfs->fd);
146 return err;
147 }
148 mfs->size = len;
149 return 0;
150 }
151
152 static int
_mapfile_size(mu_stream_t stream,mu_off_t * psize)153 _mapfile_size (mu_stream_t stream, mu_off_t *psize)
154 {
155 struct _mu_mapfile_stream *mfs = (struct _mu_mapfile_stream *) stream;
156 struct stat stbuf;
157 int err = 0;
158
159 if (mfs->ptr == MAP_FAILED)
160 return EINVAL;
161 if (mfs->ptr && (mfs->flags & PROT_WRITE))
162 msync (mfs->ptr, mfs->size, MS_SYNC);
163 if (fstat (mfs->fd, &stbuf) != 0)
164 return errno;
165 if (mfs->size != (size_t) stbuf.st_size)
166 {
167 if (mfs->ptr)
168 err = munmap (mfs->ptr, mfs->size);
169 if (err == 0)
170 {
171 mfs->size = stbuf.st_size;
172 if (mfs->size)
173 {
174 mfs->ptr = mmap (NULL, mfs->size, mfs->flags, MAP_SHARED,
175 mfs->fd, 0);
176 if (mfs->ptr == MAP_FAILED)
177 err = errno;
178 }
179 else
180 mfs->ptr = NULL;
181 }
182 else
183 err = errno;
184 }
185 if (err != 0)
186 {
187 mfs->ptr = MAP_FAILED;
188 close (mfs->fd);
189 mfs->fd = -1;
190 }
191 else
192 {
193 if (psize)
194 *psize = stbuf.st_size;
195 }
196 return err;
197 }
198
199 static int
_mapfile_flush(mu_stream_t stream)200 _mapfile_flush (mu_stream_t stream)
201 {
202 struct _mu_mapfile_stream *mfs = (struct _mu_mapfile_stream *) stream;
203 if (mfs->ptr != MAP_FAILED && mfs->ptr != NULL && (mfs->flags & PROT_WRITE))
204 return msync (mfs->ptr, mfs->size, MS_SYNC);
205 return 0;
206 }
207
208 static int
_mapfile_ioctl(struct _mu_stream * str,int code,int opcode,void * ptr)209 _mapfile_ioctl (struct _mu_stream *str, int code, int opcode, void *ptr)
210 {
211 struct _mu_mapfile_stream *mfs = (struct _mu_mapfile_stream *) str;
212
213 switch (code)
214 {
215 case MU_IOCTL_TRANSPORT:
216 if (!ptr)
217 return EINVAL;
218 else
219 {
220 mu_transport_t *ptrans = ptr;
221 switch (opcode)
222 {
223 case MU_IOCTL_OP_GET:
224 ptrans[0] = (mu_transport_t) (intptr_t) mfs->fd;
225 ptrans[1] = NULL;
226 break;
227 case MU_IOCTL_OP_SET:
228 return ENOSYS;
229 default:
230 return EINVAL;
231 }
232 }
233 break;
234
235 case MU_IOCTL_TRANSPORT_BUFFER:
236 if (!ptr)
237 return EINVAL;
238 else
239 {
240 struct mu_buffer_query *qp = ptr;
241 switch (opcode)
242 {
243 case MU_IOCTL_OP_GET:
244 return mu_stream_get_buffer (str, qp);
245 case MU_IOCTL_OP_SET:
246 return mu_stream_set_buffer (str, qp->buftype, qp->bufsize);
247 default:
248 return EINVAL;
249 }
250 }
251 break;
252
253 default:
254 return ENOSYS;
255 }
256 return 0;
257 }
258
259 static int
_mapfile_close(mu_stream_t stream)260 _mapfile_close (mu_stream_t stream)
261 {
262 struct _mu_mapfile_stream *mfs = (struct _mu_mapfile_stream *) stream;
263 int err = 0;
264
265 if (mfs->ptr != MAP_FAILED)
266 {
267 if (mfs->ptr && munmap (mfs->ptr, mfs->size) != 0)
268 err = errno;
269 if (close (mfs->fd) != 0)
270 err = errno;
271 mfs->ptr = MAP_FAILED;
272 mfs->fd = -1;
273 }
274 return err;
275 }
276
277 static int
_mapfile_open(mu_stream_t stream)278 _mapfile_open (mu_stream_t stream)
279 {
280 struct _mu_mapfile_stream *mfs = (struct _mu_mapfile_stream *) stream;
281 int mflag, flg;
282 struct stat st;
283 char *filename = mfs->filename;
284 int flags;
285
286 mu_stream_get_flags (stream, &flags);
287
288 /* Close any previous file. */
289 if (mfs->ptr != MAP_FAILED)
290 {
291 if (mfs->ptr)
292 munmap (mfs->ptr, mfs->size);
293 mfs->ptr = MAP_FAILED;
294 }
295 if (mfs->fd != -1)
296 {
297 close (mfs->fd);
298 mfs->fd = -1;
299 }
300 /* Map the flags to the system equivalent */
301 if ((flags & MU_STREAM_RDWR) == MU_STREAM_RDWR)
302 {
303 mflag = PROT_READ | PROT_WRITE;
304 flg = O_RDWR;
305 }
306 else if (flags & MU_STREAM_WRITE)
307 {
308 mflag = PROT_WRITE;
309 flg = O_RDWR;
310 }
311 else if (flags & MU_STREAM_CREAT)
312 return ENOSYS;
313 else /* default */
314 {
315 mflag = PROT_READ;
316 flg = O_RDONLY;
317 }
318
319 mfs->fd = open (filename, flg);
320 if (mfs->fd < 0)
321 return errno;
322 if (fstat (mfs->fd, &st) != 0)
323 {
324 int err = errno;
325 close (mfs->fd);
326 return err;
327 }
328 mfs->size = st.st_size;
329 if (mfs->size)
330 {
331 mfs->ptr = mmap (NULL, mfs->size, mflag, MAP_SHARED, mfs->fd, 0);
332 if (mfs->ptr == MAP_FAILED)
333 {
334 int err = errno;
335 close (mfs->fd);
336 mfs->ptr = MAP_FAILED;
337 return err;
338 }
339 }
340 else
341 mfs->ptr = NULL;
342 mfs->flags = mflag;
343 return 0;
344 }
345
346 static int
_mapfile_seek(struct _mu_stream * str,mu_off_t off,mu_off_t * presult)347 _mapfile_seek (struct _mu_stream *str, mu_off_t off, mu_off_t *presult)
348 {
349 struct _mu_mapfile_stream *mfs = (struct _mu_mapfile_stream *) str;
350
351 if (off < 0 || off > mfs->size)
352 return ESPIPE;
353 mfs->offset = off;
354 *presult = off;
355 return 0;
356 }
357
358 #endif /* _POSIX_MAPPED_FILES */
359
360 int
mu_mapfile_stream_create(mu_stream_t * pstream,const char * filename,int flags)361 mu_mapfile_stream_create (mu_stream_t *pstream, const char *filename,
362 int flags)
363 {
364 #ifndef _POSIX_MAPPED_FILES
365 return ENOSYS;
366 #else
367 int rc;
368 mu_stream_t stream;
369 struct _mu_mapfile_stream *str =
370 (struct _mu_mapfile_stream *) _mu_stream_create (sizeof (*str),
371 flags | MU_STREAM_SEEK);
372 if (!str)
373 return ENOMEM;
374
375 str->filename = mu_strdup (filename);
376 if (!str->filename)
377 {
378 free (str);
379 return ENOMEM;
380 }
381 str->fd = -1;
382 str->ptr = MAP_FAILED;
383
384 str->stream.open = _mapfile_open;
385 str->stream.close = _mapfile_close;
386 str->stream.ctl = _mapfile_ioctl;
387 str->stream.read = _mapfile_read;
388 str->stream.write = _mapfile_write;
389 str->stream.truncate = _mapfile_truncate;
390 str->stream.size = _mapfile_size;
391 str->stream.flush = _mapfile_flush;
392 str->stream.done = _mapfile_done;
393 str->stream.seek = _mapfile_seek;
394
395 stream = (mu_stream_t) str;
396 rc = mu_stream_open (stream);
397 if (rc)
398 mu_stream_destroy (&stream);
399 else
400 *pstream = stream;
401 return rc;
402 #endif
403 }
404
405
406