summaryrefslogtreecommitdiff
path: root/libmailutils/stream/temp_stream.c
blob: 03600fffbcd20bd50acf25c6f8c36fdf0ad042a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/* GNU Mailutils -- a suite of utilities for electronic mail
   Copyright (C) 2020-2021 Free Software Foundation, Inc.

   This library is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */

/* Implementation of temp_stream.

   Temp_stream combines the functionality of memory and temp_file streams.
   Streams of that type function as memory streams until their size reaches
   a preconfigured threshold value.  Once it is reached, the stream storage
   is automatically converted to temporary file, and all data written so far
   are transferred to the new storage.  If the temporary file cannot be
   created, the stream continues to operate in memory-based mode.
   
   The stream is created using the following call:
   
      int mu_temp_stream_create (mu_stream_t *pstream, size_t threshold)

   If threshold is 0, the threshold value is first looked up in the
   environment variable MU_TEMP_FILE_THRESHOLD (which should contain
   a string suitable for input to mu_strtosize).  If it is not set or
   unparsable, the mu_temp_file_threshold_size global is used instead.

   Two special values of MU_TEMP_FILE_THRESHOLD alter the behavior of
   mu_temp_stream_create:

     "0"    -  the function creates a pure tempfile-based stream
               (equivalent to mu_temp_file_stream_create).
     "inf"  -  the function returns a pure memory-based stream
               (equivalent to mu_memory_stream_create).
 */
#include <stdlib.h>
#include <errno.h>
#include <mailutils/stream.h>
#include <mailutils/sys/temp_stream.h>
#include <mailutils/cstr.h>
#include <mailutils/diag.h>
#include <mailutils/debug.h>
#include <mailutils/errno.h>

static int
temp_stream_write (struct _mu_stream *str, const char *buf, size_t size,
		   size_t *ret_size)
{
  struct _mu_temp_stream *ts = (struct _mu_temp_stream *)str;
  
  if (ts->s.mem.offset + size > ts->max_size)
    {
      int rc;
      mu_stream_t temp_file;
      rc = mu_temp_file_stream_create (&temp_file, NULL, 0);
      if (rc == 0)
	{
	  if (ts->s.mem.ptr == NULL)
	    rc = 0;
	  else
	    {
	      size_t s = 0;

	      while (s < ts->s.mem.size)
		{
		  size_t n = ts->s.mem.size - s;
		  size_t wrn;
		  
		  rc = temp_file->write (temp_file, ts->s.mem.ptr + s, n, &wrn);
		  if (rc)
		    break;
		  s += wrn;
		}
	      
	      if (rc == 0)
		{
		  mu_off_t res;
		  rc = temp_file->seek (temp_file, str->offset, &res);
		}
	    }
	  
	  if (rc == 0)
	    {
	      /* Preserve the necessary stream data */
	      temp_file->ref_count = str->ref_count;
	      if (temp_file->buftype != mu_buffer_none)
		free (temp_file->buffer);
	      temp_file->buftype = str->buftype;
	      temp_file->buffer = str->buffer;
	      temp_file->level = str->level;
	      temp_file->pos = str->pos;

	      temp_file->statmask = str->statmask;
	      temp_file->statbuf = str->statbuf;

	      /* Deinitialize previous stream backend */
	      ts->s.stream.done (str);

	      /* Replace it with the newly created one. */
	      memcpy (&ts->s.file, temp_file, sizeof (ts->s.file));

	      /* Reclaim the memory used by the stream object */
	      free (temp_file);

	      /* Write data to the new stream. */
	      return ts->s.stream.write (str, buf, size, ret_size);
	    }
	}
      else
	{
	  mu_diag_funcall (MU_DIAG_WARNING, "mu_temp_file_stream_create",
			   NULL, rc);
	  /* Switch to plain memory stream mode */
	  ts->s.stream.write = ts->saved_write;
	}
    }

  return ts->saved_write (str, buf, size, ret_size);
}

size_t mu_temp_file_threshold_size = 4096;

int
mu_temp_stream_create (mu_stream_t *pstream, size_t max_size)
{
  int rc;
  mu_stream_t stream;
  struct _mu_temp_stream *str;

  if (max_size == 0)
    {
      char *s;
      if ((s = getenv ("MU_TEMP_FILE_THRESHOLD")) != NULL)
	{
	  char *p;

	  if (strcmp(p, "inf") == 0)
	    return mu_memory_stream_create (&stream, MU_STREAM_RDWR);
	  
	  rc = mu_strtosize (s, &p, &max_size);
	  if (rc == 0)
	    {
	      if (max_size == 0)
		return mu_temp_file_stream_create (pstream, NULL, 0);
	    }
	  else
	    mu_debug (MU_DEBCAT_STREAM, MU_DEBUG_ERROR,
		      ("failed parsing MU_TEMP_FILE_THRESHOLD value: %s near %s",
		       mu_strerror (rc), p));
	}
      if (max_size == 0)
	max_size = mu_temp_file_threshold_size;
    }
  
  rc = mu_memory_stream_create (&stream, MU_STREAM_RDWR);
  if (rc)
    return rc;

  str = realloc (stream, sizeof (*str));
  if (!str)
    {
      mu_stream_destroy (&stream);
      return ENOMEM;
    }

  str->max_size = max_size;
  str->saved_write = str->s.stream.write;
  str->s.stream.write = temp_stream_write;

  *pstream = (mu_stream_t) str;
  return rc;
}

Return to:

Send suggestions and report system problems to the System administrator.