1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2010 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include "dump_common.h"
19 
print_usage(void)20 void print_usage(void)
21 {
22     printf("\nUsage: forestdb_dump [OPTION]... [filename]\n"
23     "\nOptions:\n"
24     "\n      --header-only         only print the header of a given ForestDB file"
25     "\n      --key <key>           dump only specified document"
26     "\n      --kvs <KV store name> name of KV store to be dumped"
27     "\n      --byid                sort output by document id"
28     "\n      --byseq               sort output by sequence number"
29     "\n      --hex-key             convert document id to hex (for binary key)"
30     "\n      --hex-body            convert document body data to hex (for binary data)"
31     "\n      --hex-align           number of bytes of hex alignment (default 16)"
32     "\n      --plain-meta          print meta data in plain text (default hex)"
33     "\n      --no-body             do not retrieve document bodies"
34     "\n      --no-meta             do not print meta data of documents"
35     "\n");
36 }
37 
38 typedef enum  {
39     SCAN_BY_KEY = 1,
40     SCAN_BY_SEQ = 2,
41 } scan_mode_t;
42 
43 struct dump_option{
44     char *dump_file;
45     char *one_key;
46     char *one_kvs;
47     int hex_align;
48     bool no_body;
49     bool no_meta;
50     bool print_key_in_hex;
51     bool print_plain_meta;
52     bool print_body_in_hex;
53     bool print_header_only;
54     scan_mode_t scan_mode;
55 };
56 
print_buf(fdb_kvs_handle *db, void *buf, size_t buflen, bool hex, int align)57 INLINE void print_buf(fdb_kvs_handle *db, void *buf, size_t buflen, bool hex,
58                       int align)
59 {
60     if (buf) {
61         if (!hex) {
62             // plaintext
63             printf("%.*s\n", (int)buflen, (char*)buf);
64         } else {
65             // hex dump
66             size_t i, j;
67             printf("(hex)\n");
68             for (i=0;i<buflen;i+=align) {
69                 printf("        ");
70                 for (j=i; j<i+align; ++j){
71                     if (j<buflen) {
72                         printf("%02x ", ((uint8_t*)buf)[j]);
73                     } else {
74                         printf("   ");
75                     }
76                     if ((j+1)%8 == 0) {
77                         printf(" ");
78                     }
79                 }
80                 printf(" ");
81                 for (j=i; j<i+align && j<buflen; ++j){
82                     // print only readable ascii character
83                     printf("%c",
84                      (0x20 <= ((char*)buf)[j] && ((char*)buf)[j] <= 0x7d)?
85                                ((char*)buf)[j] : '.'  );
86                 }
87                 printf("\n");
88             }
89         }
90     } else {
91         printf("(null)\n");
92     }
93 }
94 
print_doc(fdb_kvs_handle *db, char *kvs_name, uint64_t offset, struct dump_option *opt)95 void print_doc(fdb_kvs_handle *db,
96                char *kvs_name,
97                uint64_t offset,
98                struct dump_option *opt)
99 {
100     uint8_t is_wal_entry;
101     int64_t _offset;
102     void *key;
103     keylen_t keylen;
104     fdb_status wr;
105     fdb_doc fdoc;
106     struct docio_object doc;
107     struct _fdb_key_cmp_info cmp_info;
108 
109     memset(&doc, 0, sizeof(struct docio_object));
110 
111     _offset = docio_read_doc(db->dhandle, offset, &doc, true);
112     if (_offset <= 0) {
113         return;
114     }
115     if (doc.length.flag & DOCIO_TXN_COMMITTED) {
116         offset = doc.doc_offset;
117         _offset = docio_read_doc(db->dhandle, offset, &doc, true);
118         if (_offset <= 0) {
119             return;
120         }
121     }
122 
123     if (db->kvs) {
124         key = (uint8_t*)doc.key + sizeof(fdb_kvs_id_t);
125         keylen = doc.length.keylen - sizeof(fdb_kvs_id_t);
126     } else {
127         key = doc.key;
128         keylen = doc.length.keylen;
129     }
130 
131     printf("Doc ID: ");
132     print_buf(db, key, keylen,
133               opt->print_key_in_hex, opt->hex_align);
134     if (kvs_name) {
135         printf("    KV store name: %s\n", kvs_name);
136     }
137     if (doc.seqnum != SEQNUM_NOT_USED) {
138         printf("    Sequence number: %" _F64 "\n", doc.seqnum);
139     }
140     printf("    Byte offset: %" _F64 "\n", offset);
141 
142     cmp_info.kvs_config = db->kvs_config;
143     cmp_info.kvs = db->kvs;
144     fdoc.key = doc.key;
145     fdoc.keylen = doc.length.keylen;
146     wr = wal_find(&db->file->global_txn, db->file,
147                   &cmp_info, db->shandle, &fdoc, &offset);
148     is_wal_entry = (wr == FDB_RESULT_SUCCESS)?(1):(0);
149     printf("    Indexed by %s\n", (is_wal_entry)?("WAL"):("the main index"));
150     printf("    Length: %d (key), %d (metadata), %d (body)\n",
151            keylen, doc.length.metalen, doc.length.bodylen);
152     if (doc.length.flag & DOCIO_COMPRESSED) {
153         printf("    Compressed body size on disk: %d\n",
154                doc.length.bodylen_ondisk);
155     }
156     if (doc.length.flag & DOCIO_DELETED) {
157         printf("    Status: deleted (timestamp: %u)\n", doc.timestamp);
158     } else {
159         if (doc.length.flag & DOCIO_COMPACT) {
160             printf("    Status: normal (written during compaction)\n");
161         } else {
162             printf("    Status: normal\n");
163         }
164     }
165     if (!opt->no_meta) {
166         printf("    Metadata: ");
167         print_buf(db, doc.meta, doc.length.metalen,
168                   !opt->print_plain_meta, opt->hex_align);
169     }
170     if (!opt->no_body) {
171         printf("    Body: ");
172         print_buf(db, doc.body, doc.length.bodylen,
173                   opt->print_body_in_hex, opt->hex_align);
174     }
175     printf("\n");
176 
177     free(doc.key);
178     free(doc.meta);
179     free(doc.body);
180 }
181 
scan_docs(fdb_kvs_handle *db, struct dump_option *opt, char *kvs_name)182 int scan_docs(fdb_kvs_handle *db, struct dump_option *opt, char *kvs_name)
183 {
184     uint64_t offset;
185     fdb_iterator *fit;
186     fdb_status fs;
187     fdb_doc *fdoc = NULL;
188 
189     if (opt->one_key) {
190         fdb_doc_create(&fdoc, opt->one_key,
191                        strnlen(opt->one_key,FDB_MAX_KEYLEN), NULL, 0, NULL, 0);
192        fs = fdb_get(db, fdoc);
193        if (fs == FDB_RESULT_SUCCESS) {
194            offset = fdoc->offset;
195            print_doc(db, kvs_name, offset, opt);
196        } else { // MB-22046: Also need to be able to print deleted doc
197            fs = fdb_get_metaonly(db, fdoc);
198            if (fs == FDB_RESULT_SUCCESS) {
199                offset = fdoc->offset;
200                print_doc(db, kvs_name, offset, opt);
201            } else {
202                return -1;
203            }
204        }
205        fdb_doc_free(fdoc);
206        fdoc = NULL;
207     } else if (opt->scan_mode == SCAN_BY_KEY) {
208         fs = fdb_iterator_init(db, &fit, NULL, 0, NULL, 0, 0x0);
209         if (fs != FDB_RESULT_SUCCESS) {
210             return -2;
211         }
212         do {
213             if (fdb_iterator_get(fit, &fdoc) == FDB_RESULT_SUCCESS) {
214                 offset = fdoc->offset;
215                 print_doc(db, kvs_name, offset, opt);
216                 fdb_doc_free(fdoc);
217                 fdoc = NULL;
218             }
219         } while(fdb_iterator_next(fit) == FDB_RESULT_SUCCESS);
220         fdb_iterator_close(fit);
221     } else if (opt->scan_mode == SCAN_BY_SEQ) {
222         fs = fdb_iterator_sequence_init(db, &fit, 0, -1, 0x0);
223         if (fs != FDB_RESULT_SUCCESS) {
224             return -2;
225         }
226         do {
227             if (fdb_iterator_get(fit, &fdoc) == FDB_RESULT_SUCCESS) {
228                 offset = fdoc->offset;
229                 print_doc(db, kvs_name, offset, opt);
230                 fdb_doc_free(fdoc);
231                 fdoc = NULL;
232             }
233         } while(fdb_iterator_next(fit) == FDB_RESULT_SUCCESS);
234         fdb_iterator_close(fit);
235     }
236 
237     return 0;
238 }
239 
process_file(struct dump_option *opt)240 int process_file(struct dump_option *opt)
241 {
242     int i, ret;
243     fdb_file_handle *dbfile;
244     fdb_kvs_handle *db;
245     fdb_config config;
246     fdb_kvs_config kvs_config;
247     fdb_kvs_name_list name_list;
248     fdb_status fs;
249     char *filename = opt->dump_file;
250 
251     config = fdb_get_default_config();
252     config.buffercache_size = 0;
253     config.flags = FDB_OPEN_FLAG_RDONLY;
254     fs = fdb_open(&dbfile, filename, &config);
255     if (fs != FDB_RESULT_SUCCESS) {
256         printf("\nUnable to open %s\n", filename);
257         return -3;
258     }
259     if (!opt->one_key && !opt->one_kvs) {
260         // MB-22046: Avoid dumping header for specific kvs or specific key dump
261         print_header(dbfile->root);
262         if (opt->print_header_only) {
263             return 0;
264         }
265     }
266 
267     kvs_config = fdb_get_default_kvs_config();
268 
269     if (dbfile->root->config.multi_kv_instances) {
270         fdb_get_kvs_name_list(dbfile, &name_list);
271         for (i=0; (uint64_t)i<name_list.num_kvs_names; ++i) {
272             if (opt->one_kvs &&
273                 strcmp(opt->one_kvs, name_list.kvs_names[i])) {
274                 continue;
275             }
276 
277             fs = fdb_kvs_open(dbfile, &db, name_list.kvs_names[i], &kvs_config);
278             if (fs != FDB_RESULT_SUCCESS) {
279                 printf("\nUnable to open KV store %s\n", name_list.kvs_names[i]);
280                 continue;
281             }
282             if (db->kvs_config.custom_cmp) {
283                 printf("\nUnable to dump KV store %s due to "
284                        "customized comparison function\n", name_list.kvs_names[i]);
285                 fdb_kvs_close(db);
286                 continue;
287             }
288 
289             ret = scan_docs(db, opt, name_list.kvs_names[i]);
290             if (ret == -1 && opt->one_kvs) {
291                 // Only print key not found if a specific key is accompanied by
292                 // a specific kv store.
293                 // Otherwise scan all kv stores for the same key..
294                 printf("KV store '%s': key not found\n", name_list.kvs_names[i]);
295             }
296             fdb_kvs_close(db);
297         }
298 
299         fdb_free_kvs_name_list(&name_list);
300     } else {
301         fs = fdb_kvs_open(dbfile, &db, NULL, &kvs_config);
302         if (fs != FDB_RESULT_SUCCESS) {
303             printf("\nUnable to open KV store\n");
304             return -3;
305         }
306 
307         printf("\n");
308         ret = scan_docs(db, opt, NULL);
309         if (ret == -1) {
310             printf("Key not found\n");
311         }
312         fdb_kvs_close(db);
313     }
314 
315     fs = fdb_close(dbfile);
316     if (fs != FDB_RESULT_SUCCESS) {
317         printf("\nUnable to close %s\n", filename);
318         return -4;
319     }
320 
321     fdb_shutdown();
322     return 0;
323 }
324 
parse_options(int argc, char **argv, struct dump_option *opt)325 int parse_options(int argc, char **argv, struct dump_option *opt)
326 {
327     // Unfortunately, we cannot use getopt generally
328     // because Windows doesn't support it ..
329     int i = 1;
330 
331     if (argc < 2) {
332         print_usage();
333         return -1;
334     }
335 
336     // load default options ...
337     memset(opt, 0, sizeof(struct dump_option));
338     opt->hex_align = 16;
339     opt->scan_mode = SCAN_BY_KEY;
340 
341     for (i = 1; i < argc; ++i) {
342         if (argv[i][0] == '-' && argv[i][1] == '-') {
343             if (strncmp(argv[i], "--key", 16) == 0) {
344                 opt->one_key = argv[++i];
345             } else if (strncmp(argv[i], "--kvs", 16) == 0) {
346                 opt->one_kvs = argv[++i];
347             } else if (strncmp(argv[i], "--no-body", 16) == 0) {
348                 opt->no_body = true;
349             } else if (strncmp(argv[i], "--no-meta", 16) == 0) {
350                 opt->no_meta = true;
351             } else if (strncmp(argv[i], "--hex-key", 16) == 0) {
352                 opt->print_key_in_hex = true;
353             } else if (strncmp(argv[i], "--plain-meta", 16) == 0) {
354                 opt->print_plain_meta = true;
355             } else if (strncmp(argv[i], "--hex-body", 16) == 0) {
356                 opt->print_body_in_hex = true;
357             } else if (strncmp(argv[i], "--hex-align", 16) == 0) {
358                 opt->hex_align = atoi(argv[++i]);
359             } else if (strncmp(argv[i], "--byid", 16) == 0) {
360                 opt->scan_mode = SCAN_BY_KEY;
361             } else if (strncmp(argv[i], "--byseq", 16) == 0) {
362                 opt->scan_mode = SCAN_BY_SEQ;
363             } else if (strncmp(argv[i], "--header-only", 13) == 0) {
364                 opt->print_header_only = true;
365             } else {
366                 printf("\nUnknown option %s\n", argv[i]);
367                 print_usage();
368                 return -2;
369             }
370         } else {
371             opt->dump_file = argv[i];
372         }
373     }
374 
375     return 0;
376 }
377 
main(int argc, char **argv)378 int main(int argc, char **argv)
379 {
380     memleak_start();
381 
382     struct dump_option opt;
383     int ret = parse_options(argc, argv, &opt);
384 
385     if (ret) {
386         memleak_end();
387         return ret;
388     }
389 
390     ret = process_file(&opt);
391 
392     memleak_end();
393     return ret;
394 }
395