-
Notifications
You must be signed in to change notification settings - Fork 271
/
istream-metawrap.c
145 lines (128 loc) · 4.16 KB
/
istream-metawrap.c
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
/* Copyright (c) 2007-2016 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "istream-private.h"
#include "istream-metawrap.h"
struct metawrap_istream {
struct istream_private istream;
metawrap_callback_t *callback;
void *context;
uoff_t start_offset, pending_seek;
bool in_metadata;
};
static int metadata_header_read(struct metawrap_istream *mstream)
{
char *line, *p;
while ((line = i_stream_read_next_line(mstream->istream.parent)) != NULL) {
if (*line == '\0') {
mstream->callback(NULL, NULL, mstream->context);
return 1;
}
p = strchr(line, ':');
if (p == NULL) {
io_stream_set_error(&mstream->istream.iostream,
"Metadata header line is missing ':'");
mstream->istream.istream.stream_errno = EINVAL;
return -1;
}
*p++ = '\0';
mstream->callback(line, p, mstream->context);
}
if (mstream->istream.parent->eof) {
if (mstream->istream.parent->stream_errno != 0) {
mstream->istream.istream.stream_errno =
mstream->istream.parent->stream_errno;
} else {
io_stream_set_error(&mstream->istream.iostream,
"Metadata header is missing ending line");
mstream->istream.istream.stream_errno = EINVAL;
return -1;
}
mstream->istream.istream.eof = TRUE;
return -1;
}
i_assert(!mstream->istream.parent->blocking);
return 0;
}
static ssize_t i_stream_metawrap_read(struct istream_private *stream)
{
struct metawrap_istream *mstream = (struct metawrap_istream *)stream;
int ret;
i_stream_seek(stream->parent, mstream->start_offset +
stream->istream.v_offset);
if (mstream->in_metadata) {
ret = metadata_header_read(mstream);
i_assert(stream->istream.v_offset == 0);
mstream->start_offset = stream->parent->v_offset;
if (ret <= 0)
return ret;
/* this stream is kind of silently skipping over the metadata */
stream->abs_start_offset += mstream->start_offset;
mstream->in_metadata = FALSE;
if (mstream->pending_seek != 0) {
i_stream_seek(&stream->istream, mstream->pending_seek);
return i_stream_read(&stream->istream);
}
}
/* after metadata header it's all just passthrough */
return i_stream_read_copy_from_parent(&stream->istream);
}
static void
i_stream_metawrap_seek(struct istream_private *stream,
uoff_t v_offset, bool mark ATTR_UNUSED)
{
struct metawrap_istream *mstream = (struct metawrap_istream *)stream;
if (!mstream->in_metadata) {
/* already read through metadata. we can skip directly. */
stream->istream.v_offset = v_offset;
mstream->pending_seek = 0;
} else {
/* we need to read through the metadata first */
mstream->pending_seek = v_offset;
stream->istream.v_offset = 0;
}
stream->skip = stream->pos = 0;
}
static int i_stream_metawrap_stat(struct istream_private *stream, bool exact)
{
struct metawrap_istream *mstream = (struct metawrap_istream *)stream;
const struct stat *st;
int ret;
if (i_stream_stat(stream->parent, exact, &st) < 0) {
stream->istream.stream_errno = stream->parent->stream_errno;
return -1;
}
stream->statbuf = *st;
if (mstream->in_metadata) {
ret = i_stream_read(&stream->istream);
if (ret < 0 && stream->istream.stream_errno != 0)
return -1;
if (ret == 0) {
stream->statbuf.st_size = -1;
return 0;
}
}
i_assert((uoff_t)stream->statbuf.st_size >= mstream->start_offset);
stream->statbuf.st_size -= mstream->start_offset;
return 0;
}
struct istream *
i_stream_create_metawrap(struct istream *input,
metawrap_callback_t *callback, void *context)
{
struct metawrap_istream *mstream;
mstream = i_new(struct metawrap_istream, 1);
mstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
mstream->istream.read = i_stream_metawrap_read;
mstream->istream.seek = i_stream_metawrap_seek;
mstream->istream.stat = input->seekable ? i_stream_metawrap_stat : NULL;
/* we can't set abs_start_offset early enough so that it would get
passed to our child istreams. */
mstream->istream.istream.readable_fd = FALSE;
mstream->istream.istream.blocking = input->blocking;
mstream->istream.istream.seekable = input->seekable;
mstream->in_metadata = TRUE;
mstream->callback = callback;
mstream->context = context;
return i_stream_create(&mstream->istream, input,
i_stream_get_fd(input));
}