~aleteoryx/muditaos

ref: d9a1194e6f203247ebcef4b03f8ce5ebccc7c778 muditaos/module-utils/tar/tar.cpp -rw-r--r-- 3.7 KiB
d9a1194e — Lukasz Mastalerz [BH-1688] Create a standard for logs 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "tar/tar.hpp"

#include <fstream>
#include <vector>
#include <iterator>

namespace
{
    bool isValidDirectoryEntry(const tar::entry &entry)
    {
        return entry.name() != "." and entry.name() != ".." and entry.name() != "...";
    }

    void write_to_file(const tar::entry &entry, const std::filesystem::path &path)
    {
        constexpr std::size_t chunk_size = 1024 * 128;
        std::ofstream out_file{path, std::ios::binary};
        if (not out_file.is_open()) {
            throw std::filesystem::filesystem_error("During opening " + path.string(), std::error_code{});
        }

        std::vector<std::byte> raw_data(chunk_size);
        auto bytes_left = entry.size();
        while (bytes_left > 0) {
            const std::size_t to_read = bytes_left > chunk_size ? chunk_size : bytes_left;
            entry.read(raw_data.data(), to_read);

            out_file.write(reinterpret_cast<const char *>(raw_data.data()), to_read);
            bytes_left -= to_read;
        }
    }
} // namespace

namespace tar
{
    entry::entry(const std::filesystem::path &path)
    {
        if (mtar_open(&handle, path.c_str(), "rb") != MTAR_ESUCCESS) {
            throw std::filesystem::filesystem_error("During opening tar file " + path.string(), std::error_code{});
        }
        if (mtar_read_header(&handle, &tar_header) != MTAR_ESUCCESS) {
            throw std::filesystem::filesystem_error("During reading from tar file " + path.string(), std::error_code{});
        }
    }
    entry::~entry()
    {
        mtar_close(&handle);
    }
    std::size_t entry::size() const
    {
        return tar_header.size;
    }
    bool entry::is_file() const
    {
        return tar_header.type == MTAR_TREG;
    }
    bool entry::is_directory() const
    {
        return tar_header.type == MTAR_TDIR;
    }
    std::filesystem::path entry::name() const
    {
        return tar_header.name;
    }
    void entry::read(const std::byte *data, const std::size_t size) const
    {
        const std::size_t to_read = size > tar_header.size ? tar_header.size : size;
        if (mtar_read_data(&handle, (void *)(data), to_read) != MTAR_ESUCCESS) {
            throw std::filesystem::filesystem_error("During reading from tar file", std::error_code{});
        }
    }

    iterator::iterator(const std::filesystem::path &path) : entry_{new entry(path)}
    {}
    entry &iterator::operator*() const
    {
        return *entry_;
    }
    iterator::pointer iterator::operator->() const
    {
        return &**this;
    }
    iterator &iterator::operator++()
    {
        if (mtar_next(&entry_->handle) != MTAR_ESUCCESS or
            mtar_read_header(&entry_->handle, &entry_->tar_header) != MTAR_ESUCCESS) {
            entry_ = {};
        }

        return *this;
    }
    iterator iterator::operator++(int)
    {
        iterator tmp = *this;
        ++(*this);
        return tmp;
    }
    bool operator==(const iterator &a, const iterator &b)
    {
        return a.entry_ == b.entry_;
    }
    bool operator!=(const iterator &a, const iterator &b)
    {
        return a.entry_ != b.entry_;
    }

    void unpack(const std::filesystem::path &path, const std::filesystem::path &where)
    {
        for (const auto &entry : iterator(path)) {
            const auto full_path = where / entry.name();
            if (entry.is_directory() and isValidDirectoryEntry(entry)) {
                std::filesystem::create_directories(full_path);
            }
            else if (entry.is_file()) {
                write_to_file(entry, full_path);
            }
        }
    }
} // namespace tar