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