• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 1498068235

16 Oct 2024 08:46AM UTC coverage: 76.262%. First build
1498068235

push

gitlab-ci

kacf
fix: Use large data types for files sizes where appropriate.

Switch data types to use int64_t instead of `size_t`, which is only 32
bits on 32-bit platforms, in places where we expect to handle file
sizes.

Also enable error messages when we try to auto-convert from one type
to another in a lossy fashion.

Changelog: Fix possible integer overflow when dealing with large files
on 32-bit platforms.

Ticket: MEN-7613

Signed-off-by: Kristian Amlie <kristian.amlie@northern.tech>

35 of 54 new or added lines in 12 files covered. (64.81%)

7312 of 9588 relevant lines covered (76.26%)

11282.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/common/io/io.cpp
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#include <common/io.hpp>
16

17
#include <common/config.h>
18

19
#include <cerrno>
20
#include <cstdint>
21
#include <cstring>
22
#include <istream>
23
#include <memory>
24
#include <streambuf>
25
#include <vector>
26
#include <fstream>
27

28
namespace mender {
29
namespace common {
30
namespace io {
31

32
namespace error = mender::common::error;
33
namespace expected = mender::common::expected;
34

35
void AsyncReader::RepeatedAsyncRead(
×
36
        vector<uint8_t>::iterator start,
37
        vector<uint8_t>::iterator end,
38
        RepeatedAsyncIoHandler handler) {
39
        class Functor {
×
40
        public:
41
                AsyncReader &reader;
42
                vector<uint8_t>::iterator start;
43
                vector<uint8_t>::iterator end;
44
                RepeatedAsyncIoHandler handler;
45
                void ScheduleNextRead(Repeat repeat) {
×
46
                        while (repeat == Repeat::Yes) {
×
47
                                auto err = reader.AsyncRead(start, end, *this);
×
48
                                if (err == error::NoError) {
×
49
                                        break;
50
                                } else {
51
                                        repeat = handler(expected::unexpected(err));
×
52
                                }
53
                        }
54
                }
×
55
                void operator()(ExpectedSize num_read) {
×
56
                        auto repeat = handler(num_read);
×
57
                        ScheduleNextRead(repeat);
×
58
                }
×
59
        };
60
        Functor func {*this, start, end, handler};
×
61
        func.ScheduleNextRead(Repeat::Yes);
×
62
}
×
63

64
Error Copy(Writer &dst, Reader &src) {
×
65
        vector<uint8_t> buffer(MENDER_BUFSIZE);
×
66
        return Copy(dst, src, buffer);
×
67
}
68

69
Error Copy(Writer &dst, Reader &src, vector<uint8_t> &buffer) {
×
70
        while (true) {
71
                auto r_result = src.Read(buffer.begin(), buffer.end());
×
72
                if (!r_result) {
×
73
                        return r_result.error();
×
74
                } else if (r_result.value() == 0) {
×
75
                        return NoError;
×
76
                } else if (r_result.value() > buffer.size()) {
×
77
                        return error::MakeError(
78
                                error::ProgrammingError,
79
                                "Read returned more bytes than requested. This is a bug in the Read function.");
×
80
                }
81

82
                auto w_result = dst.Write(buffer.cbegin(), buffer.cbegin() + r_result.value());
×
83
                if (!w_result) {
×
84
                        return w_result.error();
×
85
                } else if (w_result.value() == 0) {
×
86
                        // Should this even happen?
87
                        return Error(std::error_condition(std::errc::io_error), "Zero write when copying data");
×
88
                } else if (r_result.value() != w_result.value()) {
×
89
                        return Error(
90
                                std::error_condition(std::errc::io_error), "Short write when copying data");
×
91
                }
92
        }
93
}
94

95
struct CopyData {
×
NEW
96
        CopyData(int64_t limit) :
×
97
                buf(MENDER_BUFSIZE),
98
                limit {limit} {
×
99
        }
×
100

101
        vector<uint8_t> buf;
102
        int64_t copied {0};
103
        int64_t limit;
104
};
105

106
void AsyncCopy(
×
107
        Writer &dst, AsyncReader &src, function<void(Error)> finished_handler, int64_t stop_after) {
108
        AsyncCopy(
×
109
                WriterPtr(&dst, [](Writer *) {}),
×
110
                AsyncReaderPtr(&src, [](AsyncReader *) {}),
×
111
                finished_handler,
112
                stop_after);
×
113
}
×
114

115
void AsyncCopy(
×
116
        WriterPtr dst, AsyncReaderPtr src, function<void(Error)> finished_handler, int64_t stop_after) {
117
        auto data = make_shared<CopyData>(stop_after);
×
118
        class Functor {
119
        public:
120
                void operator()(ExpectedSize size) {
×
121
                        if (!size) {
×
122
                                CallFinishedHandler(size.error());
×
123
                                return;
×
124
                        }
125

126

127
                        if (*size == 0) {
×
128
                                CallFinishedHandler(error::NoError);
×
129
                                return;
×
130
                        }
131

132
                        auto n = writer->Write(data->buf.begin(), data->buf.begin() + *size);
×
133
                        if (!n) {
×
134
                                CallFinishedHandler(n.error());
×
135
                                return;
136
                        } else if (*n != *size) {
×
137
                                CallFinishedHandler(Error(
×
138
                                        make_error_condition(std::errc::io_error), "Short write when copying data"));
×
139
                                return;
×
140
                        }
141

142
                        data->copied += *n;
×
143

144
                        size_t to_copy = static_cast<size_t>(
NEW
145
                                min(data->limit - data->copied, static_cast<int64_t>(data->buf.size())));
×
146
                        if (to_copy == 0) {
×
147
                                CallFinishedHandler(error::NoError);
×
148
                                return;
149
                        }
150

151
                        auto err = reader->AsyncRead(
152
                                data->buf.begin(),
153
                                data->buf.begin() + to_copy,
154
                                Functor {writer, reader, data, finished_handler});
×
155
                        if (err != error::NoError) {
×
156
                                CallFinishedHandler(err);
×
157
                        }
158
                }
159

160
                void CallFinishedHandler(const error::Error &err) {
×
161
                        // Sometimes the functor is kept on the event loop longer than we expect. It
162
                        // will eventually be destroyed, but destroy what we own now, so that
163
                        // we don't keep references to them after we're done.
164
                        auto handler = finished_handler;
×
165
                        *this = {};
×
166
                        handler(err);
×
167
                }
×
168

169
                WriterPtr writer;
170
                AsyncReaderPtr reader;
171
                shared_ptr<CopyData> data;
172
                function<void(Error)> finished_handler;
173
        };
NEW
174
        size_t to_copy = static_cast<size_t>(min(data->limit, static_cast<int64_t>(data->buf.size())));
×
175
        auto err = src->AsyncRead(
176
                data->buf.begin(), data->buf.begin() + to_copy, Functor {dst, src, data, finished_handler});
×
177
        if (err != error::NoError) {
×
178
                finished_handler(err);
×
179
        }
180
}
×
181

182
void AsyncCopy(
×
183
        AsyncWriter &dst, Reader &src, function<void(Error)> finished_handler, int64_t stop_after) {
184
        AsyncCopy(
×
185
                AsyncWriterPtr(&dst, [](AsyncWriter *) {}),
×
186
                ReaderPtr(&src, [](Reader *) {}),
×
187
                finished_handler,
188
                stop_after);
×
189
}
×
190

191
void AsyncCopy(
×
192
        AsyncWriterPtr dst, ReaderPtr src, function<void(Error)> finished_handler, int64_t stop_after) {
193
        auto data = make_shared<CopyData>(stop_after);
×
194

195
        class Functor {
196
        public:
197
                void operator()(ExpectedSize exp_written) {
×
198
                        if (!exp_written) {
×
199
                                CallFinishedHandler(exp_written.error());
×
200
                                return;
×
201
                        } else if (exp_written.value() != expected_written) {
×
202
                                CallFinishedHandler(Error(
×
203
                                        make_error_condition(std::errc::io_error), "Short write when copying data"));
×
204
                                return;
×
205
                        }
206

207
                        data->copied += *exp_written;
×
208

209
                        size_t to_copy = static_cast<size_t>(
NEW
210
                                min(data->limit - data->copied, static_cast<int64_t>(data->buf.size())));
×
211
                        if (to_copy == 0) {
×
212
                                CallFinishedHandler(error::NoError);
×
213
                                return;
×
214
                        }
215

216
                        auto exp_read = reader->Read(data->buf.begin(), data->buf.begin() + to_copy);
×
217
                        if (!exp_read) {
×
218
                                CallFinishedHandler(exp_read.error());
×
219
                                return;
220
                        }
221
                        auto &read = exp_read.value();
×
222

223
                        if (read == 0) {
×
224
                                CallFinishedHandler(error::NoError);
×
225
                                return;
226
                        }
227

228
                        auto err = writer->AsyncWrite(
229
                                data->buf.begin(),
230
                                data->buf.begin() + read,
×
231
                                Functor {writer, reader, finished_handler, data, read});
×
232
                        if (err != error::NoError) {
×
233
                                CallFinishedHandler(err);
×
234
                        }
235
                }
236

237
                void CallFinishedHandler(const error::Error &err) {
×
238
                        // Sometimes the functor is kept on the event loop longer than we expect. It
239
                        // will eventually be destroyed, but destroy what we own now, so that
240
                        // we don't keep references to them after we're done.
241
                        auto handler = finished_handler;
×
242
                        *this = {};
×
243
                        handler(err);
×
244
                }
×
245

246
                AsyncWriterPtr writer;
247
                ReaderPtr reader;
248
                function<void(Error)> finished_handler;
249
                shared_ptr<CopyData> data;
250
                size_t expected_written;
251
        };
252

253
        Functor initial {dst, src, finished_handler, data, 0};
×
254
        initial(0);
×
255
}
×
256

257
void AsyncCopy(
×
258
        AsyncWriter &dst,
259
        AsyncReader &src,
260
        function<void(Error)> finished_handler,
261
        int64_t stop_after) {
262
        AsyncCopy(
×
263
                AsyncWriterPtr(&dst, [](AsyncWriter *) {}),
×
264
                AsyncReaderPtr(&src, [](AsyncReader *) {}),
×
265
                finished_handler,
266
                stop_after);
×
267
}
×
268

269
class AsyncCopyReaderFunctor {
270
public:
271
        void operator()(io::ExpectedSize exp_size);
272

273
        void CallFinishedHandler(const error::Error &err) {
×
274
                // Sometimes the functor is kept on the event loop longer than we expect. It will
275
                // eventually be destroyed, but destroy what we own now, so that we don't keep
276
                // references to them after we're done.
277
                auto handler = finished_handler;
×
278
                *this = {};
×
279
                handler(err);
×
280
        }
×
281

282
        AsyncWriterPtr writer;
283
        AsyncReaderPtr reader;
284
        function<void(Error)> finished_handler;
285
        shared_ptr<CopyData> data;
286
};
287

288
class AsyncCopyWriterFunctor {
289
public:
290
        void operator()(io::ExpectedSize exp_size);
291

292
        void CallFinishedHandler(const error::Error &err) {
×
293
                // Sometimes the functor is kept on the event loop longer than we expect. It will
294
                // eventually be destroyed, but destroy what we own now, so that we don't keep
295
                // references to them after we're done.
296
                auto handler = finished_handler;
×
297
                *this = {};
×
298
                handler(err);
×
299
        }
×
300

301
        AsyncWriterPtr writer;
302
        AsyncReaderPtr reader;
303
        function<void(Error)> finished_handler;
304
        shared_ptr<CopyData> data;
305
        size_t expected_written;
306
};
307

308
void AsyncCopyReaderFunctor::operator()(io::ExpectedSize exp_size) {
×
309
        if (!exp_size) {
×
310
                CallFinishedHandler(exp_size.error());
×
311
                return;
×
312
        }
313
        if (exp_size.value() == 0) {
×
314
                CallFinishedHandler(error::NoError);
×
315
                return;
×
316
        }
317

318
        auto err = writer->AsyncWrite(
319
                data->buf.begin(),
320
                data->buf.begin() + exp_size.value(),
×
321
                AsyncCopyWriterFunctor {writer, reader, finished_handler, data, exp_size.value()});
×
322
        if (err != error::NoError) {
×
323
                CallFinishedHandler(err);
×
324
        }
325
}
326

327
void AsyncCopyWriterFunctor::operator()(io::ExpectedSize exp_size) {
×
328
        if (!exp_size) {
×
329
                CallFinishedHandler(exp_size.error());
×
330
                return;
×
331
        }
332
        if (exp_size.value() != expected_written) {
×
333
                CallFinishedHandler(
×
334
                        error::Error(make_error_condition(errc::io_error), "Short write in AsyncCopy"));
×
335
                return;
×
336
        }
337

338
        data->copied += *exp_size;
×
339

340
        size_t to_copy = static_cast<size_t>(
NEW
341
                min(data->limit - data->copied, static_cast<int64_t>(data->buf.size())));
×
342
        if (to_copy == 0) {
×
343
                CallFinishedHandler(error::NoError);
×
344
                return;
×
345
        }
346

347
        auto err = reader->AsyncRead(
348
                data->buf.begin(),
349
                data->buf.begin() + to_copy,
350
                AsyncCopyReaderFunctor {writer, reader, finished_handler, data});
×
351
        if (err != error::NoError) {
×
352
                CallFinishedHandler(err);
×
353
        }
354
}
355

356
void AsyncCopy(
×
357
        AsyncWriterPtr dst,
358
        AsyncReaderPtr src,
359
        function<void(Error)> finished_handler,
360
        int64_t stop_after) {
361
        auto data = make_shared<CopyData>(stop_after);
×
362

NEW
363
        size_t to_copy = static_cast<size_t>(min(data->limit, static_cast<int64_t>(data->buf.size())));
×
364
        auto err = src->AsyncRead(
365
                data->buf.begin(),
366
                data->buf.begin() + to_copy,
367
                AsyncCopyReaderFunctor {dst, src, finished_handler, data});
×
368
        if (err != error::NoError) {
×
369
                finished_handler(err);
×
370
        }
371
}
×
372

373
ExpectedSize ByteReader::Read(vector<uint8_t>::iterator start, vector<uint8_t>::iterator end) {
×
374
        assert(end > start);
375
        Vsize max_read {emitter_->size() - bytes_read_};
×
376
        Vsize iterator_size {static_cast<Vsize>(end - start)};
×
377
        Vsize bytes_to_read {min(iterator_size, max_read)};
×
378
        auto it = next(emitter_->begin(), bytes_read_);
×
379
        std::copy_n(it, bytes_to_read, start);
×
380
        bytes_read_ += bytes_to_read;
×
381
        return bytes_to_read;
×
382
}
383

384
void ByteReader::Rewind() {
×
385
        bytes_read_ = 0;
×
386
}
×
387

388
void ByteWriter::SetUnlimited(bool enabled) {
×
389
        unlimited_ = enabled;
×
390
}
×
391

392
ExpectedSize ByteWriter::Write(
×
393
        vector<uint8_t>::const_iterator start, vector<uint8_t>::const_iterator end) {
394
        assert(end > start);
395
        Vsize max_write {receiver_->size() - bytes_written_};
×
396
        if (max_write == 0 && !unlimited_) {
×
397
                return expected::unexpected(Error(make_error_condition(errc::no_space_on_device), ""));
×
398
        }
399
        Vsize iterator_size {static_cast<Vsize>(end - start)};
×
400
        Vsize bytes_to_write;
401
        if (unlimited_) {
×
402
                bytes_to_write = iterator_size;
403
                if (max_write < bytes_to_write) {
×
404
                        receiver_->resize(bytes_written_ + bytes_to_write);
×
405
                        max_write = bytes_to_write;
406
                }
407
        } else {
408
                bytes_to_write = min(iterator_size, max_write);
×
409
        }
410
        auto it = next(receiver_->begin(), bytes_written_);
×
411
        std::copy_n(start, bytes_to_write, it);
×
412
        bytes_written_ += bytes_to_write;
×
413
        return bytes_to_write;
414
}
415

416

417
ExpectedSize StreamWriter::Write(
×
418
        vector<uint8_t>::const_iterator start, vector<uint8_t>::const_iterator end) {
419
        os_->write(reinterpret_cast<const char *>(&*start), end - start);
×
420
        if (!(*(os_.get()))) {
×
421
                return expected::unexpected(Error(make_error_condition(errc::io_error), ""));
×
422
        }
423
        return end - start;
424
}
425

426
class ReaderStreamBuffer : public streambuf {
427
public:
428
        ReaderStreamBuffer(Reader &reader) :
×
429
                reader_ {reader},
430
                buf_(buf_size_) {};
×
431
        streambuf::int_type underflow() override;
432

433
private:
434
        static const Vsize buf_size_ = MENDER_BUFSIZE;
435
        Reader &reader_;
436
        vector<uint8_t> buf_;
437
};
438

439
streambuf::int_type ReaderStreamBuffer::underflow() {
×
440
        // eback -- pointer to the first char (byte)
441
        // gptr  -- pointer to the current char (byte)
442
        // egptr -- pointer past the last char (byte)
443

444
        // This function is only called if gptr() == nullptr or gptr() >= egptr(),
445
        // i.e. if there's nothing more to read.
446
        if (this->gptr() >= this->egptr()) {
×
447
                errno = 0;
×
448
                auto ex_n_read = reader_.Read(buf_.begin(), buf_.end());
×
449
                streamsize n_read;
450
                if (ex_n_read) {
×
451
                        n_read = ex_n_read.value();
×
452
                } else {
453
                        // There is no way to return an error from underflow(), generally
454
                        // the streams only care about how much data was read. No data or
455
                        // less data then requested by the caller of istream.read() means
456
                        // eofbit and failbit are set. If the user code wants to get the
457
                        // error or check if there was an error, it needs to check errno.
458
                        //
459
                        // So as long as we don't clear errno after a failure in the
460
                        // reader_.Read() above, error handling works as usual and returning
461
                        // eof below is all that needs to happen here.
462
                        //
463
                        // In case errno is not set for some reason, let's try to get it
464
                        // from the error with a fallback to a generic I/O error.
465
                        if (errno == 0) {
×
466
                                if (ex_n_read.error().code.category() == generic_category()) {
×
467
                                        errno = ex_n_read.error().code.value();
×
468
                                } else {
469
                                        errno = EIO;
×
470
                                }
471
                        }
472
                        n_read = 0;
473
                }
474

475
                streambuf::char_type *first = reinterpret_cast<streambuf::char_type *>(buf_.data());
476

477
                // set eback, gptr, egptr
478
                this->setg(first, first, first + n_read);
×
479
        }
480

481
        return this->gptr() == this->egptr() ? std::char_traits<char>::eof()
×
482
                                                                                 : std::char_traits<char>::to_int_type(*this->gptr());
×
483
};
484

485
/**
486
 * A variant of the #istream class that takes ownership of the #streambuf buffer
487
 * created for it.
488
 *
489
 * @note Base #istream is designed to work on shared buffers so it doesn't
490
 *       destruct/delete the buffer.
491
 */
492
class istreamWithUniqueBuffer : public istream {
493
public:
494
        // The unique_ptr, &&buf and std::move() model this really nicely -- a
495
        // unique_ptr rvalue (i.e. temporary) is required and it's moved into the
496
        // object. The default destructor then takes care of cleaning up properly.
497
        istreamWithUniqueBuffer(unique_ptr<streambuf> &&buf) :
×
498
                istream(buf.get()),
499
                buf_ {std::move(buf)} {};
×
500

501
private:
502
        unique_ptr<streambuf> buf_;
503
};
504

505
unique_ptr<istream> Reader::GetStream() {
×
506
        return unique_ptr<istream>(
507
                new istreamWithUniqueBuffer(unique_ptr<ReaderStreamBuffer>(new ReaderStreamBuffer(*this))));
×
508
};
509

510
ExpectedIfstream OpenIfstream(const string &path) {
×
511
        ifstream is;
×
512
        errno = 0;
×
513
        is.open(path);
×
514
        if (!is) {
×
515
                int io_errno = errno;
×
516
                return ExpectedIfstream(expected::unexpected(error::Error(
×
517
                        generic_category().default_error_condition(io_errno),
×
518
                        "Failed to open '" + path + "' for reading")));
×
519
        }
520
        return ExpectedIfstream(std::move(is));
×
521
}
522

523
ExpectedSharedIfstream OpenSharedIfstream(const string &path) {
×
524
        auto exp_is = OpenIfstream(path);
×
525
        if (!exp_is) {
×
526
                return expected::unexpected(exp_is.error());
×
527
        }
528
        return make_shared<ifstream>(std::move(exp_is.value()));
×
529
}
530

531
ExpectedOfstream OpenOfstream(const string &path, bool append) {
×
532
        ofstream os;
×
533
        errno = 0;
×
534
        os.open(path, append ? ios::app : ios::out);
×
535
        if (!os) {
×
536
                int io_errno = errno;
×
537
                return ExpectedOfstream(expected::unexpected(error::Error(
×
538
                        generic_category().default_error_condition(io_errno),
×
539
                        "Failed to open '" + path + "' for writing")));
×
540
        }
541
        return os;
×
542
}
543

544
ExpectedSharedOfstream OpenSharedOfstream(const string &path, bool append) {
×
545
        auto exp_is = OpenOfstream(path, append);
×
546
        if (!exp_is) {
×
547
                return expected::unexpected(exp_is.error());
×
548
        }
549
        return make_shared<ofstream>(std::move(exp_is.value()));
×
550
}
551

552
error::Error WriteStringIntoOfstream(ofstream &os, const string &data) {
×
553
        errno = 0;
×
554
        os.write(data.data(), data.size());
×
555
        if (os.bad() || os.fail()) {
×
556
                int io_errno = errno;
×
557
                return error::Error(
558
                        std::generic_category().default_error_condition(io_errno),
×
559
                        "Failed to write data into the stream");
×
560
        }
561

562
        return error::NoError;
×
563
}
564

565
ExpectedSize StreamReader::Read(vector<uint8_t>::iterator start, vector<uint8_t>::iterator end) {
×
566
        is_->read(reinterpret_cast<char *>(&*start), end - start);
×
567
        if (!is_) {
×
568
                int io_error = errno;
×
569
                return expected::unexpected(
×
570
                        Error(std::generic_category().default_error_condition(io_error), ""));
×
571
        }
572
        return is_->gcount();
573
}
574

575
error::Error FileReader::Rewind() {
×
576
        if (!is_) {
×
577
                auto ex_is = OpenSharedIfstream(path_);
×
578
                if (!ex_is) {
×
579
                        return ex_is.error();
×
580
                }
581
                is_ = ex_is.value();
×
582
        }
583
        if (!(*is_)) {
×
584
                return Error(std::error_condition(std::errc::io_error), "Bad stream, cannot rewind");
×
585
        }
586
        errno = 0;
×
587
        is_->seekg(0, ios::beg);
×
588
        int io_errno = errno;
×
589
        if (!(*is_)) {
×
590
                return Error(
591
                        generic_category().default_error_condition(io_errno),
×
592
                        "Failed to seek to the beginning of the stream");
×
593
        }
594
        return error::NoError;
×
595
}
596

597
ExpectedSize BufferedReader::Read(vector<uint8_t>::iterator start, vector<uint8_t>::iterator end) {
×
598
        if (rewind_done_ && !rewind_consumed_) {
×
599
                // Read from the buffer
600
                auto ex_bytes_read = buffer_reader_.Read(start, end);
×
601
                if (!ex_bytes_read) {
×
602
                        return ex_bytes_read;
603
                }
604

605
                Vsize bytes_read_buffer = ex_bytes_read.value();
×
606

607
                // Because we track the number of bytes, we should never hit EOF.
608
                AssertOrReturnUnexpected(bytes_read_buffer > 0);
×
609
                AssertOrReturnUnexpected(buffer_remaining_ >= bytes_read_buffer);
×
610

611
                buffer_remaining_ -= bytes_read_buffer;
×
612

613
                // When out of bytes, continue with reading from the wrapped reader
614
                if (buffer_remaining_ == 0) {
×
615
                        rewind_consumed_ = true;
×
616
                        if (stop_done_) {
×
617
                                buffer_.clear();
×
618
                        }
619
                }
620

621
                return bytes_read_buffer;
622
        }
623

624
        // Read from the wrapped reader and save copy into the buffer
625
        auto bytes_read = wrapped_reader_.Read(start, end);
×
626
        if (!bytes_read) {
×
627
                return bytes_read;
628
        }
629
        if (!stop_done_) {
×
630
                buffer_.insert(buffer_.end(), start, start + bytes_read.value());
×
631
        }
632
        return bytes_read;
633
}
634

635
ExpectedSize BufferedReader::Rewind() {
×
636
        if (stop_done_ && rewind_done_) {
×
637
                return expected::unexpected(error::Error(
×
638
                        make_error_condition(errc::io_error), "Buffering was stopped, cannot rewind anymore"));
×
639
        }
640
        buffer_reader_.Rewind();
641
        rewind_done_ = true;
×
642
        buffer_remaining_ = buffer_.size();
×
643
        rewind_consumed_ = (buffer_remaining_ == 0);
×
644
        return buffer_remaining_;
645
}
646

647
ExpectedSize BufferedReader::StopBufferingAndRewind() {
×
648
        auto result = Rewind();
×
649
        stop_done_ = true;
×
650
        return result;
×
651
}
652

653
error::Error BufferedReader::StopBufferingAndDiscard() {
×
654
        if (rewind_done_ && !rewind_consumed_) {
×
655
                return error::Error(
656
                        make_error_condition(errc::io_error), "Cannot stop buffering, pending rewind read");
×
657
        }
658
        stop_done_ = true;
×
659
        rewind_consumed_ = true;
×
660
        buffer_.clear();
×
661
        return error::NoError;
×
662
}
663

664
error::Error AsyncBufferedReader::AsyncRead(
×
665
        vector<uint8_t>::iterator start, vector<uint8_t>::iterator end, AsyncIoHandler handler) {
666
        if (rewind_done_ && !rewind_consumed_) {
×
667
                // Read from the buffer
668
                auto ex_bytes_read = buffer_reader_.Read(start, end);
×
669
                if (!ex_bytes_read) {
×
670
                        handler(ex_bytes_read);
×
671
                        return ex_bytes_read.error();
×
672
                }
673

674
                Vsize bytes_read_buffer = ex_bytes_read.value();
×
675

676
                // Because we track the number of bytes, we should never hit EOF.
677
                AssertOrReturnError(bytes_read_buffer > 0);
×
678
                AssertOrReturnError(buffer_remaining_ >= bytes_read_buffer);
×
679

680
                buffer_remaining_ -= bytes_read_buffer;
×
681

682
                // When out of bytes, continue with reading from the wrapped reader
683
                if (buffer_remaining_ == 0) {
×
684
                        rewind_consumed_ = true;
×
685
                        if (stop_done_) {
×
686
                                buffer_.clear();
×
687
                        }
688
                }
689

690
                handler(ex_bytes_read);
×
691
                return error::NoError;
×
692
        }
693

694
        // Read from the wrapped reader and save copy into the buffer
695
        auto wrapper_handler = [this, start, handler](ExpectedSize result) {
×
696
                if (!result) {
×
697
                        handler(result);
×
698
                        return;
×
699
                }
700
                if (!stop_done_) {
×
701
                        buffer_.insert(buffer_.end(), start, start + result.value());
×
702
                }
703
                handler(result);
×
704
        };
×
705
        auto err = wrapped_reader_.AsyncRead(start, end, wrapper_handler);
×
706
        return err;
×
707
}
708

709
ExpectedSize AsyncBufferedReader::Rewind() {
×
710
        if (stop_done_ && rewind_done_) {
×
711
                return expected::unexpected(error::Error(
×
712
                        make_error_condition(errc::io_error), "Buffering was stopped, cannot rewind anymore"));
×
713
        }
714
        buffer_reader_.Rewind();
715
        rewind_done_ = true;
×
716
        buffer_remaining_ = buffer_.size();
×
717
        rewind_consumed_ = (buffer_remaining_ == 0);
×
718
        return buffer_remaining_;
719
}
720

721
ExpectedSize AsyncBufferedReader::StopBufferingAndRewind() {
×
722
        auto result = Rewind();
×
723
        stop_done_ = true;
×
724
        return result;
×
725
}
726

727
error::Error AsyncBufferedReader::StopBufferingAndDiscard() {
×
728
        if (rewind_done_ && !rewind_consumed_) {
×
729
                return error::Error(
730
                        make_error_condition(errc::io_error), "Cannot stop buffering, pending rewind read");
×
731
        }
732
        stop_done_ = true;
×
733
        rewind_consumed_ = true;
×
734
        buffer_.clear();
×
735
        return error::NoError;
×
736
}
737

738
} // namespace io
739
} // namespace common
740
} // namespace mender
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc