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

mendersoftware / mender / 1022567176

02 Oct 2023 07:50AM UTC coverage: 80.127% (+2.5%) from 77.645%
1022567176

push

gitlab-ci

kacf
chore: Centralize selection of `std::filesystem` library.

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

6447 of 8046 relevant lines covered (80.13%)

9912.21 hits per line

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

73.58
/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 <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(
3✔
36
        vector<uint8_t>::iterator start,
37
        vector<uint8_t>::iterator end,
38
        RepeatedAsyncIoHandler handler) {
39
        class Functor {
8,453✔
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) {
3,173✔
46
                        while (repeat == Repeat::Yes) {
3,173✔
47
                                auto err = reader.AsyncRead(start, end, *this);
6,340✔
48
                                if (err == error::NoError) {
3,170✔
49
                                        break;
50
                                } else {
51
                                        repeat = handler(expected::unexpected(err));
×
52
                                }
53
                        }
54
                }
3,173✔
55
                void operator()(ExpectedSize num_read) {
3,170✔
56
                        auto repeat = handler(num_read);
3,170✔
57
                        ScheduleNextRead(repeat);
3,170✔
58
                }
3,170✔
59
        };
60
        Functor func {*this, start, end, handler};
3✔
61
        func.ScheduleNextRead(Repeat::Yes);
3✔
62
}
3✔
63

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

69
Error Copy(Writer &dst, Reader &src, vector<uint8_t> &buffer) {
201✔
70
        while (true) {
71
                auto r_result = src.Read(buffer.begin(), buffer.end());
1,330✔
72
                if (!r_result) {
1,330✔
73
                        return r_result.error();
3✔
74
                } else if (r_result.value() == 0) {
1,327✔
75
                        return NoError;
195✔
76
                } else if (r_result.value() > buffer.size()) {
1,132✔
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());
1,132✔
83
                if (!w_result) {
1,132✔
84
                        return w_result.error();
1✔
85
                } else if (w_result.value() == 0) {
1,131✔
86
                        // Should this even happen?
87
                        return Error(std::error_condition(std::errc::io_error), "Zero write when copying data");
2✔
88
                } else if (r_result.value() != w_result.value()) {
1,130✔
89
                        return Error(
90
                                std::error_condition(std::errc::io_error), "Short write when copying data");
2✔
91
                }
92
        }
93
}
94

95
struct CopyData {
116✔
96
        CopyData(size_t limit) :
116✔
97
                buf(MENDER_BUFSIZE),
98
                limit {limit} {
116✔
99
        }
116✔
100

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

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

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

126

127
                        if (*size == 0) {
2,178✔
128
                                finished_handler(error::NoError);
69✔
129
                                return;
69✔
130
                        }
131

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

142
                        data->copied += *n;
2,107✔
143

144
                        size_t to_copy = min(data->limit - data->copied, data->buf.size());
2,107✔
145
                        if (to_copy == 0) {
2,107✔
146
                                finished_handler(error::NoError);
2✔
147
                                return;
2✔
148
                        }
149

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

159
                WriterPtr writer;
160
                AsyncReaderPtr reader;
161
                shared_ptr<CopyData> data;
162
                function<void(Error)> finished_handler;
163
        };
164
        size_t to_copy = min(data->limit, data->buf.size());
208✔
165
        auto err = src->AsyncRead(
166
                data->buf.begin(), data->buf.begin() + to_copy, Functor {dst, src, data, finished_handler});
312✔
167
        if (err != error::NoError) {
104✔
168
                finished_handler(err);
×
169
        }
170
}
104✔
171

172
void AsyncCopy(
×
173
        AsyncWriter &dst, Reader &src, function<void(Error)> finished_handler, size_t stop_after) {
174
        AsyncCopy(
×
175
                AsyncWriterPtr(&dst, [](AsyncWriter *) {}),
×
176
                ReaderPtr(&src, [](Reader *) {}),
×
177
                finished_handler,
178
                stop_after);
×
179
}
×
180

