// Copyright (c) 2013, Facebook, Inc. All rights reserved. // This source code is licensed under the BSD-style license found in the // LICENSE file in the root directory of this source tree. An additional grant // of patent rights can be found in the PATENTS file in the same directory. // // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include "db/filename.h" #include #include #include #include #include "db/dbformat.h" #include "rocksdb/env.h" #include "util/logging.h" #include "util/stop_watch.h" namespace rocksdb { // Given a path, flatten the path name by replacing all chars not in // {[0-9,a-z,A-Z,-,_,.]} with _. And append '_LOG\0' at the end. // Return the number of chars stored in dest not including the trailing '\0'. static size_t GetInfoLogPrefix(const std::string& path, char* dest, int len) { const char suffix[] = "_LOG"; size_t write_idx = 0; size_t i = 0; size_t src_len = path.size(); while (i < src_len && write_idx < len - sizeof(suffix)) { if ((path[i] >= 'a' && path[i] <= 'z') || (path[i] >= '0' && path[i] <= '9') || (path[i] >= 'A' && path[i] <= 'Z') || path[i] == '-' || path[i] == '.' || path[i] == '_'){ dest[write_idx++] = path[i]; } else { if (i > 0) dest[write_idx++] = '_'; } i++; } assert(sizeof(suffix) <= len - write_idx); // "\0" is automatically added by snprintf snprintf(dest + write_idx, len - write_idx, suffix); write_idx += sizeof(suffix) - 1; return write_idx; } static std::string MakeFileName(const std::string& name, uint64_t number, const char* suffix) { char buf[100]; snprintf(buf, sizeof(buf), "/%06llu.%s", static_cast(number), suffix); return name + buf; } std::string LogFileName(const std::string& name, uint64_t number) { assert(number > 0); return MakeFileName(name, number, "log"); } std::string ArchivalDirectory(const std::string& dir) { return dir + "/" + ARCHIVAL_DIR; } std::string ArchivedLogFileName(const std::string& name, uint64_t number) { assert(number > 0); return MakeFileName(name + "/" + ARCHIVAL_DIR, number, "log"); } std::string MakeTableFileName(const std::string& path, uint64_t number) { return MakeFileName(path, number, "sst"); } uint64_t TableFileNameToNumber(const std::string& name) { uint64_t number = 0; uint64_t base = 1; int pos = static_cast(name.find_last_of('.')); while (--pos >= 0 && name[pos] >= '0' && name[pos] <= '9') { number += (name[pos] - '0') * base; base *= 10; } return number; } std::string TableFileName(const std::vector& db_paths, uint64_t number, uint32_t path_id) { assert(number > 0); std::string path; if (path_id >= db_paths.size()) { path = db_paths.back().path; } else { path = db_paths[path_id].path; } return MakeTableFileName(path, number); } const size_t kFormatFileNumberBufSize = 38; void FormatFileNumber(uint64_t number, uint32_t path_id, char* out_buf, size_t out_buf_size) { if (path_id == 0) { snprintf(out_buf, out_buf_size, "%" PRIu64, number); } else { snprintf(out_buf, out_buf_size, "%" PRIu64 "(path " "%" PRIu32 ")", number, path_id); } } std::string DescriptorFileName(const std::string& dbname, uint64_t number) { assert(number > 0); char buf[100]; snprintf(buf, sizeof(buf), "/MANIFEST-%06llu", static_cast(number)); return dbname + buf; } std::string CurrentFileName(const std::string& dbname) { return dbname + "/CURRENT"; } std::string LockFileName(const std::string& dbname) { return dbname + "/LOCK"; } std::string TempFileName(const std::string& dbname, uint64_t number) { return MakeFileName(dbname, number, "dbtmp"); } InfoLogPrefix::InfoLogPrefix(bool has_log_dir, const std::string& db_absolute_path) { if (!has_log_dir) { const char kInfoLogPrefix[] = "LOG"; // "\0" is automatically added to the end snprintf(buf, sizeof(buf), kInfoLogPrefix); prefix = Slice(buf, sizeof(kInfoLogPrefix) - 1); } else { size_t len = GetInfoLogPrefix(db_absolute_path, buf, sizeof(buf)); prefix = Slice(buf, len); } } std::string InfoLogFileName(const std::string& dbname, const std::string& db_path, const std::string& log_dir) { if (log_dir.empty()) return dbname + "/LOG"; InfoLogPrefix info_log_prefix(true, db_path); return log_dir + "/" + info_log_prefix.buf; } // Return the name of the old info log file for "dbname". std::string OldInfoLogFileName(const std::string& dbname, uint64_t ts, const std::string& db_path, const std::string& log_dir) { char buf[50]; snprintf(buf, sizeof(buf), "%llu", static_cast(ts)); if (log_dir.empty()) return dbname + "/LOG.old." + buf; InfoLogPrefix info_log_prefix(true, db_path); return log_dir + "/" + info_log_prefix.buf + ".old." + buf; } std::string MetaDatabaseName(const std::string& dbname, uint64_t number) { char buf[100]; snprintf(buf, sizeof(buf), "/METADB-%llu", static_cast(number)); return dbname + buf; } std::string IdentityFileName(const std::string& dbname) { return dbname + "/IDENTITY"; } // Owned filenames have the form: // dbname/IDENTITY // dbname/CURRENT // dbname/LOCK // dbname/ // dbname/.old.[0-9]+ // dbname/MANIFEST-[0-9]+ // dbname/[0-9]+.(log|sst) // dbname/METADB-[0-9]+ // Disregards / at the beginning bool ParseFileName(const std::string& fname, uint64_t* number, FileType* type, WalFileType* log_type) { return ParseFileName(fname, number, "", type, log_type); } bool ParseFileName(const std::string& fname, uint64_t* number, const Slice& info_log_name_prefix, FileType* type, WalFileType* log_type) { Slice rest(fname); if (fname.length() > 1 && fname[0] == '/') { rest.remove_prefix(1); } if (rest == "IDENTITY") { *number = 0; *type = kIdentityFile; } else if (rest == "CURRENT") { *number = 0; *type = kCurrentFile; } else if (rest == "LOCK") { *number = 0; *type = kDBLockFile; } else if (info_log_name_prefix.size() > 0 && rest.starts_with(info_log_name_prefix)) { rest.remove_prefix(info_log_name_prefix.size()); if (rest == "" || rest == ".old") { *number = 0; *type = kInfoLogFile; } else if (rest.starts_with(".old.")) { uint64_t ts_suffix; // sizeof also counts the trailing '\0'. rest.remove_prefix(sizeof(".old.") - 1); if (!ConsumeDecimalNumber(&rest, &ts_suffix)) { return false; } *number = ts_suffix; *type = kInfoLogFile; } } else if (rest.starts_with("MANIFEST-")) { rest.remove_prefix(strlen("MANIFEST-")); uint64_t num; if (!ConsumeDecimalNumber(&rest, &num)) { return false; } if (!rest.empty()) { return false; } *type = kDescriptorFile; *number = num; } else if (rest.starts_with("METADB-")) { rest.remove_prefix(strlen("METADB-")); uint64_t num; if (!ConsumeDecimalNumber(&rest, &num)) { return false; } if (!rest.empty()) { return false; } *type = kMetaDatabase; *number = num; } else { // Avoid strtoull() to keep filename format independent of the // current locale bool archive_dir_found = false; if (rest.starts_with(ARCHIVAL_DIR)) { if (rest.size() <= ARCHIVAL_DIR.size()) { return false; } rest.remove_prefix(ARCHIVAL_DIR.size() + 1); // Add 1 to remove / also if (log_type) { *log_type = kArchivedLogFile; } archive_dir_found = true; } uint64_t num; if (!ConsumeDecimalNumber(&rest, &num)) { return false; } Slice suffix = rest; if (suffix == Slice(".log")) { *type = kLogFile; if (log_type && !archive_dir_found) { *log_type = kAliveLogFile; } } else if (archive_dir_found) { return false; // Archive dir can contain only log files } else if (suffix == Slice(".sst")) { *type = kTableFile; } else if (suffix == Slice(".dbtmp")) { *type = kTempFile; } else { return false; } *number = num; } return true; } Status SetCurrentFile(Env* env, const std::string& dbname, uint64_t descriptor_number, Directory* directory_to_fsync) { // Remove leading "dbname/" and add newline to manifest file name std::string manifest = DescriptorFileName(dbname, descriptor_number); Slice contents = manifest; assert(contents.starts_with(dbname + "/")); contents.remove_prefix(dbname.size() + 1); std::string tmp = TempFileName(dbname, descriptor_number); Status s = WriteStringToFile(env, contents.ToString() + "\n", tmp, true); if (s.ok()) { s = env->RenameFile(tmp, CurrentFileName(dbname)); } if (s.ok()) { if (directory_to_fsync != nullptr) { directory_to_fsync->Fsync(); } } else { env->DeleteFile(tmp); } return s; } Status SetIdentityFile(Env* env, const std::string& dbname) { std::string id = env->GenerateUniqueId(); assert(!id.empty()); // Reserve the filename dbname/000000.dbtmp for the temporary identity file std::string tmp = TempFileName(dbname, 0); Status s = WriteStringToFile(env, id, tmp, true); if (s.ok()) { s = env->RenameFile(tmp, IdentityFileName(dbname)); } if (!s.ok()) { env->DeleteFile(tmp); } return s; } Status SyncManifest(Env* env, const DBOptions* db_options, WritableFile* file) { if (db_options->disableDataSync) { return Status::OK(); } else if (db_options->use_fsync) { StopWatch sw(env, db_options->statistics.get(), MANIFEST_FILE_SYNC_MICROS); return file->Fsync(); } else { StopWatch sw(env, db_options->statistics.get(), MANIFEST_FILE_SYNC_MICROS); return file->Sync(); } } } // namespace rocksdb