]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added o_stream_pwrite().
authorTimo Sirainen <tss@iki.fi>
Wed, 25 Feb 2009 02:39:41 +0000 (21:39 -0500)
committerTimo Sirainen <tss@iki.fi>
Wed, 25 Feb 2009 02:39:41 +0000 (21:39 -0500)
--HG--
branch : HEAD

src/lib/ostream-file.c
src/lib/ostream-internal.h
src/lib/ostream.c
src/lib/ostream.h

index 187648a5cb33cba38a4da08ca8d7a27ebe30764a..22dbfc3b9681a6dc3a0b71bce2052d2408e4373a 100644 (file)
@@ -92,6 +92,20 @@ o_stream_file_set_max_buffer_size(struct iostream_private *stream,
        fstream->max_buffer_size = max_size;
 }
 
+static size_t file_buffer_get_used_size(struct file_ostream *fstream)
+{
+       if (fstream->head == fstream->tail)
+               return fstream->full ? fstream->buffer_size : 0;
+       else if (fstream->head < fstream->tail) {
+               /* ...HXXXT... */
+               return fstream->tail - fstream->head;
+       } else {
+               /* XXXT...HXXX */
+               return fstream->tail +
+                       (fstream->buffer_size - fstream->head);
+       }
+}
+
 static void update_buffer(struct file_ostream *fstream, size_t size)
 {
        size_t used;
@@ -578,6 +592,90 @@ static ssize_t o_stream_file_sendv(struct ostream_private *stream,
        return ret;
 }
 
+static size_t
+o_stream_file_update_buffer(struct file_ostream *fstream,
+                           const void *data, size_t size, size_t pos)
+{
+       size_t avail, copy_size;
+
+       if (fstream->head < fstream->tail) {
+               /* ...HXXXT... */
+               i_assert(pos < fstream->tail);
+               avail = fstream->tail - pos;
+       } else {
+               /* XXXT...HXXX */
+               avail = fstream->buffer_size - pos;
+       }
+       copy_size = I_MIN(size, avail);
+       memcpy(fstream->buffer + pos, data, copy_size);
+       data = CONST_PTR_OFFSET(data, copy_size);
+       size -= copy_size;
+
+       if (size > 0 && fstream->head >= fstream->tail) {
+               /* wraps to beginning of the buffer */
+               copy_size = I_MIN(size, fstream->tail);
+               memcpy(fstream->buffer, data, copy_size);
+               size -= copy_size;
+       }
+       return size;
+}
+
+static int
+o_stream_file_write_at(struct ostream_private *stream,
+                      const void *data, size_t size, uoff_t offset)
+{
+       struct file_ostream *fstream = (struct file_ostream *)stream;
+       size_t used, pos, skip, left;
+
+       /* update buffer if the write overlaps it */
+       used = file_buffer_get_used_size(fstream);
+       if (fstream->buffer_offset < offset + size &&
+           fstream->buffer_offset + used > offset) {
+               if (fstream->buffer_offset <= offset) {
+                       /* updating from the beginning */
+                       skip = 0;
+               } else {
+                       skip = fstream->buffer_offset - offset;
+               }
+               pos = (fstream->head + offset + skip - fstream->buffer_offset) %
+                       fstream->buffer_size;
+               left = o_stream_file_update_buffer(fstream,
+                               CONST_PTR_OFFSET(data, skip), size - skip, pos);
+               if (left > 0) {
+                       /* didn't write all of it */
+                       if (skip > 0) {
+                               /* we also have to write a prefix. don't
+                                  bother with two syscalls, just write all
+                                  of it in one pwrite(). */
+                       } else {
+                               /* write only the suffix */
+                               data = CONST_PTR_OFFSET(data, skip);
+                               size -= skip;
+                               offset += skip;
+                       }
+               } else if (skip == 0) {
+                       /* everything done */
+                       return 0;
+               } else {
+                       /* still have to write prefix */
+                       size = skip;
+
+               }
+       }
+
+       /* we couldn't write everything to the buffer. flush the buffer
+          and pwrite() the rest. */
+       if (o_stream_file_flush(stream) < 0)
+               return -1;
+
+       if (pwrite_full(fstream->fd, data, size, offset) < 0) {
+               stream->ostream.stream_errno = errno;
+               stream_closed(fstream);
+               return -1;
+       }
+       return 0;
+}
+
 static off_t io_stream_sendfile(struct ostream_private *outstream,
                                struct istream *instream, int in_fd)
 {
@@ -640,55 +738,30 @@ static off_t io_stream_copy(struct ostream_private *outstream,
 {
        struct file_ostream *foutstream = (struct file_ostream *)outstream;
        uoff_t start_offset;
-       struct const_iovec iov[3];
-       int iov_len;
+       struct const_iovec iov;
        const unsigned char *data;
-       size_t size, skip_size;
        ssize_t ret;
-       int pos;
-
-       iov_len = o_stream_fill_iovec(foutstream, iov);
-
-        skip_size = 0;
-       for (pos = 0; pos < iov_len; pos++)
-               skip_size += iov[pos].iov_len;
 
        start_offset = instream->v_offset;
        for (;;) {
-               (void)i_stream_read_data(instream, &data, &size,
+               (void)i_stream_read_data(instream, &data, &iov.iov_len,
                                         foutstream->optimal_block_size-1);
-               if (size == 0) {
+               if (iov.iov_len == 0) {
                        /* all sent */
                        break;
                }
 
-               pos = iov_len++;
-               iov[pos].iov_base = (void *) data;
-               iov[pos].iov_len = size;
-
-               ret = o_stream_writev(foutstream, iov, iov_len);
-               if (ret < 0)
+               iov.iov_base = data;
+               ret = o_stream_file_sendv(outstream, &iov, 1);
+               if (ret <= 0) {
+                       if (ret == 0)
+                               break;
                        return -1;
-
-               if (skip_size > 0) {
-                       if ((size_t)ret < skip_size) {
-                               update_buffer(foutstream, ret);
-                               skip_size -= ret;
-                               ret = 0;
-                       } else {
-                               update_buffer(foutstream, skip_size);
-                               ret -= skip_size;
-                               skip_size = 0;
-                       }
                }
-               outstream->ostream.offset += ret;
                i_stream_skip(instream, ret);
 
-               if ((size_t)ret != iov[pos].iov_len)
+               if ((size_t)ret != iov.iov_len)
                        break;
-
-               i_assert(skip_size == 0);
-               iov_len = 0;
        }
 
        return (off_t) (instream->v_offset - start_offset);
@@ -842,6 +915,7 @@ o_stream_create_fd_common(int fd, bool autoclose_fd)
        fstream->ostream.get_used_size = o_stream_file_get_used_size;
        fstream->ostream.seek = o_stream_file_seek;
        fstream->ostream.sendv = o_stream_file_sendv;
+       fstream->ostream.write_at = o_stream_file_write_at;
        fstream->ostream.send_istream = o_stream_file_send_istream;
 
        return fstream;
index a2144b1a7b11454640aa58d0a9320d1891164f41..3536f2aec04d7fb71218d5163eac571a11c94247 100644 (file)
@@ -17,6 +17,8 @@ struct ostream_private {
        ssize_t (*sendv)(struct ostream_private *stream,
                         const struct const_iovec *iov,
                         unsigned int iov_count);
+       ssize_t (*write_at)(struct ostream_private *stream,
+                           const void *data, size_t size, uoff_t offset);
        off_t (*send_istream)(struct ostream_private *outstream,
                              struct istream *instream);
 
index 552f3fe8ca22a69abd9385f825f77cac622b1fcd..d95543b93ab566ffecfe97d369e92388a0eb754c 100644 (file)
@@ -179,6 +179,23 @@ off_t o_stream_send_istream(struct ostream *outstream,
        return ret;
 }
 
+int o_stream_pwrite(struct ostream *stream, const void *data, size_t size,
+                   uoff_t offset)
+{
+       int ret;
+
+       if (unlikely(stream->closed))
+               return -1;
+
+       ret = stream->real_stream->write_at(stream->real_stream,
+                                           data, size, offset);
+       if (unlikely(ret < 0)) {
+               i_assert(stream->stream_errno != 0);
+               stream->last_failed_errno = stream->stream_errno;
+       }
+       return ret;
+}
+
 struct ostream *o_stream_create(struct ostream_private *_stream)
 {
        _stream->ostream.real_stream = _stream;
index 08e4dfd541ec5e63cd8f08a980a994ee4a4bc90d..93de8f64bd1efb2a524c442c60ab8d67c17f3001 100644 (file)
@@ -93,4 +93,8 @@ ssize_t o_stream_send_str(struct ostream *stream, const char *str);
 off_t o_stream_send_istream(struct ostream *outstream,
                            struct istream *instream);
 
+/* Write data to specified offset. Returns 0 if successful, -1 if error. */
+int o_stream_pwrite(struct ostream *stream, const void *data, size_t size,
+                   uoff_t offset);
+
 #endif