1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3 /* @UNSAFE: whole file */
4
5 #include "lib.h"
6 #include "ioloop.h"
7 #include "istream-file-private.h"
8 #include "net.h"
9
10 #include <time.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <sys/stat.h>
14
i_stream_file_close(struct iostream_private * stream,bool close_parent ATTR_UNUSED)15 void i_stream_file_close(struct iostream_private *stream,
16 bool close_parent ATTR_UNUSED)
17 {
18 struct istream_private *_stream =
19 container_of(stream, struct istream_private, iostream);
20 struct file_istream *fstream =
21 container_of(_stream, struct file_istream, istream);
22
23 if (fstream->autoclose_fd && _stream->fd != -1) {
24 /* Ignore ECONNRESET because we don't really care about it here,
25 as we are closing the socket down in any case. There might be
26 unsent data but nothing we can do about that. */
27 if (unlikely(close(_stream->fd) < 0 && errno != ECONNRESET)) {
28 i_error("file_istream.close(%s) failed: %m",
29 i_stream_get_name(&_stream->istream));
30 }
31 }
32 _stream->fd = -1;
33 }
34
i_stream_file_open(struct istream_private * stream)35 static int i_stream_file_open(struct istream_private *stream)
36 {
37 const char *path = i_stream_get_name(&stream->istream);
38
39 stream->fd = open(path, O_RDONLY);
40 if (stream->fd == -1) {
41 io_stream_set_error(&stream->iostream,
42 "open(%s) failed: %m", path);
43 stream->istream.stream_errno = errno;
44 return -1;
45 }
46 return 0;
47 }
48
i_stream_file_read(struct istream_private * stream)49 ssize_t i_stream_file_read(struct istream_private *stream)
50 {
51 struct file_istream *fstream =
52 container_of(stream, struct file_istream, istream);
53 uoff_t offset;
54 size_t size;
55 ssize_t ret;
56
57 if (!i_stream_try_alloc(stream, 1, &size))
58 return -2;
59
60 if (stream->fd == -1) {
61 if (i_stream_file_open(stream) < 0)
62 return -1;
63 i_assert(stream->fd != -1);
64 }
65
66 offset = stream->istream.v_offset + (stream->pos - stream->skip);
67
68 if (fstream->file) {
69 ret = pread(stream->fd, stream->w_buffer + stream->pos,
70 size, offset);
71 } else if (fstream->seen_eof) {
72 /* don't try to read() again. EOF from keyboard (^D)
73 requires this to work right. */
74 ret = 0;
75 } else {
76 ret = read(stream->fd, stream->w_buffer + stream->pos,
77 size);
78 }
79
80 if (ret == 0) {
81 /* EOF */
82 stream->istream.eof = TRUE;
83 fstream->seen_eof = TRUE;
84 return -1;
85 }
86
87 if (unlikely(ret < 0)) {
88 if ((errno == EINTR || errno == EAGAIN) &&
89 !stream->istream.blocking) {
90 ret = 0;
91 } else {
92 i_assert(errno != 0);
93 /* if we get EBADF for a valid fd, it means something's
94 really wrong and we'd better just crash. */
95 i_assert(errno != EBADF);
96 if (fstream->file) {
97 io_stream_set_error(&stream->iostream,
98 "pread(size=%zu offset=%"PRIuUOFF_T") failed: %m",
99 size, offset);
100 } else {
101 io_stream_set_error(&stream->iostream,
102 "read(size=%zu) failed: %m",
103 size);
104 }
105 stream->istream.stream_errno = errno;
106 return -1;
107 }
108 }
109
110 if (ret > 0 && fstream->skip_left > 0) {
111 i_assert(!fstream->file);
112 i_assert(stream->skip == stream->pos);
113
114 if (fstream->skip_left >= (size_t)ret) {
115 fstream->skip_left -= ret;
116 ret = 0;
117 } else {
118 ret -= fstream->skip_left;
119 stream->pos += fstream->skip_left;
120 stream->skip += fstream->skip_left;
121 fstream->skip_left = 0;
122 }
123 }
124
125 stream->pos += ret;
126 i_assert(ret != 0 || !fstream->file);
127 i_assert(ret != -1);
128 return ret;
129 }
130
i_stream_file_seek(struct istream_private * stream,uoff_t v_offset,bool mark ATTR_UNUSED)131 static void i_stream_file_seek(struct istream_private *stream, uoff_t v_offset,
132 bool mark ATTR_UNUSED)
133 {
134 struct file_istream *fstream =
135 container_of(stream, struct file_istream, istream);
136
137 if (!stream->istream.seekable) {
138 if (v_offset < stream->istream.v_offset)
139 i_panic("stream doesn't support seeking backwards");
140 fstream->skip_left += v_offset - stream->istream.v_offset;
141 }
142
143 stream->istream.v_offset = v_offset;
144 stream->skip = stream->pos = 0;
145 fstream->seen_eof = FALSE;
146 }
147
i_stream_file_sync(struct istream_private * stream)148 static void i_stream_file_sync(struct istream_private *stream)
149 {
150 if (!stream->istream.seekable) {
151 /* can't do anything or data would be lost */
152 return;
153 }
154
155 stream->skip = stream->pos = 0;
156 stream->istream.eof = FALSE;
157 }
158
159 static int
i_stream_file_stat(struct istream_private * stream,bool exact ATTR_UNUSED)160 i_stream_file_stat(struct istream_private *stream, bool exact ATTR_UNUSED)
161 {
162 struct file_istream *fstream =
163 container_of(stream, struct file_istream, istream);
164 const char *name = i_stream_get_name(&stream->istream);
165
166 if (!fstream->file) {
167 /* return defaults */
168 } else if (stream->fd != -1) {
169 if (fstat(stream->fd, &stream->statbuf) < 0) {
170 stream->istream.stream_errno = errno;
171 io_stream_set_error(&stream->iostream,
172 "file_istream.fstat(%s) failed: %m", name);
173 i_error("%s", i_stream_get_error(&stream->istream));
174 return -1;
175 }
176 } else {
177 if (stat(name, &stream->statbuf) < 0) {
178 stream->istream.stream_errno = errno;
179 io_stream_set_error(&stream->iostream,
180 "file_istream.stat(%s) failed: %m", name);
181 i_error("%s", i_stream_get_error(&stream->istream));
182 return -1;
183 }
184 }
185 return 0;
186 }
187
188 struct istream *
i_stream_create_file_common(struct file_istream * fstream,int fd,const char * path,size_t max_buffer_size,bool autoclose_fd)189 i_stream_create_file_common(struct file_istream *fstream,
190 int fd, const char *path,
191 size_t max_buffer_size, bool autoclose_fd)
192 {
193 struct istream *input;
194 struct stat st;
195 bool is_file;
196 int flags;
197
198 fstream->autoclose_fd = autoclose_fd;
199
200 fstream->istream.iostream.close = i_stream_file_close;
201 fstream->istream.max_buffer_size = max_buffer_size;
202 fstream->istream.read = i_stream_file_read;
203 fstream->istream.seek = i_stream_file_seek;
204 fstream->istream.sync = i_stream_file_sync;
205 fstream->istream.stat = i_stream_file_stat;
206
207 /* if it's a file, set the flags properly */
208 if (fd == -1) {
209 /* only the path is known for now - the fd is opened later */
210 is_file = TRUE;
211 } else if (fstat(fd, &st) < 0)
212 is_file = FALSE;
213 else if (S_ISREG(st.st_mode))
214 is_file = TRUE;
215 else if (!S_ISDIR(st.st_mode))
216 is_file = FALSE;
217 else {
218 /* we're trying to open a directory.
219 we're not designed for it. */
220 io_stream_set_error(&fstream->istream.iostream,
221 "%s is a directory, can't read it as file",
222 path != NULL ? path : t_strdup_printf("<fd %d>", fd));
223 fstream->istream.istream.stream_errno = EISDIR;
224 is_file = FALSE;
225 }
226 if (is_file) {
227 fstream->file = TRUE;
228 fstream->istream.istream.blocking = TRUE;
229 fstream->istream.istream.seekable = TRUE;
230 } else if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
231 i_assert(fd > -1);
232 /* shouldn't happen */
233 fstream->istream.istream.stream_errno = errno;
234 io_stream_set_error(&fstream->istream.iostream,
235 "fcntl(%d, F_GETFL) failed: %m", fd);
236 } else if ((flags & O_NONBLOCK) == 0) {
237 /* blocking socket/fifo */
238 fstream->istream.istream.blocking = TRUE;
239 }
240 fstream->istream.istream.readable_fd = TRUE;
241
242 input = i_stream_create(&fstream->istream, NULL, fd, 0);
243 i_stream_set_name(input, is_file ? "(file)" : "(fd)");
244 return input;
245 }
246
i_stream_create_fd(int fd,size_t max_buffer_size)247 struct istream *i_stream_create_fd(int fd, size_t max_buffer_size)
248 {
249 struct file_istream *fstream;
250
251 i_assert(fd != -1);
252
253 fstream = i_new(struct file_istream, 1);
254 return i_stream_create_file_common(fstream, fd, NULL,
255 max_buffer_size, FALSE);
256 }
257
i_stream_create_fd_autoclose(int * fd,size_t max_buffer_size)258 struct istream *i_stream_create_fd_autoclose(int *fd, size_t max_buffer_size)
259 {
260 struct istream *input;
261 struct file_istream *fstream;
262
263 i_assert(*fd != -1);
264
265 fstream = i_new(struct file_istream, 1);
266 input = i_stream_create_file_common(fstream, *fd, NULL,
267 max_buffer_size, TRUE);
268 *fd = -1;
269 return input;
270 }
271
i_stream_create_file(const char * path,size_t max_buffer_size)272 struct istream *i_stream_create_file(const char *path, size_t max_buffer_size)
273 {
274 struct file_istream *fstream;
275 struct istream *input;
276
277 fstream = i_new(struct file_istream, 1);
278 input = i_stream_create_file_common(fstream, -1, path,
279 max_buffer_size, TRUE);
280 i_stream_set_name(input, path);
281 return input;
282 }
283