// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #ifndef CEPH_RGW_TAR_H #define CEPH_RGW_TAR_H #include #include #include #include #include #include #include #include namespace rgw { namespace tar { static constexpr size_t BLOCK_SIZE = 512; static inline std::pair> interpret_block(const StatusIndicator& status, ceph::bufferlist& bl); class StatusIndicator { friend std::pair> interpret_block(const StatusIndicator& status, ceph::bufferlist& bl); bool is_empty; bool is_eof; StatusIndicator() : is_empty(false), is_eof(false) { } StatusIndicator(const StatusIndicator& prev_status, const bool is_empty) : is_empty(is_empty), is_eof(is_empty && prev_status.empty()) { } public: bool empty() const { return is_empty; } bool eof() const { return is_eof; } static StatusIndicator create() { return StatusIndicator(); } } /* class StatusIndicator */; enum class FileType : char { UNKNOWN = '\0', /* The tar format uses ASCII encoding. */ NORMAL_FILE = '0', DIRECTORY = '5' }; /* enum class FileType */ class HeaderView { protected: /* Everythng is char here (ASCII encoding), so we don't need to worry about * the struct padding. */ const struct header_t { char filename[100]; char __filemode[8]; char __owner_id[8]; char __group_id[8]; char filesize[12]; char lastmod[12]; char checksum[8]; char filetype; char __padding[355]; } *header; static_assert(sizeof(*header) == BLOCK_SIZE, "The TAR header must be exactly BLOCK_SIZE length"); /* The label is far more imporant from what the code really does. */ static size_t pos2len(const size_t pos) { return pos + 1; } public: HeaderView(const char (&header)[BLOCK_SIZE]) : header(reinterpret_cast(header)) { } FileType get_filetype() const { switch (header->filetype) { case static_cast(FileType::NORMAL_FILE): return FileType::NORMAL_FILE; case static_cast(FileType::DIRECTORY): return FileType::DIRECTORY; default: return FileType::UNKNOWN; } } boost::string_ref get_filename() const { return boost::string_ref(header->filename, std::min(sizeof(header->filename), strlen(header->filename))); } size_t get_filesize() const { /* The string_ref is pretty suitable here because tar encodes its * metadata in ASCII. */ const boost::string_ref raw(header->filesize, sizeof(header->filesize)); /* We need to find where the padding ends. */ const auto pad_ends_at = std::min(raw.find_last_not_of('\0'), raw.find_last_not_of(' ')); const auto trimmed = raw.substr(0, pad_ends_at == boost::string_ref::npos ? boost::string_ref::npos : pos2len(pad_ends_at)); size_t sum = 0, mul = 1; for (const char c : boost::adaptors::reverse(trimmed)) { sum += (c - '0') * mul; mul *= 8; } return sum; } }; /* class Header */ static inline std::pair> interpret_block(const StatusIndicator& status, ceph::bufferlist& bl) { static constexpr std::array zero_block = {0, }; const char (&block)[BLOCK_SIZE] = \ reinterpret_cast(*bl.c_str()); if (std::memcmp(zero_block.data(), block, BLOCK_SIZE) == 0) { return std::make_pair(StatusIndicator(status, true), boost::none); } else { return std::make_pair(StatusIndicator(status, false), HeaderView(block)); } } } /* namespace tar */ } /* namespace rgw */ #endif /* CEPH_RGW_TAR_H */