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
154
155
156
|
/*
* Copyright (c) 2020-2022, Peter Elliott <pelliott@serenityos.org>
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/String.h>
#include <AK/StringView.h>
#include <string.h>
#include <sys/types.h>
// glibc before 2.28 defines these from sys/types.h, but we don't want
// TarFileHeader::major() and TarFileHeader::minor() to use those macros
#ifdef minor
# undef minor
#endif
#ifdef major
# undef major
#endif
namespace Archive {
enum class TarFileType : char {
NormalFile = '0',
AlternateNormalFile = '\0',
HardLink = '1',
SymLink = '2',
CharacterSpecialFile = '3',
BlockSpecialFile = '4',
Directory = '5',
FIFO = '6',
ContiguousFile = '7',
GlobalExtendedHeader = 'g',
ExtendedHeader = 'x',
// GNU extensions
LongName = 'L',
};
constexpr size_t block_size = 512;
constexpr StringView gnu_magic = "ustar "sv; // gnu format magic
constexpr StringView gnu_version = " "sv; // gnu format version
constexpr StringView ustar_magic = "ustar"sv; // ustar format magic
constexpr StringView ustar_version = "00"sv; // ustar format version
constexpr StringView posix1_tar_magic = ""sv; // POSIX.1-1988 format magic
constexpr StringView posix1_tar_version = ""sv; // POSIX.1-1988 format version
template<size_t N>
static ErrorOr<size_t> get_field_as_integral(char const (&field)[N])
{
size_t value = 0;
for (size_t i = 0; i < N; ++i) {
if (field[i] == 0 || field[i] == ' ')
break;
if (field[i] < '0' || field[i] > '7')
return Error::from_string_literal("Passed a non-octal value");
value *= 8;
value += field[i] - '0';
}
return value;
}
template<size_t N>
static StringView get_field_as_string_view(char const (&field)[N])
{
return { field, min(__builtin_strlen(field), N) };
}
template<size_t N, class TSource>
static void set_field(char (&field)[N], TSource&& source)
{
VERIFY(N >= source.length());
memcpy(field, StringView(source).characters_without_null_termination(), source.length());
if (N > source.length())
field[source.length()] = 0;
}
template<class TSource, size_t N>
static void set_octal_field(char (&field)[N], TSource&& source)
{
set_field(field, String::formatted("{:o}", forward<TSource>(source)));
}
class [[gnu::packed]] TarFileHeader {
public:
StringView filename() const { return get_field_as_string_view(m_filename); }
ErrorOr<mode_t> mode() const { return TRY(get_field_as_integral(m_mode)); }
ErrorOr<uid_t> uid() const { return TRY(get_field_as_integral(m_uid)); }
ErrorOr<gid_t> gid() const { return TRY(get_field_as_integral(m_gid)); }
// FIXME: support 2001-star size encoding
ErrorOr<size_t> size() const { return TRY(get_field_as_integral(m_size)); }
ErrorOr<time_t> timestamp() const { return TRY(get_field_as_integral(m_timestamp)); }
ErrorOr<unsigned> checksum() const { return TRY(get_field_as_integral(m_checksum)); }
TarFileType type_flag() const { return TarFileType(m_type_flag); }
StringView link_name() const { return { m_link_name, strlen(m_link_name) }; }
StringView magic() const { return get_field_as_string_view(m_magic); }
StringView version() const { return get_field_as_string_view(m_version); }
StringView owner_name() const { return get_field_as_string_view(m_owner_name); }
StringView group_name() const { return get_field_as_string_view(m_group_name); }
ErrorOr<int> major() const { return TRY(get_field_as_integral(m_major)); }
ErrorOr<int> minor() const { return TRY(get_field_as_integral(m_minor)); }
// FIXME: support ustar filename prefix
StringView prefix() const { return get_field_as_string_view(m_prefix); }
void set_filename(StringView filename) { set_field(m_filename, filename); }
void set_mode(mode_t mode) { set_octal_field(m_mode, mode); }
void set_uid(uid_t uid) { set_octal_field(m_uid, uid); }
void set_gid(gid_t gid) { set_octal_field(m_gid, gid); }
void set_size(size_t size) { set_octal_field(m_size, size); }
void set_timestamp(time_t timestamp) { set_octal_field(m_timestamp, timestamp); }
void set_type_flag(TarFileType type) { m_type_flag = to_underlying(type); }
void set_link_name(StringView link_name) { set_field(m_link_name, link_name); }
// magic doesn't necessarily include a null byte
void set_magic(StringView magic) { set_field(m_magic, magic); }
// version doesn't necessarily include a null byte
void set_version(StringView version) { set_field(m_version, version); }
void set_owner_name(StringView owner_name) { set_field(m_owner_name, owner_name); }
void set_group_name(StringView group_name) { set_field(m_group_name, group_name); }
void set_major(int major) { set_octal_field(m_major, major); }
void set_minor(int minor) { set_octal_field(m_minor, minor); }
void set_prefix(StringView prefix) { set_field(m_prefix, prefix); }
unsigned expected_checksum() const;
void calculate_checksum();
bool is_zero_block() const;
bool content_is_like_extended_header() const;
void set_filename_and_prefix(StringView filename);
private:
char m_filename[100] { 0 };
char m_mode[8] { 0 };
char m_uid[8] { 0 };
char m_gid[8] { 0 };
char m_size[12] { 0 };
char m_timestamp[12] { 0 };
char m_checksum[8] { 0 }; // an uninitialized header's checksum is filled with spaces
char m_type_flag { 0 };
char m_link_name[100] { 0 };
char m_magic[6] { 0 };
char m_version[2] { 0 };
char m_owner_name[32] { 0 };
char m_group_name[32] { 0 };
char m_major[8] { 0 };
char m_minor[8] { 0 };
char m_prefix[155] { 0 }; // zero out the prefix for archiving
};
}
|