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