From: Timo Sirainen Date: Fri, 27 Jun 2025 21:21:17 +0000 (+0300) Subject: lib: Add functions to reliably autoclose fd for i/ostream-file X-Git-Tag: 2.4.2~695 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d0afc4312778f3dd64ad22c2c6119f1cf5aa2c00;p=thirdparty%2Fdovecot%2Fcore.git lib: Add functions to reliably autoclose fd for i/ostream-file These can be used to create iostreams where fd is autoclosed after both istream and ostream are closed in either order. --- diff --git a/src/lib/iostream.c b/src/lib/iostream.c index f2ba00088d..2b45c8b902 100644 --- a/src/lib/iostream.c +++ b/src/lib/iostream.c @@ -6,6 +6,45 @@ #include "ostream.h" #include "iostream-private.h" +struct iostream_fd *iostream_fd_init(int fd) +{ + struct iostream_fd *ref = i_new(struct iostream_fd, 1); + ref->refcount = 1; + ref->fd = fd; + return ref; +} + +void iostream_fd_ref(struct iostream_fd *ref) +{ + i_assert(ref->refcount > 0); + ref->refcount++; +} + +bool iostream_fd_unref(struct iostream_fd **_ref) +{ + struct iostream_fd *ref = *_ref; + + i_assert(ref != NULL); + i_assert(ref->refcount > 0); + + if (--ref->refcount > 0) + return TRUE; + i_free(ref); + return FALSE; +} + +void io_stream_create_fd_autoclose(int *fd, size_t max_in_buffer_size, + size_t max_out_buffer_size, + struct istream **input_r, + struct ostream **output_r) +{ + struct iostream_fd *fd_ref = iostream_fd_init(*fd); + *input_r = i_stream_create_fd_ref_autoclose(fd_ref, max_in_buffer_size); + *output_r = o_stream_create_fd_ref_autoclose(fd_ref, max_out_buffer_size); + iostream_fd_unref(&fd_ref); + *fd = -1; +} + static void io_stream_default_close(struct iostream_private *stream ATTR_UNUSED, bool close_parent ATTR_UNUSED) diff --git a/src/lib/iostream.h b/src/lib/iostream.h index 87bc37486b..72b5da86a9 100644 --- a/src/lib/iostream.h +++ b/src/lib/iostream.h @@ -1,6 +1,24 @@ #ifndef IOSTREAM_H #define IOSTREAM_H +struct iostream_fd { + int refcount; + int fd; +}; + +/* Used to allow autoclosing fds with istream-file and ostream-file without + requiring them to be closed in any specific order. */ +struct iostream_fd *iostream_fd_init(int fd); +void iostream_fd_ref(struct iostream_fd *ref); +bool iostream_fd_unref(struct iostream_fd **ref); + +/* Create i/ostreams for the given fd. The fd is set to -1 immediately to avoid + accidentally closing it twice. */ +void io_stream_create_fd_autoclose(int *fd, size_t max_in_buffer_size, + size_t max_out_buffer_size, + struct istream **input_r, + struct ostream **output_r); + /* Returns human-readable reason for why iostream was disconnected. The output is either "Connection closed" for clean disconnections or "Connection closed: " for unclean disconnections. */ diff --git a/src/lib/istream-file-private.h b/src/lib/istream-file-private.h index c4701ed2ff..840de65fd2 100644 --- a/src/lib/istream-file-private.h +++ b/src/lib/istream-file-private.h @@ -6,6 +6,7 @@ struct file_istream { struct istream_private istream; + struct iostream_fd *fd_ref; uoff_t skip_left; bool file:1; diff --git a/src/lib/istream-file.c b/src/lib/istream-file.c index 8c945bd910..4683ef04b1 100644 --- a/src/lib/istream-file.c +++ b/src/lib/istream-file.c @@ -20,7 +20,9 @@ void i_stream_file_close(struct iostream_private *stream, struct file_istream *fstream = container_of(_stream, struct file_istream, istream); - if (fstream->autoclose_fd && _stream->fd != -1) { + bool refs_left = fstream->fd_ref != NULL && + iostream_fd_unref(&fstream->fd_ref); + if (fstream->autoclose_fd && _stream->fd != -1 && !refs_left) { /* Ignore ECONNRESET because we don't really care about it here, as we are closing the socket down in any case. There might be unsent data but nothing we can do about that. */ @@ -269,6 +271,18 @@ struct istream *i_stream_create_fd_autoclose(int *fd, size_t max_buffer_size) return input; } +struct istream *i_stream_create_fd_ref_autoclose(struct iostream_fd *ref, + size_t max_buffer_size) +{ + struct file_istream *fstream; + + fstream = i_new(struct file_istream, 1); + fstream->fd_ref = ref; + iostream_fd_ref(ref); + return i_stream_create_file_common(fstream, ref->fd, NULL, + max_buffer_size, TRUE); +} + struct istream *i_stream_create_file(const char *path, size_t max_buffer_size) { struct file_istream *fstream; diff --git a/src/lib/istream.h b/src/lib/istream.h index acab9feed5..f0648f36a8 100644 --- a/src/lib/istream.h +++ b/src/lib/istream.h @@ -5,6 +5,7 @@ #include struct ioloop; +struct iostream_fd; struct istream { uoff_t v_offset; @@ -41,6 +42,10 @@ typedef void istream_callback_t(void *context); struct istream *i_stream_create_fd(int fd, size_t max_buffer_size); /* The fd is set to -1 immediately to avoid accidentally closing it twice. */ struct istream *i_stream_create_fd_autoclose(int *fd, size_t max_buffer_size); +/* Autoclose the fd once ref's refcount drops to 0. This function increases the + refcount, so the caller is expected to unref it as well. */ +struct istream *i_stream_create_fd_ref_autoclose(struct iostream_fd *ref, + size_t max_buffer_size); /* Open the given path only when something is actually tried to be read from the stream. */ struct istream *i_stream_create_file(const char *path, size_t max_buffer_size); diff --git a/src/lib/ostream-file-private.h b/src/lib/ostream-file-private.h index dc3d4dbb75..122a974e40 100644 --- a/src/lib/ostream-file-private.h +++ b/src/lib/ostream-file-private.h @@ -11,6 +11,7 @@ struct file_ostream { unsigned int iov_count, const char **error_r); int fd; + struct iostream_fd *fd_ref; struct io *io; uoff_t buffer_offset; uoff_t real_offset; diff --git a/src/lib/ostream-file.c b/src/lib/ostream-file.c index b350e6df60..f6b5a1eefa 100644 --- a/src/lib/ostream-file.c +++ b/src/lib/ostream-file.c @@ -30,14 +30,14 @@ ((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX) static void stream_send_io(struct file_ostream *fstream); -static struct ostream * o_stream_create_fd_common(int fd, - size_t max_buffer_size, bool autoclose_fd); static void stream_closed(struct file_ostream *fstream) { io_remove(&fstream->io); - if (fstream->autoclose_fd && fstream->fd != -1) { + bool refs_left = fstream->fd_ref != NULL && + iostream_fd_unref(&fstream->fd_ref); + if (fstream->autoclose_fd && fstream->fd != -1 && !refs_left) { /* Ignore ECONNRESET because we don't really care about it here, as we are closing the socket down in any case. There might be unsent data but nothing we can do about that. */ @@ -1080,15 +1080,19 @@ static void fstream_init_file(struct file_ostream *fstream) } } -static -struct ostream * o_stream_create_fd_common(int fd, size_t max_buffer_size, - bool autoclose_fd) +static struct ostream * +o_stream_create_fd_common(int fd, struct iostream_fd *ref, + size_t max_buffer_size, bool autoclose_fd) { struct file_ostream *fstream; struct ostream *ostream; off_t offset; fstream = i_new(struct file_ostream, 1); + if (ref != NULL) { + fstream->fd_ref = ref; + iostream_fd_ref(ref); + } ostream = o_stream_create_file_common (fstream, fd, max_buffer_size, autoclose_fd); @@ -1121,18 +1125,24 @@ struct ostream * o_stream_create_fd_common(int fd, size_t max_buffer_size, struct ostream * o_stream_create_fd(int fd, size_t max_buffer_size) { - return o_stream_create_fd_common(fd, max_buffer_size, FALSE); + return o_stream_create_fd_common(fd, NULL, max_buffer_size, FALSE); } struct ostream * o_stream_create_fd_autoclose(int *fd, size_t max_buffer_size) { - struct ostream *ostream = o_stream_create_fd_common(*fd, + struct ostream *ostream = o_stream_create_fd_common(*fd, NULL, max_buffer_size, TRUE); *fd = -1; return ostream; } +struct ostream *o_stream_create_fd_ref_autoclose(struct iostream_fd *ref, + size_t max_buffer_size) +{ + return o_stream_create_fd_common(ref->fd, ref, max_buffer_size, TRUE); +} + struct ostream * o_stream_create_fd_file(int fd, uoff_t offset, bool autoclose_fd) { diff --git a/src/lib/ostream.h b/src/lib/ostream.h index abce2c186e..f36e98b446 100644 --- a/src/lib/ostream.h +++ b/src/lib/ostream.h @@ -3,6 +3,8 @@ #include "ioloop.h" +struct iostream_fd; + enum ostream_send_istream_result { /* All of the istream was successfully sent to ostream. */ OSTREAM_SEND_ISTREAM_RESULT_FINISHED, @@ -59,6 +61,10 @@ typedef void ostream_callback_t(void *context); struct ostream *o_stream_create_fd(int fd, size_t max_buffer_size); /* The fd is set to -1 immediately to avoid accidentally closing it twice. */ struct ostream *o_stream_create_fd_autoclose(int *fd, size_t max_buffer_size); +/* Autoclose the fd once ref's refcount drops to 0. This function increases the + refcount, so the caller is expected to unref it as well. */ +struct ostream *o_stream_create_fd_ref_autoclose(struct iostream_fd *ref, + size_t max_buffer_size); /* Create an output stream from a regular file which begins at given offset. If offset==UOFF_T_MAX, the current offset isn't known. */ struct ostream *