summaryrefslogtreecommitdiffstats
path: root/src/tools/cephfs/DataScan.h
blob: 5c87fe2bdec4a89a169209ba49445f8e3964d5be (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
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
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
// -*- 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 MDSTable;

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,
	std::string &metadata_pool_name,
        const FSMap *fsmap,
        fs_cluster_id_t fscid) = 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,
        const InodeStore &dentry) = 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,
        const InodeStore &dentry) = 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),
	force_init(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,
	std::string &metadata_pool_name,
        const FSMap *fsmap,
        fs_cluster_id_t fscid) override;

    int inject_with_backtrace(
        const inode_backtrace_t &bt,
        const InodeStore &dentry) override;

    int inject_lost_and_found(
        inodeno_t ino,
        const InodeStore &dentry) override;

    int init_roots(int64_t data_pool_id) override;

    int check_roots(bool *result) override;
};

/**
 * A class that knows how to work with objects in a CephFS
 * metadata pool.
 */
class MetadataTool
{
  protected:

  librados::IoCtx metadata_io;

  /**
   * Construct a synthetic InodeStore for a normal file
   */
  void build_file_dentry(
    inodeno_t ino, uint64_t file_size, time_t file_mtime,
    const file_layout_t &layout,
    InodeStore *out);

  /**
   * Construct a synthetic InodeStore for a directory
   */
  void build_dir_dentry(
    inodeno_t ino,
    const frag_info_t &fragstat,
    const file_layout_t &layout,
    InodeStore *out);

  /**
   * 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, snapid_t *dnfirst=nullptr);
};

/**
 * A class that knows how to manipulate CephFS metadata pools
 */
class MetadataDriver : public RecoveryDriver, public MetadataTool
{
  protected:
    /**
     * 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);
    int find_or_create_dirfrag(
        inodeno_t ino,
        frag_t fragment,
        bool *created);


    /**
     * 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,
	std::string &metadata_pool_name,
        const FSMap *fsmap,
        fs_cluster_id_t fscid) override;

    int inject_linkage(
        inodeno_t dir_ino, const std::string &dname,
        const frag_t fragment, const InodeStore &inode, snapid_t dnfirst=CEPH_NOSNAP);

    int inject_with_backtrace(
        const inode_backtrace_t &bt,
        const InodeStore &dentry) override;

    int inject_lost_and_found(
        inodeno_t ino,
        const InodeStore &dentry) override;

    int init_roots(int64_t data_pool_id) override;

    int check_roots(bool *result) override;

    int load_table(MDSTable *table);
    int save_table(MDSTable *table);
};

class DataScan : public MDSUtility, public MetadataTool
{
  protected:
    RecoveryDriver *driver;
    fs_cluster_id_t fscid;

    string metadata_pool_name;
    std::vector<int64_t> data_pools;

    // IoCtx for data pool (where we scrape file 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();

    /**
     * Scan metadata pool for 0th dirfrags to link orphaned
     * directory inodes.
     */
    int scan_frags();

    /**
     * Cleanup xattrs from data pool
     */
    int cleanup();

    /**
     * Check if an inode number is in the permitted ranges
     */
    bool valid_ino(inodeno_t ino) const;


    int scan_links();

    // Accept pools which are not in the FSMap
    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);

    int probe_filter(librados::IoCtx &ioctx);

    /**
     * Apply a function to all objects in an ioctx's pool, optionally
     * restricted to only those objects with a 00000000 offset and
     * no tag matching DataScan::scrub_tag.
     */
    int forall_objects(
        librados::IoCtx &ioctx,
        bool untagged_only,
        std::function<int(std::string, uint64_t, uint64_t)> handler);

  public:
    static void usage();
    int main(const std::vector<const char *> &args);

    DataScan()
      : driver(NULL), fscid(FS_CLUSTER_ID_NONE),
	data_pool_id(-1), n(0), m(1),
        force_pool(false), force_corrupt(false),
        force_init(false)
    {
    }

    ~DataScan() override
    {
      delete driver;
    }
};