]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
9f95a23c | 2 | // vim: ts=8 sw=2 smarttab ft=cpp |
7c673cae | 3 | |
1e59de90 | 4 | #pragma once |
7c673cae FG |
5 | |
6 | #include <algorithm> | |
7 | #include <array> | |
8 | #include <cstring> | |
f67539c2 | 9 | #include <string_view> |
7c673cae FG |
10 | #include <tuple> |
11 | #include <utility> | |
12 | ||
13 | #include <boost/optional.hpp> | |
14 | #include <boost/range/adaptor/reversed.hpp> | |
7c673cae FG |
15 | |
16 | namespace rgw { | |
17 | namespace tar { | |
18 | ||
19 | static constexpr size_t BLOCK_SIZE = 512; | |
20 | ||
21 | ||
22 | static inline std::pair<class StatusIndicator, | |
23 | boost::optional<class HeaderView>> | |
24 | interpret_block(const StatusIndicator& status, ceph::bufferlist& bl); | |
25 | ||
26 | ||
27 | class StatusIndicator { | |
28 | friend std::pair<class StatusIndicator, | |
29 | boost::optional<class HeaderView>> | |
30 | interpret_block(const StatusIndicator& status, ceph::bufferlist& bl); | |
31 | ||
32 | bool is_empty; | |
33 | bool is_eof; | |
34 | ||
35 | StatusIndicator() | |
36 | : is_empty(false), | |
37 | is_eof(false) { | |
38 | } | |
39 | ||
40 | StatusIndicator(const StatusIndicator& prev_status, | |
41 | const bool is_empty) | |
42 | : is_empty(is_empty), | |
43 | is_eof(is_empty && prev_status.empty()) { | |
44 | } | |
45 | ||
46 | public: | |
47 | bool empty() const { | |
48 | return is_empty; | |
49 | } | |
50 | ||
51 | bool eof() const { | |
52 | return is_eof; | |
53 | } | |
54 | ||
55 | static StatusIndicator create() { | |
56 | return StatusIndicator(); | |
57 | } | |
58 | } /* class StatusIndicator */; | |
59 | ||
60 | ||
61 | enum class FileType : char { | |
62 | UNKNOWN = '\0', | |
63 | ||
64 | /* The tar format uses ASCII encoding. */ | |
65 | NORMAL_FILE = '0', | |
66 | DIRECTORY = '5' | |
67 | }; /* enum class FileType */ | |
68 | ||
69 | class HeaderView { | |
70 | protected: | |
1e59de90 | 71 | /* Everything is char here (ASCII encoding), so we don't need to worry about |
7c673cae FG |
72 | * the struct padding. */ |
73 | const struct header_t { | |
74 | char filename[100]; | |
75 | char __filemode[8]; | |
76 | char __owner_id[8]; | |
77 | char __group_id[8]; | |
78 | char filesize[12]; | |
79 | char lastmod[12]; | |
80 | char checksum[8]; | |
81 | char filetype; | |
82 | char __padding[355]; | |
83 | } *header; | |
84 | ||
85 | static_assert(sizeof(*header) == BLOCK_SIZE, | |
86 | "The TAR header must be exactly BLOCK_SIZE length"); | |
87 | ||
1e59de90 | 88 | /* The label is far more important from what the code really does. */ |
7c673cae FG |
89 | static size_t pos2len(const size_t pos) { |
90 | return pos + 1; | |
91 | } | |
92 | ||
93 | public: | |
11fdf7f2 | 94 | explicit HeaderView(const char (&header)[BLOCK_SIZE]) |
7c673cae FG |
95 | : header(reinterpret_cast<const header_t*>(header)) { |
96 | } | |
97 | ||
98 | FileType get_filetype() const { | |
99 | switch (header->filetype) { | |
100 | case static_cast<char>(FileType::NORMAL_FILE): | |
101 | return FileType::NORMAL_FILE; | |
102 | case static_cast<char>(FileType::DIRECTORY): | |
103 | return FileType::DIRECTORY; | |
104 | default: | |
105 | return FileType::UNKNOWN; | |
106 | } | |
107 | } | |
108 | ||
f67539c2 TL |
109 | std::string_view get_filename() const { |
110 | return std::string_view(header->filename, | |
7c673cae FG |
111 | std::min(sizeof(header->filename), |
112 | strlen(header->filename))); | |
113 | } | |
114 | ||
115 | size_t get_filesize() const { | |
116 | /* The string_ref is pretty suitable here because tar encodes its | |
117 | * metadata in ASCII. */ | |
f67539c2 | 118 | const std::string_view raw(header->filesize, sizeof(header->filesize)); |
7c673cae FG |
119 | |
120 | /* We need to find where the padding ends. */ | |
121 | const auto pad_ends_at = std::min(raw.find_last_not_of('\0'), | |
122 | raw.find_last_not_of(' ')); | |
123 | const auto trimmed = raw.substr(0, | |
f67539c2 | 124 | pad_ends_at == std::string_view::npos ? std::string_view::npos |
7c673cae FG |
125 | : pos2len(pad_ends_at)); |
126 | ||
127 | size_t sum = 0, mul = 1; | |
128 | for (const char c : boost::adaptors::reverse(trimmed)) { | |
129 | sum += (c - '0') * mul; | |
130 | mul *= 8; | |
131 | } | |
132 | ||
133 | return sum; | |
134 | } | |
135 | }; /* class Header */ | |
136 | ||
137 | ||
138 | static inline std::pair<StatusIndicator, | |
139 | boost::optional<HeaderView>> | |
140 | interpret_block(const StatusIndicator& status, ceph::bufferlist& bl) { | |
141 | static constexpr std::array<char, BLOCK_SIZE> zero_block = {0, }; | |
142 | const char (&block)[BLOCK_SIZE] = \ | |
143 | reinterpret_cast<const char (&)[BLOCK_SIZE]>(*bl.c_str()); | |
144 | ||
145 | if (std::memcmp(zero_block.data(), block, BLOCK_SIZE) == 0) { | |
146 | return std::make_pair(StatusIndicator(status, true), boost::none); | |
147 | } else { | |
148 | return std::make_pair(StatusIndicator(status, false), HeaderView(block)); | |
149 | } | |
150 | } | |
151 | ||
152 | } /* namespace tar */ | |
153 | } /* namespace rgw */ |