1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
5 //
6 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
9 #include "file/filename.h"
10 #include <cinttypes>
11
12 #include <ctype.h>
13 #include <stdio.h>
14 #include <vector>
15 #include "file/writable_file_writer.h"
16 #include "logging/logging.h"
17 #include "rocksdb/env.h"
18 #include "test_util/sync_point.h"
19 #include "util/stop_watch.h"
20 #include "util/string_util.h"
21
22 namespace ROCKSDB_NAMESPACE {
23
24 static const std::string kRocksDbTFileExt = "sst";
25 static const std::string kLevelDbTFileExt = "ldb";
26 static const std::string kRocksDBBlobFileExt = "blob";
27
28 // Given a path, flatten the path name by replacing all chars not in
29 // {[0-9,a-z,A-Z,-,_,.]} with _. And append '_LOG\0' at the end.
30 // Return the number of chars stored in dest not including the trailing '\0'.
GetInfoLogPrefix(const std::string & path,char * dest,int len)31 static size_t GetInfoLogPrefix(const std::string& path, char* dest, int len) {
32 const char suffix[] = "_LOG";
33
34 size_t write_idx = 0;
35 size_t i = 0;
36 size_t src_len = path.size();
37
38 while (i < src_len && write_idx < len - sizeof(suffix)) {
39 if ((path[i] >= 'a' && path[i] <= 'z') ||
40 (path[i] >= '0' && path[i] <= '9') ||
41 (path[i] >= 'A' && path[i] <= 'Z') ||
42 path[i] == '-' ||
43 path[i] == '.' ||
44 path[i] == '_'){
45 dest[write_idx++] = path[i];
46 } else {
47 if (i > 0) {
48 dest[write_idx++] = '_';
49 }
50 }
51 i++;
52 }
53 assert(sizeof(suffix) <= len - write_idx);
54 // "\0" is automatically added by snprintf
55 snprintf(dest + write_idx, len - write_idx, suffix);
56 write_idx += sizeof(suffix) - 1;
57 return write_idx;
58 }
59
MakeFileName(uint64_t number,const char * suffix)60 static std::string MakeFileName(uint64_t number, const char* suffix) {
61 char buf[100];
62 snprintf(buf, sizeof(buf), "%06llu.%s",
63 static_cast<unsigned long long>(number), suffix);
64 return buf;
65 }
66
MakeFileName(const std::string & name,uint64_t number,const char * suffix)67 static std::string MakeFileName(const std::string& name, uint64_t number,
68 const char* suffix) {
69 return name + "/" + MakeFileName(number, suffix);
70 }
71
LogFileName(const std::string & name,uint64_t number)72 std::string LogFileName(const std::string& name, uint64_t number) {
73 assert(number > 0);
74 return MakeFileName(name, number, "log");
75 }
76
LogFileName(uint64_t number)77 std::string LogFileName(uint64_t number) {
78 assert(number > 0);
79 return MakeFileName(number, "log");
80 }
81
BlobFileName(const std::string & blobdirname,uint64_t number)82 std::string BlobFileName(const std::string& blobdirname, uint64_t number) {
83 assert(number > 0);
84 return MakeFileName(blobdirname, number, kRocksDBBlobFileExt.c_str());
85 }
86
BlobFileName(const std::string & dbname,const std::string & blob_dir,uint64_t number)87 std::string BlobFileName(const std::string& dbname, const std::string& blob_dir,
88 uint64_t number) {
89 assert(number > 0);
90 return MakeFileName(dbname + "/" + blob_dir, number,
91 kRocksDBBlobFileExt.c_str());
92 }
93
ArchivalDirectory(const std::string & dir)94 std::string ArchivalDirectory(const std::string& dir) {
95 return dir + "/" + ARCHIVAL_DIR;
96 }
ArchivedLogFileName(const std::string & name,uint64_t number)97 std::string ArchivedLogFileName(const std::string& name, uint64_t number) {
98 assert(number > 0);
99 return MakeFileName(name + "/" + ARCHIVAL_DIR, number, "log");
100 }
101
MakeTableFileName(const std::string & path,uint64_t number)102 std::string MakeTableFileName(const std::string& path, uint64_t number) {
103 return MakeFileName(path, number, kRocksDbTFileExt.c_str());
104 }
105
MakeTableFileName(uint64_t number)106 std::string MakeTableFileName(uint64_t number) {
107 return MakeFileName(number, kRocksDbTFileExt.c_str());
108 }
109
Rocks2LevelTableFileName(const std::string & fullname)110 std::string Rocks2LevelTableFileName(const std::string& fullname) {
111 assert(fullname.size() > kRocksDbTFileExt.size() + 1);
112 if (fullname.size() <= kRocksDbTFileExt.size() + 1) {
113 return "";
114 }
115 return fullname.substr(0, fullname.size() - kRocksDbTFileExt.size()) +
116 kLevelDbTFileExt;
117 }
118
TableFileNameToNumber(const std::string & name)119 uint64_t TableFileNameToNumber(const std::string& name) {
120 uint64_t number = 0;
121 uint64_t base = 1;
122 int pos = static_cast<int>(name.find_last_of('.'));
123 while (--pos >= 0 && name[pos] >= '0' && name[pos] <= '9') {
124 number += (name[pos] - '0') * base;
125 base *= 10;
126 }
127 return number;
128 }
129
TableFileName(const std::vector<DbPath> & db_paths,uint64_t number,uint32_t path_id)130 std::string TableFileName(const std::vector<DbPath>& db_paths, uint64_t number,
131 uint32_t path_id) {
132 assert(number > 0);
133 std::string path;
134 if (path_id >= db_paths.size()) {
135 path = db_paths.back().path;
136 } else {
137 path = db_paths[path_id].path;
138 }
139 return MakeTableFileName(path, number);
140 }
141
FormatFileNumber(uint64_t number,uint32_t path_id,char * out_buf,size_t out_buf_size)142 void FormatFileNumber(uint64_t number, uint32_t path_id, char* out_buf,
143 size_t out_buf_size) {
144 if (path_id == 0) {
145 snprintf(out_buf, out_buf_size, "%" PRIu64, number);
146 } else {
147 snprintf(out_buf, out_buf_size, "%" PRIu64
148 "(path "
149 "%" PRIu32 ")",
150 number, path_id);
151 }
152 }
153
DescriptorFileName(const std::string & dbname,uint64_t number)154 std::string DescriptorFileName(const std::string& dbname, uint64_t number) {
155 assert(number > 0);
156 char buf[100];
157 snprintf(buf, sizeof(buf), "/MANIFEST-%06llu",
158 static_cast<unsigned long long>(number));
159 return dbname + buf;
160 }
161
CurrentFileName(const std::string & dbname)162 std::string CurrentFileName(const std::string& dbname) {
163 return dbname + "/CURRENT";
164 }
165
LockFileName(const std::string & dbname)166 std::string LockFileName(const std::string& dbname) {
167 return dbname + "/LOCK";
168 }
169
TempFileName(const std::string & dbname,uint64_t number)170 std::string TempFileName(const std::string& dbname, uint64_t number) {
171 return MakeFileName(dbname, number, kTempFileNameSuffix.c_str());
172 }
173
InfoLogPrefix(bool has_log_dir,const std::string & db_absolute_path)174 InfoLogPrefix::InfoLogPrefix(bool has_log_dir,
175 const std::string& db_absolute_path) {
176 if (!has_log_dir) {
177 const char kInfoLogPrefix[] = "LOG";
178 // "\0" is automatically added to the end
179 snprintf(buf, sizeof(buf), kInfoLogPrefix);
180 prefix = Slice(buf, sizeof(kInfoLogPrefix) - 1);
181 } else {
182 size_t len = GetInfoLogPrefix(db_absolute_path, buf, sizeof(buf));
183 prefix = Slice(buf, len);
184 }
185 }
186
InfoLogFileName(const std::string & dbname,const std::string & db_path,const std::string & log_dir)187 std::string InfoLogFileName(const std::string& dbname,
188 const std::string& db_path, const std::string& log_dir) {
189 if (log_dir.empty()) {
190 return dbname + "/LOG";
191 }
192
193 InfoLogPrefix info_log_prefix(true, db_path);
194 return log_dir + "/" + info_log_prefix.buf;
195 }
196
197 // Return the name of the old info log file for "dbname".
OldInfoLogFileName(const std::string & dbname,uint64_t ts,const std::string & db_path,const std::string & log_dir)198 std::string OldInfoLogFileName(const std::string& dbname, uint64_t ts,
199 const std::string& db_path, const std::string& log_dir) {
200 char buf[50];
201 snprintf(buf, sizeof(buf), "%llu", static_cast<unsigned long long>(ts));
202
203 if (log_dir.empty()) {
204 return dbname + "/LOG.old." + buf;
205 }
206
207 InfoLogPrefix info_log_prefix(true, db_path);
208 return log_dir + "/" + info_log_prefix.buf + ".old." + buf;
209 }
210
OptionsFileName(const std::string & dbname,uint64_t file_num)211 std::string OptionsFileName(const std::string& dbname, uint64_t file_num) {
212 char buffer[256];
213 snprintf(buffer, sizeof(buffer), "%s%06" PRIu64,
214 kOptionsFileNamePrefix.c_str(), file_num);
215 return dbname + "/" + buffer;
216 }
217
TempOptionsFileName(const std::string & dbname,uint64_t file_num)218 std::string TempOptionsFileName(const std::string& dbname, uint64_t file_num) {
219 char buffer[256];
220 snprintf(buffer, sizeof(buffer), "%s%06" PRIu64 ".%s",
221 kOptionsFileNamePrefix.c_str(), file_num,
222 kTempFileNameSuffix.c_str());
223 return dbname + "/" + buffer;
224 }
225
MetaDatabaseName(const std::string & dbname,uint64_t number)226 std::string MetaDatabaseName(const std::string& dbname, uint64_t number) {
227 char buf[100];
228 snprintf(buf, sizeof(buf), "/METADB-%llu",
229 static_cast<unsigned long long>(number));
230 return dbname + buf;
231 }
232
IdentityFileName(const std::string & dbname)233 std::string IdentityFileName(const std::string& dbname) {
234 return dbname + "/IDENTITY";
235 }
236
237 // Owned filenames have the form:
238 // dbname/IDENTITY
239 // dbname/CURRENT
240 // dbname/LOCK
241 // dbname/<info_log_name_prefix>
242 // dbname/<info_log_name_prefix>.old.[0-9]+
243 // dbname/MANIFEST-[0-9]+
244 // dbname/[0-9]+.(log|sst|blob)
245 // dbname/METADB-[0-9]+
246 // dbname/OPTIONS-[0-9]+
247 // dbname/OPTIONS-[0-9]+.dbtmp
248 // Disregards / at the beginning
ParseFileName(const std::string & fname,uint64_t * number,FileType * type,WalFileType * log_type)249 bool ParseFileName(const std::string& fname,
250 uint64_t* number,
251 FileType* type,
252 WalFileType* log_type) {
253 return ParseFileName(fname, number, "", type, log_type);
254 }
255
ParseFileName(const std::string & fname,uint64_t * number,const Slice & info_log_name_prefix,FileType * type,WalFileType * log_type)256 bool ParseFileName(const std::string& fname, uint64_t* number,
257 const Slice& info_log_name_prefix, FileType* type,
258 WalFileType* log_type) {
259 Slice rest(fname);
260 if (fname.length() > 1 && fname[0] == '/') {
261 rest.remove_prefix(1);
262 }
263 if (rest == "IDENTITY") {
264 *number = 0;
265 *type = kIdentityFile;
266 } else if (rest == "CURRENT") {
267 *number = 0;
268 *type = kCurrentFile;
269 } else if (rest == "LOCK") {
270 *number = 0;
271 *type = kDBLockFile;
272 } else if (info_log_name_prefix.size() > 0 &&
273 rest.starts_with(info_log_name_prefix)) {
274 rest.remove_prefix(info_log_name_prefix.size());
275 if (rest == "" || rest == ".old") {
276 *number = 0;
277 *type = kInfoLogFile;
278 } else if (rest.starts_with(".old.")) {
279 uint64_t ts_suffix;
280 // sizeof also counts the trailing '\0'.
281 rest.remove_prefix(sizeof(".old.") - 1);
282 if (!ConsumeDecimalNumber(&rest, &ts_suffix)) {
283 return false;
284 }
285 *number = ts_suffix;
286 *type = kInfoLogFile;
287 }
288 } else if (rest.starts_with("MANIFEST-")) {
289 rest.remove_prefix(strlen("MANIFEST-"));
290 uint64_t num;
291 if (!ConsumeDecimalNumber(&rest, &num)) {
292 return false;
293 }
294 if (!rest.empty()) {
295 return false;
296 }
297 *type = kDescriptorFile;
298 *number = num;
299 } else if (rest.starts_with("METADB-")) {
300 rest.remove_prefix(strlen("METADB-"));
301 uint64_t num;
302 if (!ConsumeDecimalNumber(&rest, &num)) {
303 return false;
304 }
305 if (!rest.empty()) {
306 return false;
307 }
308 *type = kMetaDatabase;
309 *number = num;
310 } else if (rest.starts_with(kOptionsFileNamePrefix)) {
311 uint64_t ts_suffix;
312 bool is_temp_file = false;
313 rest.remove_prefix(kOptionsFileNamePrefix.size());
314 const std::string kTempFileNameSuffixWithDot =
315 std::string(".") + kTempFileNameSuffix;
316 if (rest.ends_with(kTempFileNameSuffixWithDot)) {
317 rest.remove_suffix(kTempFileNameSuffixWithDot.size());
318 is_temp_file = true;
319 }
320 if (!ConsumeDecimalNumber(&rest, &ts_suffix)) {
321 return false;
322 }
323 *number = ts_suffix;
324 *type = is_temp_file ? kTempFile : kOptionsFile;
325 } else {
326 // Avoid strtoull() to keep filename format independent of the
327 // current locale
328 bool archive_dir_found = false;
329 if (rest.starts_with(ARCHIVAL_DIR)) {
330 if (rest.size() <= ARCHIVAL_DIR.size()) {
331 return false;
332 }
333 rest.remove_prefix(ARCHIVAL_DIR.size() + 1); // Add 1 to remove / also
334 if (log_type) {
335 *log_type = kArchivedLogFile;
336 }
337 archive_dir_found = true;
338 }
339 uint64_t num;
340 if (!ConsumeDecimalNumber(&rest, &num)) {
341 return false;
342 }
343 if (rest.size() <= 1 || rest[0] != '.') {
344 return false;
345 }
346 rest.remove_prefix(1);
347
348 Slice suffix = rest;
349 if (suffix == Slice("log")) {
350 *type = kLogFile;
351 if (log_type && !archive_dir_found) {
352 *log_type = kAliveLogFile;
353 }
354 } else if (archive_dir_found) {
355 return false; // Archive dir can contain only log files
356 } else if (suffix == Slice(kRocksDbTFileExt) ||
357 suffix == Slice(kLevelDbTFileExt)) {
358 *type = kTableFile;
359 } else if (suffix == Slice(kRocksDBBlobFileExt)) {
360 *type = kBlobFile;
361 } else if (suffix == Slice(kTempFileNameSuffix)) {
362 *type = kTempFile;
363 } else {
364 return false;
365 }
366 *number = num;
367 }
368 return true;
369 }
370
SetCurrentFile(FileSystem * fs,const std::string & dbname,uint64_t descriptor_number,FSDirectory * directory_to_fsync)371 IOStatus SetCurrentFile(FileSystem* fs, const std::string& dbname,
372 uint64_t descriptor_number,
373 FSDirectory* directory_to_fsync) {
374 // Remove leading "dbname/" and add newline to manifest file name
375 std::string manifest = DescriptorFileName(dbname, descriptor_number);
376 Slice contents = manifest;
377 assert(contents.starts_with(dbname + "/"));
378 contents.remove_prefix(dbname.size() + 1);
379 std::string tmp = TempFileName(dbname, descriptor_number);
380 IOStatus s = WriteStringToFile(fs, contents.ToString() + "\n", tmp, true);
381 if (s.ok()) {
382 TEST_KILL_RANDOM("SetCurrentFile:0", rocksdb_kill_odds * REDUCE_ODDS2);
383 s = fs->RenameFile(tmp, CurrentFileName(dbname), IOOptions(), nullptr);
384 TEST_KILL_RANDOM("SetCurrentFile:1", rocksdb_kill_odds * REDUCE_ODDS2);
385 }
386 if (s.ok()) {
387 if (directory_to_fsync != nullptr) {
388 s = directory_to_fsync->Fsync(IOOptions(), nullptr);
389 }
390 } else {
391 fs->DeleteFile(tmp, IOOptions(), nullptr);
392 }
393 return s;
394 }
395
SetIdentityFile(Env * env,const std::string & dbname,const std::string & db_id)396 Status SetIdentityFile(Env* env, const std::string& dbname,
397 const std::string& db_id) {
398 std::string id;
399 if (db_id.empty()) {
400 id = env->GenerateUniqueId();
401 } else {
402 id = db_id;
403 }
404 assert(!id.empty());
405 // Reserve the filename dbname/000000.dbtmp for the temporary identity file
406 std::string tmp = TempFileName(dbname, 0);
407 Status s = WriteStringToFile(env, id, tmp, true);
408 if (s.ok()) {
409 s = env->RenameFile(tmp, IdentityFileName(dbname));
410 }
411 if (!s.ok()) {
412 env->DeleteFile(tmp);
413 }
414 return s;
415 }
416
SyncManifest(Env * env,const ImmutableDBOptions * db_options,WritableFileWriter * file)417 IOStatus SyncManifest(Env* env, const ImmutableDBOptions* db_options,
418 WritableFileWriter* file) {
419 TEST_KILL_RANDOM("SyncManifest:0", rocksdb_kill_odds * REDUCE_ODDS2);
420 StopWatch sw(env, db_options->statistics.get(), MANIFEST_FILE_SYNC_MICROS);
421 return file->Sync(db_options->use_fsync);
422 }
423
GetInfoLogFiles(Env * env,const std::string & db_log_dir,const std::string & dbname,std::string * parent_dir,std::vector<std::string> * info_log_list)424 Status GetInfoLogFiles(Env* env, const std::string& db_log_dir,
425 const std::string& dbname, std::string* parent_dir,
426 std::vector<std::string>* info_log_list) {
427 assert(parent_dir != nullptr);
428 assert(info_log_list != nullptr);
429 uint64_t number = 0;
430 FileType type = kLogFile;
431
432 if (!db_log_dir.empty()) {
433 *parent_dir = db_log_dir;
434 } else {
435 *parent_dir = dbname;
436 }
437
438 InfoLogPrefix info_log_prefix(!db_log_dir.empty(), dbname);
439
440 std::vector<std::string> file_names;
441 Status s = env->GetChildren(*parent_dir, &file_names);
442
443 if (!s.ok()) {
444 return s;
445 }
446
447 for (auto& f : file_names) {
448 if (ParseFileName(f, &number, info_log_prefix.prefix, &type) &&
449 (type == kInfoLogFile)) {
450 info_log_list->push_back(f);
451 }
452 }
453 return Status::OK();
454 }
455
NormalizePath(const std::string & path)456 std::string NormalizePath(const std::string& path) {
457 std::string dst;
458 for (auto c : path) {
459 if (!dst.empty() && c == kFilePathSeparator &&
460 dst.back() == kFilePathSeparator) {
461 continue;
462 }
463 dst.push_back(c);
464 }
465 return dst;
466 }
467
468 } // namespace ROCKSDB_NAMESPACE
469