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