1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2020-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 /* Implementation of temp_stream.
18
19 Temp_stream combines the functionality of memory and temp_file streams.
20 Streams of that type function as memory streams until their size reaches
21 a preconfigured threshold value. Once it is reached, the stream storage
22 is automatically converted to temporary file, and all data written so far
23 are transferred to the new storage. If the temporary file cannot be
24 created, the stream continues to operate in memory-based mode.
25
26 The stream is created using the following call:
27
28 int mu_temp_stream_create (mu_stream_t *pstream, size_t threshold)
29
30 If threshold is 0, the threshold value is first looked up in the
31 environment variable MU_TEMP_FILE_THRESHOLD (which should contain
32 a string suitable for input to mu_strtosize). If it is not set or
33 unparsable, the mu_temp_file_threshold_size global is used instead.
34
35 Two special values of MU_TEMP_FILE_THRESHOLD alter the behavior of
36 mu_temp_stream_create:
37
38 "0" - the function creates a pure tempfile-based stream
39 (equivalent to mu_temp_file_stream_create).
40 "inf" - the function returns a pure memory-based stream
41 (equivalent to mu_memory_stream_create).
42 */
43 #include <stdlib.h>
44 #include <errno.h>
45 #include <mailutils/stream.h>
46 #include <mailutils/sys/temp_stream.h>
47 #include <mailutils/cstr.h>
48 #include <mailutils/diag.h>
49 #include <mailutils/debug.h>
50 #include <mailutils/errno.h>
51
52 static int
temp_stream_write(struct _mu_stream * str,const char * buf,size_t size,size_t * ret_size)53 temp_stream_write (struct _mu_stream *str, const char *buf, size_t size,
54 size_t *ret_size)
55 {
56 struct _mu_temp_stream *ts = (struct _mu_temp_stream *)str;
57
58 if (ts->s.mem.offset + size > ts->max_size)
59 {
60 int rc;
61 mu_stream_t temp_file;
62 rc = mu_temp_file_stream_create (&temp_file, NULL, 0);
63 if (rc == 0)
64 {
65 if (ts->s.mem.ptr == NULL)
66 rc = 0;
67 else
68 {
69 size_t s = 0;
70
71 while (s < ts->s.mem.size)
72 {
73 size_t n = ts->s.mem.size - s;
74 size_t wrn;
75
76 rc = temp_file->write (temp_file, ts->s.mem.ptr + s, n, &wrn);
77 if (rc)
78 break;
79 s += wrn;
80 }
81
82 if (rc == 0)
83 {
84 mu_off_t res;
85 rc = temp_file->seek (temp_file, str->offset, &res);
86 }
87 }
88
89 if (rc == 0)
90 {
91 /* Preserve the necessary stream data */
92 temp_file->ref_count = str->ref_count;
93 if (temp_file->buftype != mu_buffer_none)
94 free (temp_file->buffer);
95 temp_file->buftype = str->buftype;
96 temp_file->buffer = str->buffer;
97 temp_file->level = str->level;
98 temp_file->pos = str->pos;
99
100 temp_file->statmask = str->statmask;
101 temp_file->statbuf = str->statbuf;
102
103 /* Deinitialize previous stream backend */
104 ts->s.stream.done (str);
105
106 /* Replace it with the newly created one. */
107 memcpy (&ts->s.file, temp_file, sizeof (ts->s.file));
108
109 /* Reclaim the memory used by the stream object */
110 free (temp_file);
111
112 /* Write data to the new stream. */
113 return ts->s.stream.write (str, buf, size, ret_size);
114 }
115 }
116 else
117 {
118 mu_diag_funcall (MU_DIAG_WARNING, "mu_temp_file_stream_create",
119 NULL, rc);
120 /* Switch to plain memory stream mode */
121 ts->s.stream.write = ts->saved_write;
122 }
123 }
124
125 return ts->saved_write (str, buf, size, ret_size);
126 }
127
128 size_t mu_temp_file_threshold_size = 4096;
129
130 int
mu_temp_stream_create(mu_stream_t * pstream,size_t max_size)131 mu_temp_stream_create (mu_stream_t *pstream, size_t max_size)
132 {
133 int rc;
134 mu_stream_t stream;
135 struct _mu_temp_stream *str;
136
137 if (max_size == 0)
138 {
139 char *s;
140 if ((s = getenv ("MU_TEMP_FILE_THRESHOLD")) != NULL)
141 {
142 char *p;
143
144 if (strcmp(p, "inf") == 0)
145 return mu_memory_stream_create (&stream, MU_STREAM_RDWR);
146
147 rc = mu_strtosize (s, &p, &max_size);
148 if (rc == 0)
149 {
150 if (max_size == 0)
151 return mu_temp_file_stream_create (pstream, NULL, 0);
152 }
153 else
154 mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
155 ("failed parsing MU_TEMP_FILE_THRESHOLD value: %s near %s",
156 mu_strerror (rc), p));
157 }
158 if (max_size == 0)
159 max_size = mu_temp_file_threshold_size;
160 }
161
162 rc = mu_memory_stream_create (&stream, MU_STREAM_RDWR);
163 if (rc)
164 return rc;
165
166 str = realloc (stream, sizeof (*str));
167 if (!str)
168 {
169 mu_stream_destroy (&stream);
170 return ENOMEM;
171 }
172
173 str->max_size = max_size;
174 str->saved_write = str->s.stream.write;
175 str->s.stream.write = temp_stream_write;
176
177 *pstream = (mu_stream_t) str;
178 return rc;
179 }
180
181