diff --git a/src/mds/common/helper.cc b/src/mds/common/helper.cc index 607618eaf..87dcfad06 100644 --- a/src/mds/common/helper.cc +++ b/src/mds/common/helper.cc @@ -74,6 +74,7 @@ std::string Helper::ToLowerCase(const std::string& str) { bool Helper::StringToBool(const std::string& str) { return !(str == "0" || str == "false"); } int32_t Helper::StringToInt32(const std::string& str) { return std::strtol(str.c_str(), nullptr, 10); } int64_t Helper::StringToInt64(const std::string& str) { return std::strtoll(str.c_str(), nullptr, 10); } +uint64_t Helper::StringToUint64(const std::string& str) { return std::strtoull(str.c_str(), nullptr, 10); } float Helper::StringToFloat(const std::string& str) { return std::strtof(str.c_str(), nullptr); } double Helper::StringToDouble(const std::string& str) { return std::strtod(str.c_str(), nullptr); } diff --git a/src/mds/common/helper.h b/src/mds/common/helper.h index 79c124722..3d68de5e8 100644 --- a/src/mds/common/helper.h +++ b/src/mds/common/helper.h @@ -43,6 +43,7 @@ class Helper { static bool StringToBool(const std::string& str); static int32_t StringToInt32(const std::string& str); static int64_t StringToInt64(const std::string& str); + static uint64_t StringToUint64(const std::string& str); static float StringToFloat(const std::string& str); static double StringToDouble(const std::string& str); diff --git a/src/mds/filesystem/fs_utils.cc b/src/mds/filesystem/fs_utils.cc index 388a61e4a..a43f67e84 100644 --- a/src/mds/filesystem/fs_utils.cc +++ b/src/mds/filesystem/fs_utils.cc @@ -251,7 +251,11 @@ Status FsUtils::GenRootDirJsonString(std::string& output) { nlohmann::json doc = nlohmann::json::array(); nlohmann::json item; - item["ino"] = attr.ino(); + // Emit ino as a JSON string: trash-realm inos (kTrashInodeId, + // kTrashSubInodeStart+) exceed 2^53 and lose precision when parsed as a + // JSON number by the dashboard JS, which then sends a wrong-parent URL + // back. Stringifying all inos keeps the wire format uniform. + item["ino"] = std::to_string(attr.ino()); item["name"] = "/"; item["type"] = "directory"; if (fs_info_.partition_policy().type() == pb::mds::PartitionType::MONOLITHIC_PARTITION) { @@ -336,9 +340,16 @@ Status FsUtils::GenDirJsonString(Ino parent, std::string& output) { const auto& attr = it->second; nlohmann::json item; - item["ino"] = dentry.ino(); + item["ino"] = std::to_string(dentry.ino()); item["name"] = dentry.name(); item["type"] = dentry.type() == pb::mds::FileType::DIRECTORY ? "directory" : "file"; + // Hour-bucket sub-trash directories live in the trash ino range and have + // no persistent partition (every request builds a request-local + // ShardPartition; see mds/common/trash.h). Suppress the dashboard shard + // link for them, matching the synthesized `.trash` row above. + if (IsTrashInode(dentry.ino())) { + item["no_shard"] = true; + } if (fs_info_.partition_policy().type() == pb::mds::PartitionType::MONOLITHIC_PARTITION) { item["node"] = fs_info_.partition_policy().mono().mds_id(); } else { @@ -355,6 +366,34 @@ Status FsUtils::GenDirJsonString(Ino parent, std::string& output) { doc.push_back(item); } + // Synthesize the virtual `.trash` directory under root. kTrashInodeId has + // no KV dentry — it is built on demand via BuildTrashInodeAttr (see + // mds/common/trash.h), so the ScanDentryOperation above never returns it. + // Gated on trash_days > 0 to mirror the GC enabled check + // (mds/background/gc.cc:1100) and the client-visible behavior. + if (parent == kRootIno && fs_info_.trash_days() > 0) { + const auto attr = + BuildTrashInodeAttr(fs_info_.fs_id(), fs_info_.create_time_s() * 1000000000ULL); + nlohmann::json item; + item["ino"] = std::to_string(attr.ino()); + item["name"] = kTrashName; + item["type"] = "directory"; + // .trash is a virtual root; it has no on-disk partition, so suppress + // the shard link in the dashboard JS. + item["no_shard"] = true; + if (fs_info_.partition_policy().type() == pb::mds::PartitionType::MONOLITHIC_PARTITION) { + item["node"] = fs_info_.partition_policy().mono().mds_id(); + } else { + item["node"] = hash_router_->GetMDS(attr.ino()); + } + item["description"] = fmt::format( + "{},{}/{},{},{},{},{},{},{},{}", attr.version(), attr.mode(), + Helper::FsModeToString(attr.mode()), attr.nlink(), attr.uid(), attr.gid(), + attr.length(), FormatTime(attr.ctime()), FormatTime(attr.mtime()), + FormatTime(attr.atime())); + doc.push_back(item); + } + output = doc.dump(); return Status::OK(); diff --git a/src/mds/service/fsstat_service.cc b/src/mds/service/fsstat_service.cc index 3357a83c2..c2ac9866d 100644 --- a/src/mds/service/fsstat_service.cc +++ b/src/mds/service/fsstat_service.cc @@ -247,6 +247,7 @@ static void RenderFsInfo(const std::vector& fs_infoes, butil::I os << "Owner"; os << "Navigation"; os << "Time"; + os << "Trash"; os << "RecycleTime"; os << "MountPoint"; os << "Storage"; @@ -271,6 +272,10 @@ static void RenderFsInfo(const std::vector& fs_infoes, butil::I os << "" << fs_info.owner() << ""; os << "" << render_navigation_func(fs_info) << ""; os << "" << render_time_func(fs_info) << ""; + os << "" + << fmt::format("trash_days: {}
immediate_trash_quota: {}", fs_info.trash_days(), + fs_info.immediate_trash_quota() ? "true" : "false") + << ""; os << "" << fmt::format("{}hour", fs_info.recycle_time_hour()) << ""; os << fmt::format(R"(Goto [{}])", fs_info.fs_name(), fs_info.mount_points_size()); @@ -1149,15 +1154,16 @@ body { info.className = 'info'; info.textContent = `[${item.ino},${item.description}][${item.node}]`; - const shard_link = document.createElement('a'); - shard_link.href = `shard/${fs_id}/${item.ino}`; - shard_link.target = '_blank'; - shard_link.textContent = 'shard'; - folderDiv.appendChild(icon); folderDiv.appendChild(folderName); folderDiv.appendChild(info); - folderDiv.appendChild(shard_link); + if (!item.no_shard) { + const shard_link = document.createElement('a'); + shard_link.href = `shard/${fs_id}/${item.ino}`; + shard_link.target = '_blank'; + shard_link.textContent = 'shard'; + folderDiv.appendChild(shard_link); + } // 只给目录名添加点击事件 folderName.addEventListener('click', async function(e) { @@ -1850,7 +1856,7 @@ void FsStatServiceImpl::default_method(::google::protobuf::RpcController* contro // /FsStatService/delfiles/{fs_id}/{ino} uint32_t fs_id = Helper::StringToInt32(params[1]); - uint64_t ino = Helper::StringToInt64(params[2]); + uint64_t ino = Helper::StringToUint64(params[2]); auto file_system_set = Server::GetInstance().GetFileSystemSet(); auto file_system = file_system_set->GetFileSystem(fs_id); @@ -1912,7 +1918,7 @@ void FsStatServiceImpl::default_method(::google::protobuf::RpcController* contro // /FsStatService/{fs_id}/{ino} uint32_t fs_id = Helper::StringToInt32(params[0]); - uint64_t ino = Helper::StringToInt64(params[1]); + uint64_t ino = Helper::StringToUint64(params[1]); auto file_system_set = Server::GetInstance().GetFileSystemSet(); auto file_system = file_system_set->GetFileSystem(fs_id); @@ -1934,7 +1940,7 @@ void FsStatServiceImpl::default_method(::google::protobuf::RpcController* contro // /FsStatService/partition/{fs_id}/{ino} uint32_t fs_id = Helper::StringToInt32(params[1]); - uint64_t ino = Helper::StringToInt64(params[2]); + uint64_t ino = Helper::StringToUint64(params[2]); LOG(INFO) << fmt::format("Get dir json, fs_id: {}, ino: {}", fs_id, ino); @@ -1961,7 +1967,7 @@ void FsStatServiceImpl::default_method(::google::protobuf::RpcController* contro // /FsStatService/chunk/{fs_id}/{ino} uint32_t fs_id = Helper::StringToInt32(params[1]); - uint64_t ino = Helper::StringToInt64(params[2]); + uint64_t ino = Helper::StringToUint64(params[2]); FsUtils fs_utils(Server::GetInstance().GetOperationProcessor()); @@ -1978,7 +1984,7 @@ void FsStatServiceImpl::default_method(::google::protobuf::RpcController* contro // /FsStatService/shard/{fs_id}/{ino} uint32_t fs_id = Helper::StringToInt32(params[1]); - uint64_t ino = Helper::StringToInt64(params[2]); + uint64_t ino = Helper::StringToUint64(params[2]); auto file_system_set = Server::GetInstance().GetFileSystemSet(); auto file_system = file_system_set->GetFileSystem(fs_id);