Coverage Report

Created: 2026-06-09 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib/istream-failure-at.c
Line
Count
Source
1
/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "istream-private.h"
5
#include "istream-failure-at.h"
6
7
struct failure_at_istream {
8
  struct istream_private istream;
9
  int error_code;
10
  char *error_string;
11
  uoff_t failure_offset;
12
};
13
14
static void i_stream_failure_at_destroy(struct iostream_private *stream)
15
1.78k
{
16
1.78k
  struct failure_at_istream *fstream =
17
1.78k
    container_of(stream, struct failure_at_istream,
18
1.78k
           istream.iostream);
19
20
1.78k
  i_free(fstream->error_string);
21
1.78k
}
22
23
static ssize_t
24
i_stream_failure_at_read(struct istream_private *stream)
25
7.20k
{
26
7.20k
  struct failure_at_istream *fstream =
27
7.20k
    container_of(stream, struct failure_at_istream, istream);
28
7.20k
  uoff_t new_offset;
29
7.20k
  ssize_t ret;
30
31
7.20k
  i_stream_seek(stream->parent, stream->parent_start_offset +
32
7.20k
          stream->istream.v_offset);
33
34
7.20k
  ret = i_stream_read_copy_from_parent(&stream->istream);
35
7.20k
  new_offset = stream->istream.v_offset + (stream->pos - stream->skip);
36
7.20k
  if (ret >= 0 && new_offset >= fstream->failure_offset) {
37
0
    if (stream->istream.v_offset >= fstream->failure_offset) {
38
      /* we already passed the wanted failure offset,
39
         return error immediately. */
40
0
      stream->pos = stream->skip;
41
0
      stream->istream.stream_errno = errno =
42
0
        fstream->error_code;
43
0
      io_stream_set_error(&stream->iostream, "%s",
44
0
              fstream->error_string);
45
0
      ret = -1;
46
0
    } else {
47
      /* return data up to the wanted failure offset and
48
         on the next read() call return failure */
49
0
      size_t new_pos = fstream->failure_offset -
50
0
        stream->istream.v_offset + stream->skip;
51
0
      i_assert(new_pos >= stream->skip &&
52
0
         stream->pos >= new_pos);
53
0
      ret -= stream->pos - new_pos;
54
0
      stream->pos = new_pos;
55
0
    }
56
7.20k
  } else if (ret < 0 && stream->istream.stream_errno == 0 &&
57
1.35k
       fstream->failure_offset == UOFF_T_MAX) {
58
    /* failure at EOF */
59
0
    stream->istream.stream_errno = errno =
60
0
      fstream->error_code;
61
0
    io_stream_set_error(&stream->iostream, "%s",
62
0
            fstream->error_string);
63
0
  }
64
7.20k
  return ret;
65
7.20k
}
66
67
struct istream *
68
i_stream_create_failure_at(struct istream *input, uoff_t failure_offset,
69
         int stream_errno, const char *error_string)
70
1.78k
{
71
1.78k
  struct failure_at_istream *fstream;
72
73
1.78k
  fstream = i_new(struct failure_at_istream, 1);
74
1.78k
  fstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
75
1.78k
  fstream->istream.stream_size_passthrough = TRUE;
76
77
1.78k
  fstream->istream.read = i_stream_failure_at_read;
78
1.78k
  fstream->istream.iostream.destroy = i_stream_failure_at_destroy;
79
80
1.78k
  fstream->istream.istream.blocking = input->blocking;
81
1.78k
  fstream->istream.istream.seekable = input->seekable;
82
83
1.78k
  fstream->error_code = stream_errno;
84
1.78k
  fstream->error_string = i_strdup(error_string);
85
1.78k
  fstream->failure_offset = failure_offset;
86
1.78k
  return i_stream_create(&fstream->istream, input,
87
1.78k
             i_stream_get_fd(input), 0);
88
1.78k
}
89
90
struct istream *
91
i_stream_create_failure_at_eof(struct istream *input, int stream_errno,
92
             const char *error_string)
93
0
{
94
  return i_stream_create_failure_at(input, UOFF_T_MAX, stream_errno,
95
0
            error_string);
96
0
}