181
void AsyncCopy(
4✔
182
        AsyncWriterPtr dst, ReaderPtr src, function<void(Error)> finished_handler, size_t stop_after) {
183
        auto data = make_shared<CopyData>(stop_after);
4✔
184

185
        class Functor {
186
        public:
187
                void operator()(ExpectedSize exp_written) {
157✔
188
                        if (!exp_written) {
157✔
189
                                finished_handler(exp_written.error());
×
190
                                return;
2✔
191
                        } else if (exp_written.value() != expected_written) {
157✔
192
                                finished_handler(Error(
×
193
                                        make_error_condition(std::errc::io_error), "Short write when copying data"));
×
194
                                return;
×
195
                        }
196

197
                        data->copied += *exp_written;
157✔
198

199
                        size_t to_copy = min(data->limit - data->copied, data->buf.size());
157✔
200
                        if (to_copy == 0) {
157✔
201
                                finished_handler(error::NoError);
×
202
                                return;
×
203
                        }
204

205
                        auto exp_read = reader->Read(data->buf.begin(), data->buf.begin() + to_copy);
157✔
206
                        if (!exp_read) {
157✔
207
                                finished_handler(exp_read.error());
×
208
                                return;
×
209
                        }
210
                        auto &read = exp_read.value();
157✔
211

212
                        if (read == 0) {
157✔
213
                                finished_handler(error::NoError);
2✔
214
                                return;
2✔
215
                        }
216

217
                        auto err = writer->AsyncWrite(
218
                                data->buf.begin(),
219
                                data->buf.begin() + read,
155✔
220
                                Functor {writer, reader, finished_handler, data, read});
465✔
221
                        if (err != error::NoError) {
155✔
222
                                finished_handler(err);
×
223
                        }
224
                }
225

226
                AsyncWriterPtr writer;
227
                ReaderPtr reader;
228
                function<void(Error)> finished_handler;
229
                shared_ptr<CopyData> data;
230
                size_t expected_written;
231
        };
232

233
        Functor initial {dst, src, finished_handler, data, 0};
8✔
234
        initial(0);
8✔
235
}
4✔
236

237
void AsyncCopy(
×
238
        AsyncWriter &dst, AsyncReader &src, function<void(Error)> finished_handler, size_t stop_after) {
239
        AsyncCopy(
×
240
                AsyncWriterPtr(&dst, [](AsyncWriter *) {}),
×
241
                AsyncReaderPtr(&src, [](AsyncReader *) {}),
×
242
                finished_handler,
243
                stop_after);
×
244
}
×
245

246
class AsyncCopyReaderFunctor {
247
public:
248
        void operator()(io::ExpectedSize exp_size);
249

250
        AsyncWriterPtr writer;
251
        AsyncReaderPtr reader;
252
        function<void(Error)> finished_handler;
253
        shared_ptr<CopyData> data;
254
};
255

256
class AsyncCopyWriterFunctor {
257
public:
258
        void operator()(io::ExpectedSize exp_size);
259

260
        AsyncWriterPtr writer;
261
        AsyncReaderPtr reader;
262
        function<void(Error)> finished_handler;
263
        shared_ptr<CopyData> data;
264
        size_t expected_written;
265
};
266

267
void AsyncCopyReaderFunctor::operator()(io::ExpectedSize exp_size) {
160✔
268
        if (!exp_size) {
160✔
269
                finished_handler(exp_size.error());
6✔
270
                return;
6✔
271
        }
272
        if (exp_size.value() == 0) {
154✔
273
                finished_handler(error::NoError);
×
274
                return;
×
275
        }
276

277
        auto err = writer->AsyncWrite(
278
                data->buf.begin(),
279
                data->buf.begin() + exp_size.value(),
154✔
280
                AsyncCopyWriterFunctor {writer, reader, finished_handler, data, exp_size.value()});
616✔
281
        if (err != error::NoError) {
154✔
282
                finished_handler(err);
×
283
        }
284
}
285

286
void AsyncCopyWriterFunctor::operator()(io::ExpectedSize exp_size) {
153✔
287
        if (!exp_size) {
153✔
288
                finished_handler(exp_size.error());
×
289
                return;
×
290
        }
291
        if (exp_size.value() != expected_written) {
153✔
292
                finished_handler(
×
293
                        error::Error(make_error_condition(errc::io_error), "Short write in AsyncCopy"));
×
294
                return;
×
295
        }
296

297
        data->copied += *exp_size;
153✔
298

299
        size_t to_copy = min(data->limit - data->copied, data->buf.size());
153✔
300
        if (to_copy == 0) {
153✔
301
                finished_handler(error::NoError);
×
302
                return;
×
303
        }
304

305
        auto err = reader->AsyncRead(
306
                data->buf.begin(),
307
                data->buf.begin() + to_copy,
308
                AsyncCopyReaderFunctor {writer, reader, finished_handler, data});
612✔
309
        if (err != error::NoError) {
153✔
310
                finished_handler(err);
×
311
        }
312
}
313

314
void AsyncCopy(
8✔
315
        AsyncWriterPtr dst,
316
        AsyncReaderPtr src,
317
        function<void(Error)> finished_handler,
318
        size_t stop_after) {
319
        auto data = make_shared<CopyData>(stop_after);
8✔
320

321
        size_t to_copy = min(data->limit, data->buf.size());
16✔
322
        auto err = src->AsyncRead(
323
                data->buf.begin(),
324
                data->buf.begin() + to_copy,
325
                AsyncCopyReaderFunctor {dst, src, finished_handler, data});
32✔
326
        if (err != error::NoError) {
8✔
327
                finished_handler(err);
×
328
        }
329
}
8✔
330

