* Copyright (c) 2023-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "connection_detector.h"
#include <dirent.h>
#include <iostream>
#include <sstream>
#include <sys/stat.h>
#include <unistd.h>
#include <vector>
#include "dfs_error.h"
#include "dfs_radar.h"
#include "radar_report.h"
#include "utils_log.h"
namespace OHOS {
namespace Storage {
namespace DistributedFile {
using namespace FileManagement;
constexpr int32_t INVALID_USER_ID = -1;
std::string ConnectionDetector::GetCellByIndex(const std::string &str, int targetIndex)
{
size_t begin = 0;
size_t end = 0;
int count = 0;
do {
size_t i;
for (i = begin; i < str.size(); ++i) {
if (str[i] != '\t' && str[i] != ' ') {
break;
}
}
begin = i;
for (i = begin; i < str.size(); ++i) {
if (!(str[i] != '\t' && str[i] != ' ')) {
break;
}
}
end = i;
if (end == str.size()) {
break;
}
++count;
if (count == targetIndex) {
break;
}
begin = end;
} while (true);
return str.substr(begin, end - begin);
}
bool ConnectionDetector::MatchConnectionStatus(ifstream &inputFile)
{
string str;
getline(inputFile, str);
if (str.find("connection_status") == string::npos) {
return false;
}
while (getline(inputFile, str)) {
if (str.empty()) {
return false;
}
auto connectionStatus = GetCellByIndex(str, 2);
auto tcpStatus = GetCellByIndex(str, 3);
if (connectionStatus == "2" && (tcpStatus == "2" || tcpStatus == "3")) {
return true;
}
}
return false;
}
bool ConnectionDetector::MatchConnectionGroup(const std::string &fileName, const string &networkId)
{
if (access(fileName.c_str(), F_OK) != 0) {
LOGE("Cannot find the status file");
return false;
}
ifstream statusFile(fileName);
std::string str;
getline(statusFile, str);
bool result = false;
while (getline(statusFile, str)) {
if (str.find(networkId) == 0) {
result = MatchConnectionStatus(statusFile);
break;
}
}
statusFile.close();
return result;
}
static int FilterFunc(const struct dirent *filename)
{
if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") {
return DISMATCH;
}
return MATCH;
}
struct NameList {
struct dirent **namelist = {nullptr};
int direntNum = 0;
};
static void Deleter(struct NameList *arg)
{
if (arg == nullptr) {
return;
}
if (arg->namelist != nullptr) {
for (int i = 0; i < arg->direntNum; i++) {
free((arg->namelist)[i]);
(arg->namelist)[i] = nullptr;
}
free(arg->namelist);
arg->namelist = nullptr;
}
delete arg;
arg = nullptr;
}
bool ConnectionDetector::CheckValidDir(const std::string &path)
{
struct stat buf {
};
auto ret = stat(path.c_str(), &buf);
if (ret == -1) {
LOGE("stat failed, errno = %{public}d", errno);
RadarParaInfo info = {"CheckValidDir", ReportLevel::INNER, DfxBizStage::SOFTBUS_OPENP2P,
"kernel", "", ret, "stat failed,errno = " + to_string(errno)};
RadarReportAdapter::GetInstance().ReportLinkConnectionAdapter(info);
return false;
}
if ((buf.st_mode & S_IFMT) != S_IFDIR) {
LOGE("It is not a dir.");
return false;
}
return true;
}
bool ConnectionDetector::GetConnectionStatus(const std::string &targetDir, const std::string &networkId)
{
if (!CheckValidDir(SYS_HMDFS_PATH)) {
return false;
}
unique_ptr<struct NameList, decltype(Deleter) *> pNameList = {new (nothrow) struct NameList, Deleter};
if (pNameList == nullptr) {
LOGE("Failed to request heap memory.");
return false;
}
int num = scandir(SYS_HMDFS_PATH.c_str(), &(pNameList->namelist), FilterFunc, alphasort);
if (num < 0) {
LOGE("Failed to scandir, errno = %{public}d", errno);
RadarParaInfo info = {"GetConnectionStatus", ReportLevel::INNER, DfxBizStage::SOFTBUS_OPENP2P,
DEFAULT_PKGNAME, networkId, num, "Failed to scandir"};
RadarReportAdapter::GetInstance().ReportLinkConnectionAdapter(info);
return false;
}
pNameList->direntNum = num;
for (int i = 0; i < num; i++) {
if ((pNameList->namelist[i])->d_name != targetDir) {
continue;
}
string dest = SYS_HMDFS_PATH + '/' + targetDir;
if ((pNameList->namelist[i])->d_type == DT_DIR) {
string statusFile = SYS_HMDFS_PATH + '/' + targetDir + '/' + CONNECTION_STATUS_FILE_NAME;
if (MatchConnectionGroup(statusFile, networkId)) {
LOGI("Parse connection status success.");
return true;
}
break;
}
}
return false;
}
uint64_t ConnectionDetector::MocklispHash(const string &str)
{
struct stat statBuf;
auto err = stat(str.c_str(), &statBuf);
if (err != 0) {
LOGE("stat failed %{public}s error, err: %{public}d", GetAnonyString(str).c_str(), err);
RadarParaInfo info = {"MocklispHash", ReportLevel::INNER, DfxBizStage::SOFTBUS_OPENP2P,
"Kernel", "", err, "stat failed"};
RadarReportAdapter::GetInstance().ReportLinkConnectionAdapter(info);
return static_cast<uint64_t>(FileManagement::ERR_BAD_VALUE);
}
LOGI("statBuf dev id: %{public}lu", static_cast<unsigned long>(statBuf.st_dev));
return statBuf.st_dev;
}
int32_t ConnectionDetector::RepeatGetConnectionStatus(const std::string &targetDir, const std::string &networkId)
{
int retryCount = 0;
while (retryCount++ < MAX_RETRY - 1 && !GetConnectionStatus(targetDir, networkId)) {
usleep(CHECK_SESSION_DELAY_TIME);
}
return retryCount == MAX_RETRY ? -1 : NO_ERROR;
}
int32_t ConnectionDetector::GetCurrentUserId()
{
std::vector<int32_t> userIds{};
auto ret = AccountSA::OsAccountManager::QueryActiveOsAccountIds(userIds);
if (ret != NO_ERROR || userIds.empty()) {
LOGE("query active os account id failed, ret = %{public}d", ret);
return INVALID_USER_ID;
}
return userIds[0];
}
std::string ConnectionDetector::ParseHmdfsPath()
{
auto userId = GetCurrentUserId();
if (userId == INVALID_USER_ID) {
return "";
}
std::string path = HMDFS_PATH;
size_t pos = path.find(CURRENT_USER_ID_FLAG);
if (pos == std::string::npos) {
return "";
}
return path.replace(pos, CURRENT_USER_ID_FLAG.length(), std::to_string(userId));
}
}
}
}