summaryrefslogtreecommitdiffstats
path: root/src/rgw/rgw_tar.h
blob: b06943a3c1852c8cee1d8da2ac37607818047410 (plain)
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab ft=cpp

#pragma once

#include <algorithm>
#include <array>
#include <cstring>
#include <string_view>
#include <tuple>
#include <utility>

#include <boost/optional.hpp>
#include <boost/range/adaptor/reversed.hpp>

namespace rgw {
namespace tar {

static constexpr size_t BLOCK_SIZE = 512;


static inline std::pair<class StatusIndicator,
                        boost::optional<class HeaderView>>
interpret_block(const StatusIndicator& status, ceph::bufferlist& bl);


class StatusIndicator {
  friend std::pair<class StatusIndicator,
                   boost::optional<class HeaderView>>
  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:
  /* Everything 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 important from what the code really does. */
  static size_t pos2len(const size_t pos) {
    return pos + 1;
  }

public:
  explicit HeaderView(const char (&header)[BLOCK_SIZE])
    : header(reinterpret_cast<const header_t*>(header)) {
  }

  FileType get_filetype() const {
    switch (header->filetype) {
      case static_cast<char>(FileType::NORMAL_FILE):
        return FileType::NORMAL_FILE;
      case static_cast<char>(FileType::DIRECTORY):
        return FileType::DIRECTORY;
      default:
        return FileType::UNKNOWN;
    }
  }

  std::string_view get_filename() const {
    return std::string_view(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 std::string_view 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 == std::string_view::npos ? std::string_view::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<StatusIndicator,
                        boost::optional<HeaderView>>
interpret_block(const StatusIndicator& status, ceph::bufferlist& bl) {
  static constexpr std::array<char, BLOCK_SIZE> zero_block = {0, };
  const char (&block)[BLOCK_SIZE] = \
    reinterpret_cast<const char (&)[BLOCK_SIZE]>(*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 */