331
void ByteWriter::SetUnlimited(bool enabled) {
222✔
332
        unlimited_ = enabled;
222✔
333
}
222✔
334

335
ExpectedSize ByteWriter::Write(
5,833✔
336
        vector<uint8_t>::const_iterator start, vector<uint8_t>::const_iterator end) {
337
        assert(end > start);
338
        Vsize max_write {receiver_->size() - bytes_written_};
5,833✔
339
        if (max_write == 0 && !unlimited_) {
5,833✔
340
                return expected::unexpected(Error(make_error_condition(errc::no_space_on_device), ""));
×
341
        }
342
        Vsize iterator_size {static_cast<Vsize>(end - start)};
5,833✔
343
        Vsize bytes_to_write;
344
        if (unlimited_) {
5,833✔
345
                bytes_to_write = iterator_size;
346
                if (max_write < bytes_to_write) {
5,800✔
347
                        receiver_->resize(bytes_written_ + bytes_to_write);
5,800✔
348
                        max_write = bytes_to_write;
349
                }
350
        } else {
351
                bytes_to_write = min(iterator_size, max_write);
33✔
352
        }
353
        auto it = next(receiver_->begin(), bytes_written_);
5,833✔
354
        std::copy_n(start, bytes_to_write, it);
5,833✔
355
        bytes_written_ += bytes_to_write;
5,833✔
356
        return bytes_to_write;
357
}
358

359

360
ExpectedSize StreamWriter::Write(
12✔
361
        vector<uint8_t>::const_iterator start, vector<uint8_t>::const_iterator end) {
362
        os_->write(reinterpret_cast<const char *>(&*start), end - start);
12✔
363
        if (!(*(os_.get()))) {
12✔
364
                return expected::unexpected(Error(make_error_condition(errc::io_error), ""));
×
365
        }
366
        return end - start;
367
}
368

369
class ReaderStreamBuffer : public streambuf {
370
public:
371
        ReaderStreamBuffer(Reader &reader) :
477✔
372
                reader_ {reader},
373
                buf_(buf_size_) {};
477✔
374
        streambuf::int_type underflow() override;
375

376
private:
377
        static const Vsize buf_size_ = MENDER_BUFSIZE;
378
        Reader &reader_;
379
        vector<uint8_t> buf_;
380
};
381

382
streambuf::int_type ReaderStreamBuffer::underflow() {
899✔
383
        // eback -- pointer to the first char (byte)
384
        // gptr  -- pointer to the current char (byte)
385
        // egptr -- pointer past the last char (byte)
386

387
        // This function is only called if gptr() == nullptr or gptr() >= egptr(),
388
        // i.e. if there's nothing more to read.
389
        if (this->gptr() >= this->egptr()) {
899✔
390
                errno = 0;
899✔
391
                auto ex_n_read = reader_.Read(buf_.begin(), buf_.end());
899✔
392
                streamsize n_read;
393
                if (ex_n_read) {
899✔
394
                        n_read = ex_n_read.value();
899✔
395
                } else {
396
                        // There is no way to return an error from underflow(), generally
397
                        // the streams only care about how much data was read. No data or
398
                        // less data then requested by the caller of istream.read() means
399
                        // eofbit and failbit are set. If the user code wants to get the
400
                        // error or check if there was an error, it needs to check errno.
401
                        //
402
                        // So as long as we don't clear errno after a failure in the
403
                        // reader_.Read() above, error handling works as usual and returning
404
                        // eof below is all that needs to happen here.
405
                        //
406
                        // In case errno is not set for some reason, let's try to get it
407
                        // from the error with a fallback to a generic I/O error.
408
                        if (errno == 0) {
×
409
                                if (ex_n_read.error().code.category() == generic_category()) {
×
410
                                        errno = ex_n_read.error().code.value();
×
411
                                } else {
412
                                        errno = EIO;
×
413
                                }
414
                        }
415
                        n_read = 0;
416
                }
417

418
                streambuf::char_type *first = reinterpret_cast<streambuf::char_type *>(buf_.data());
419

420
                // set eback, gptr, egptr
421
                this->setg(first, first, first + n_read);
899✔
422
        }
423

424
        return this->gptr() == this->egptr() ? std::char_traits<char>::eof()
899✔
425
                                                                                 : std::char_traits<char>::to_int_type(*this->gptr());
899✔
426
};
427

428
/**
429
 * A variant of the #istream class that takes ownership of the #streambuf buffer
430
 * created for it.
431
 *
432
 * @note Base #istream is designed to work on shared buffers so it doesn't
433
 *       destruct/delete the buffer.
434
 */
