1 /* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "fdpass.h"
5 #include "ostream-file-private.h"
6 #include "ostream-unix.h"
7 
8 struct unix_ostream {
9 	struct file_ostream fstream;
10 	int write_fd;
11 };
12 
13 static void
o_stream_unix_close(struct iostream_private * stream,bool close_parent)14 o_stream_unix_close(struct iostream_private *stream, bool close_parent)
15 {
16 	struct unix_ostream *ustream =
17 		container_of(stream, struct unix_ostream,
18 			     fstream.ostream.iostream);
19 
20 	i_close_fd(&ustream->write_fd);
21 	o_stream_file_close(stream, close_parent);
22 }
23 
o_stream_unix_writev(struct file_ostream * fstream,const struct const_iovec * iov,unsigned int iov_count)24 static ssize_t o_stream_unix_writev(struct file_ostream *fstream,
25 				   const struct const_iovec *iov,
26 				   unsigned int iov_count)
27 {
28 	struct unix_ostream *ustream =
29 		container_of(fstream, struct unix_ostream, fstream);
30 	size_t sent;
31 	ssize_t ret;
32 
33 	if (ustream->write_fd == -1) {
34 		/* no fd */
35 		return o_stream_file_writev(fstream, iov, iov_count);
36 	}
37 
38 	/* send first iovec along with fd */
39 	if (iov_count == 0)
40 		return 0;
41 	i_assert(iov[0].iov_len > 0);
42 	ret = fd_send(fstream->fd, ustream->write_fd,
43 		iov[0].iov_base, iov[0].iov_len);
44 	if (ret < 0)
45 		return ret;
46 
47 	/* update stream */
48 	sent = ret;
49 	fstream->real_offset += sent;
50 
51 	ustream->write_fd = -1;
52 
53 	if (sent < iov[0].iov_len || iov_count == 1) {
54 		/* caller will call us again to write the rest */
55 		return sent;
56 	}
57 
58 	/* send remaining iovecs */
59 	ret = o_stream_file_writev(fstream, &iov[1], iov_count-1);
60 	if (ret < 0)
61 		return  (errno == EAGAIN || errno == EINTR ? (ssize_t)sent : ret);
62 	sent += ret;
63 	return sent;
64 }
65 
o_stream_create_unix(int fd,size_t max_buffer_size)66 struct ostream *o_stream_create_unix(int fd, size_t max_buffer_size)
67 {
68 	struct unix_ostream *ustream;
69 	struct ostream *output;
70 
71 	i_assert(fd != -1);
72 
73 	ustream = i_new(struct unix_ostream, 1);
74 	ustream->write_fd = -1;
75 	output = o_stream_create_file_common(&ustream->fstream, fd,
76 					    max_buffer_size, FALSE);
77 	output->real_stream->iostream.close = o_stream_unix_close;
78 	ustream->fstream.writev = o_stream_unix_writev;
79 
80 	return output;
81 }
82 
o_stream_unix_write_fd(struct ostream * output,int fd)83 bool o_stream_unix_write_fd(struct ostream *output, int fd)
84 {
85 	struct unix_ostream *ustream =
86 		container_of(output->real_stream, struct unix_ostream,
87 			     fstream.ostream);
88 
89 	i_assert(fd >= 0);
90 
91 	if (ustream->write_fd >= 0)
92 		return FALSE;
93 	ustream->write_fd = fd;
94 	return TRUE;
95 }
96