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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
|
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
* Ceph - scalable distributed file system
*
* Copyright (C) 2015 Red Hat
*
* This is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software
* Foundation. See file COPYING.
*
*/
#include "MDSUtility.h"
#include "include/rados/librados.hpp"
class InodeStore;
class RecoveryDriver {
protected:
// If true, overwrite structures that generate decoding errors.
bool force_corrupt;
// If true, overwrite root objects during init_roots even if they
// exist
bool force_init;
public:
virtual int init(librados::Rados &rados, const MDSMap *mdsmap) = 0;
void set_force_corrupt(const bool val)
{
force_corrupt = val;
}
void set_force_init(const bool val)
{
force_init = val;
}
/**
* Inject an inode + dentry parents into the metadata pool,
* based on a backtrace recovered from the data pool
*/
virtual int inject_with_backtrace(
const inode_backtrace_t &bt,
uint64_t size,
time_t mtime,
const ceph_file_layout &layout) = 0;
/**
* Inject an inode + dentry into the lost+found directory,
* when all we know about a file is its inode.
*/
virtual int inject_lost_and_found(
inodeno_t ino,
uint64_t size,
time_t mtime,
const ceph_file_layout &layout) = 0;
/**
* Create any missing roots (i.e. mydir, strays, root inode)
*/
virtual int init_roots(
int64_t data_pool_id) = 0;
/**
* Pre-injection check that all the roots are present in
* the metadata pool. Used to avoid parallel workers interfering
* with one another, by cueing the user to go run 'init' on a
* single node before running a parallel scan.
*
* @param result: set to true if roots are present, else set to false
* @returns 0 on no unexpected errors, else error code. Missing objects
* are not considered an unexpected error: check *result for
* this case.
*/
virtual int check_roots(bool *result) = 0;
/**
* Helper to compose dnames for links to lost+found
* inodes.
*/
std::string lost_found_dname(inodeno_t ino)
{
char s[20];
snprintf(s, sizeof(s), "%llx", (unsigned long long)ino);
return std::string(s);
}
RecoveryDriver()
: force_corrupt(false)
{}
virtual ~RecoveryDriver() {}
};
class LocalFileDriver : public RecoveryDriver
{
protected:
const std::string path;
librados::IoCtx &data_io;
int inject_data(
const std::string &file_path,
uint64_t size,
uint32_t chunk_size,
inodeno_t ino);
public:
LocalFileDriver(const std::string &path_, librados::IoCtx &data_io_)
: RecoveryDriver(), path(path_), data_io(data_io_)
{}
// Implement RecoveryDriver interface
int init(librados::Rados &rados, const MDSMap *mdsmap);
int inject_with_backtrace(
const inode_backtrace_t &bt,
uint64_t size,
time_t mtime,
ceph_file_layout const &layout);
int inject_lost_and_found(
inodeno_t ino,
uint64_t size,
time_t mtime,
ceph_file_layout const &layout);
int init_roots(int64_t data_pool_id);
int check_roots(bool *result);
};
/**
* A class that knows how to manipulate CephFS metadata pools
*/
class MetadataDriver : public RecoveryDriver
{
protected:
librados::IoCtx metadata_io;
/**
* Create a .inode object, i.e. root or mydir
*/
int inject_unlinked_inode(inodeno_t inono, int mode, int64_t data_pool_id);
/**
* Check for existence of .inode objects, before
* trying to go ahead and inject metadata.
*/
int root_exists(inodeno_t ino, bool *result);
/**
* Try and read an fnode from a dirfrag
*/
int read_fnode(inodeno_t ino, frag_t frag,
fnode_t *fnode, uint64_t *read_version);
/**
* Try and read a dentry from a dirfrag
*/
int read_dentry(inodeno_t parent_ino, frag_t frag,
const std::string &dname, InodeStore *inode);
int find_or_create_dirfrag(
inodeno_t ino,
frag_t fragment,
bool *created);
int inject_linkage(
inodeno_t dir_ino, const std::string &dname,
const frag_t fragment, const InodeStore &inode);
/**
* Work out which fragment of a directory should contain a named
* dentry, recursing up the trace as necessary to retrieve
* fragtrees.
*/
int get_frag_of(
inodeno_t dirino,
const std::string &dname,
frag_t *result_ft);
public:
// Implement RecoveryDriver interface
int init(librados::Rados &rados, const MDSMap *mdsmap);
int inject_with_backtrace(
const inode_backtrace_t &bt,
uint64_t size,
time_t mtime,
ceph_file_layout const &layout);
int inject_lost_and_found(
inodeno_t ino,
uint64_t size,
time_t mtime,
ceph_file_layout const &layout);
int init_roots(int64_t data_pool_id);
int check_roots(bool *result);
};
class DataScan : public MDSUtility
{
protected:
RecoveryDriver *driver;
// IoCtx for data pool (where we scrape backtraces from)
librados::IoCtx data_io;
// Remember the data pool ID for use in layouts
int64_t data_pool_id;
uint32_t n;
uint32_t m;
/**
* Scan data pool for backtraces, and inject inodes to metadata pool
*/
int scan_inodes();
/**
* Scan data pool for file sizes and mtimes
*/
int scan_extents();
// Accept pools which are not in the MDSMap
bool force_pool;
// Respond to decode errors by overwriting
bool force_corrupt;
// Overwrite root objects even if they exist
bool force_init;
// Only scan inodes without this scrub tag
string filter_tag;
/**
* @param r set to error on valid key with invalid value
* @return true if argument consumed, else false
*/
bool parse_kwarg(
const std::vector<const char*> &args,
std::vector<const char *>::const_iterator &i,
int *r);
/**
* @return true if argument consumed, else false
*/
bool parse_arg(
const std::vector<const char*> &arg,
std::vector<const char *>::const_iterator &i);
public:
void usage();
int main(const std::vector<const char *> &args);
DataScan()
: driver(NULL), data_pool_id(-1), n(0), m(1),
force_pool(false), force_corrupt(false),
force_init(false)
{
}
~DataScan()
{
delete driver;
}
};
|