~aleteoryx/muditaos

muditaos/module-utils/tar/tar.cpp -rw-r--r-- 3.9 KiB
a405cad6Aleteoryx trim readme 6 days 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
123
124
125
// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md

#include "tar/tar.hpp"

#include <vector>
#include <iterator>

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

    void writeToFile(const tar::entry &entry, const std::filesystem::path &path)
    {
        auto fd = std::fopen(path.c_str(), "wb");
        if (fd == nullptr) {
            throw std::filesystem::filesystem_error("During opening " + path.string(), std::error_code{});
        }

        constexpr std::size_t chunkSize = 256 * 1024;
        auto rawDataBuffer              = std::make_unique<std::uint8_t[]>(chunkSize);

        auto bytesLeft = static_cast<std::size_t>(entry.size());
        while (bytesLeft > 0) {
            const auto bytesToRead = std::min(bytesLeft, chunkSize);
            entry.read(rawDataBuffer.get(), bytesToRead);
            std::fwrite(rawDataBuffer.get(), sizeof(*rawDataBuffer.get()), bytesToRead, fd);
            bytesLeft -= bytesToRead;
        }

        std::fclose(fd);
    }
} // 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{});
        }
        streamBuffer = std::make_unique<char[]>(streamBufferSize);
        setvbuf(handle.stream, streamBuffer.get(), _IOFBF, streamBufferSize);

        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(std::uint8_t *data, const std::size_t size) const
    {
        const auto bytesToRead = std::min(size, static_cast<std::size_t>(tar_header.size));
        if (mtar_read_data(&handle, reinterpret_cast<void *>(data), bytesToRead) != 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()) {
                writeToFile(entry, full_path);
            }
        }
    }
} // namespace tar