Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / rgw / rgw_tar.h
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 */