435
class istreamWithUniqueBuffer : public istream {
436
public:
437
        // The unique_ptr, &&buf and std::move() model this really nicely -- a
438
        // unique_ptr rvalue (i.e. temporary) is required and it's moved into the
439
        // object. The default destructor then takes care of cleaning up properly.
440
        istreamWithUniqueBuffer(unique_ptr<streambuf> &&buf) :
477✔
441
                istream(buf.get()),
442
                buf_ {std::move(buf)} {};
477✔
443

444
private:
445
        unique_ptr<streambuf> buf_;
446
};
447

448
unique_ptr<istream> Reader::GetStream() {
477✔
449
        return unique_ptr<istream>(
450
                new istreamWithUniqueBuffer(unique_ptr<ReaderStreamBuffer>(new ReaderStreamBuffer(*this))));
477✔
451
};
452

453
ExpectedIfstream OpenIfstream(const string &path) {
274✔
454
        ifstream is;
548✔
455
        errno = 0;
274✔
456
        is.open(path);
274✔
457
        if (!is) {
274✔
458
                int io_errno = errno;
6✔
459
                return ExpectedIfstream(expected::unexpected(error::Error(
6✔
460
                        generic_category().default_error_condition(io_errno),
12✔
461
                        "Failed to open '" + path + "' for reading")));
18✔
462
        }
463
        return ExpectedIfstream(std::move(is));
268✔
464
}
465

466
ExpectedSharedIfstream OpenSharedIfstream(const string &path) {
7✔
467
        auto exp_is = OpenIfstream(path);
7✔
468
        if (!exp_is) {
7✔
469
                return expected::unexpected(exp_is.error());
×
470
        }
471
        return make_shared<ifstream>(std::move(exp_is.value()));
14✔
472
}
473

474
ExpectedOfstream OpenOfstream(const string &path, bool append) {
1,040✔
475
        ofstream os;
2,080✔
476
        errno = 0;
1,040✔
477
        os.open(path, append ? ios::app : ios::out);
1,986✔
478
        if (!os) {
1,040✔
479
                int io_errno = errno;
1✔
480
                return ExpectedOfstream(expected::unexpected(error::Error(
1✔
481
                        generic_category().default_error_condition(io_errno),
2✔
482
                        "Failed to open '" + path + "' for writing")));
3✔
483
        }
484
        return os;
1,039✔
485
}
486

487
ExpectedSharedOfstream OpenSharedOfstream(const string &path, bool append) {
×
488
        auto exp_is = OpenOfstream(path, append);
×
489
        if (!exp_is) {
×
490
                return expected::unexpected(exp_is.error());
×
491
        }
492
        return make_shared<ofstream>(std::move(exp_is.value()));
×
493
}
494

495
error::Error WriteStringIntoOfstream(ofstream &os, const string &data) {
762✔
496
        errno = 0;
762✔
497
        os.write(data.data(), data.size());
762✔
498
        if (os.bad() || os.fail()) {
762✔
499
                int io_errno = errno;
1✔
500
                return error::Error(
501
                        std::generic_category().default_error_condition(io_errno),
2✔
502
                        "Failed to write data into the stream");
2✔
503
        }
504

505
        return error::NoError;
761✔
506
}
507

508
ExpectedSize StreamReader::Read(vector<uint8_t>::iterator start, vector<uint8_t>::iterator end) {
1,588✔
509
        is_->read(reinterpret_cast<char *>(&*start), end - start);
1,588✔
510
        if (!is_) {
1,588✔
511
                int io_error = errno;
×
512
                return expected::unexpected(
×
513
                        Error(std::generic_category().default_error_condition(io_error), ""));
×
514
        }
515
        return is_->gcount();
516
}
517

518
error::Error FileReader::Rewind() {
4✔
519
        if (!is_) {
4✔
520
                auto ex_is = OpenSharedIfstream(path_);
3✔
521
                if (!ex_is) {
3✔
522
                        return ex_is.error();
×
523
                }
524
                is_ = ex_is.value();
3✔
525
        }
526
        if (!(*is_)) {
4✔
527
                return Error(std::error_condition(std::errc::io_error), "Bad stream, cannot rewind");
×
528
        }
529
        errno = 0;
4✔
530
        is_->seekg(0, ios::beg);
4✔
531
        int io_errno = errno;
4✔
532
        if (!(*is_)) {
4✔
533
                return Error(
534
                        generic_category().default_error_condition(io_errno),
×
535
                        "Failed to seek to the beginning of the stream");
×
536
        }
537
        return error::NoError;
4✔
538
}
539

540
} // namespace io
541
} // namespace common
542
} // 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