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