/* * Copyright (c) 2021-2024 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 "utils/file_utils.h" #include <cerrno> #include <cstdlib> #include <cstring> #include <dirent.h> #include <fcntl.h> #include <fstream> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include "securec.h" #include "storage_service_errno.h" #include "storage_service_log.h" #include "string_ex.h" #ifdef USE_LIBRESTORECON #include "policycoreutils.h" #endif namespace OHOS { namespace StorageDaemon { constexpr uint32_t ALL_PERMS = (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO); const int BUF_LEN = 1024; const uint8_t KILL_RETRY_TIME = 5; const uint32_t KILL_RETRY_INTERVAL_MS = 100 * 1000; const std::string MOUNT_POINT_INFO = "/proc/mounts"; int32_t ChMod(const std::string &path, mode_t mode) { return TEMP_FAILURE_RETRY(chmod(path.c_str(), mode)); } int32_t ChOwn(const std::string &path, uid_t uid, gid_t gid) { return TEMP_FAILURE_RETRY(chown(path.c_str(), uid, gid)); } int32_t MkDir(const std::string &path, mode_t mode) { return TEMP_FAILURE_RETRY(mkdir(path.c_str(), mode)); } int32_t RmDir(const std::string &path) { return TEMP_FAILURE_RETRY(rmdir(path.c_str())); } int32_t Mount(const std::string &source, const std::string &target, const char *type, unsigned long flags, const void *data) { return TEMP_FAILURE_RETRY(mount(source.c_str(), target.c_str(), type, flags, data)); } int32_t UMount(const std::string &path) { return TEMP_FAILURE_RETRY(umount(path.c_str())); } int32_t UMount2(const std::string &path, int flag) { return TEMP_FAILURE_RETRY(umount2(path.c_str(), flag)); } bool IsDir(const std::string &path) { // check whether the path exists struct stat st; int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st)); if (ret) { return false; } return S_ISDIR(st.st_mode); } bool IsFile(const std::string &path) { // check whether the path exists struct stat buf = {}; if (stat(path.c_str(), &buf) != 0) { return false; } return S_ISREG(buf.st_mode); } bool MkDirRecurse(const std::string& path, mode_t mode) { std::string::size_type index = 0; do { std::string subPath = path; index = path.find('/', index + 1); if (index != std::string::npos) { subPath = path.substr(0, index); } if (TEMP_FAILURE_RETRY(access(subPath.c_str(), F_OK)) != 0) { if (MkDir(subPath, mode) != 0 && errno != EEXIST) { return false; } } } while (index != std::string::npos); return TEMP_FAILURE_RETRY(access(path.c_str(), F_OK)) == 0; } // On success, true is returned. On error, false is returned, and errno is set appropriately. bool PrepareDir(const std::string &path, mode_t mode, uid_t uid, gid_t gid) { LOGI("prepare for %{public}s", path.c_str()); // check whether the path exists struct stat st; if (TEMP_FAILURE_RETRY(lstat(path.c_str(), &st)) == E_ERR) { if (errno != ENOENT) { LOGE("failed to lstat, errno %{public}d", errno); return false; } } else { if (!S_ISDIR(st.st_mode)) { LOGE("%{public}s exists and is not a directory", path.c_str()); return false; } if (((st.st_mode & ALL_PERMS) != mode) && ChMod(path, mode)) { LOGE("dir exists and failed to chmod, errno %{public}d", errno); return false; } if (((st.st_uid != uid) || (st.st_gid != gid)) && ChOwn(path, uid, gid)) { LOGE("dir exists and failed to chown, errno %{public}d", errno); return false; } return true; } mode_t mask = umask(0); if (MkDir(path, mode)) { LOGE("failed to mkdir, errno %{public}d", errno); umask(mask); return false; } umask(mask); if (ChMod(path, mode)) { LOGE("failed to chmod, errno %{public}d", errno); return false; } if (ChOwn(path, uid, gid)) { LOGE("failed to chown, errno %{public}d", errno); return false; } #ifdef USE_LIBRESTORECON int err = Restorecon(path.c_str()); if (err) { LOGE("failed to restorecon, err:%{public}d", err); return false; } #endif return true; } bool RmDirRecurse(const std::string &path) { LOGD("rm dir %{public}s", path.c_str()); DIR *dir = opendir(path.c_str()); if (!dir) { if (errno == ENOENT) { return true; } LOGE("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno); return false; } for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) { if (ent->d_type == DT_DIR) { if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { continue; } if (!RmDirRecurse(path + "/" + ent->d_name)) { LOGE("failed to RmDirRecurse %{public}s, errno %{public}d", path.c_str(), errno); (void)closedir(dir); return false; } } else { if (unlink((path + "/" + ent->d_name).c_str())) { LOGE("failed to unlink file %{public}s, errno %{public}d", ent->d_name, errno); (void)closedir(dir); return false; } } } (void)closedir(dir); if (rmdir(path.c_str())) { LOGE("failed to rm dir %{public}s, errno %{public}d", path.c_str(), errno); return false; } return true; } void TravelChmod(const std::string &path, mode_t mode) { struct stat st; DIR *d = nullptr; struct dirent *dp = nullptr; const char *skip1 = "."; const char *skip2 = ".."; if (stat(path.c_str(), &st) < 0 || !S_ISDIR(st.st_mode)) { LOGE("invalid path"); return; } (void)ChMod(path, mode); if (!(d = opendir(path.c_str()))) { LOGE("opendir failed"); return; } while ((dp = readdir(d)) != nullptr) { if ((!strncmp(dp->d_name, skip1, strlen(skip1))) || (!strncmp(dp->d_name, skip2, strlen(skip2)))) { continue; } std::string subpath = path + "/" + dp->d_name; stat(subpath.c_str(), &st); (void)ChMod(subpath, mode); if (S_ISDIR(st.st_mode)) { TravelChmod(subpath, mode); } } (void)closedir(d); } bool StringToUint32(const std::string &str, uint32_t &num) { if (str.empty()) { return false; } if (!IsNumericStr(str)) { LOGE("Not numeric entry"); return false; } int value; if (!StrToInt(str, value)) { LOGE("String to int convert failed"); return false; } num = static_cast<uint32_t>(value); return true; } void GetSubDirs(const std::string &path, std::vector<std::string> &dirList) { dirList.clear(); struct stat st; int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st)); if (ret != 0 || ((st.st_mode & S_IFDIR) != S_IFDIR)) { LOGE("path is not dir"); return; } DIR *dir = opendir(path.c_str()); if (!dir) { LOGE("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno); return; } for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) { if ((ent->d_type != DT_DIR) || (strcmp(ent->d_name, ".") == 0) || (strcmp(ent->d_name, "..") == 0)) { continue; } dirList.push_back(ent->d_name); } (void)closedir(dir); } void ReadDigitDir(const std::string &path, std::vector<FileList> &dirInfo) { struct stat st; int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st)); if (ret != 0 || ((st.st_mode & S_IFDIR) != S_IFDIR)) { LOGE("path is not dir"); return; } DIR *dir = opendir(path.c_str()); if (!dir) { LOGE("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno); return; } for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) { if ((ent->d_type != DT_DIR) || (strcmp(ent->d_name, ".") == 0) || (strcmp(ent->d_name, "..") == 0)) { continue; } uint32_t userId; std::string name(ent->d_name); if (!StringToUint32(name, userId)) { continue; } FileList entry = { .userId = userId, .path = path + "/" + name }; dirInfo.push_back(entry); } (void)closedir(dir); } void OpenSubFile(const std::string &path, std::vector<std::string> &file) { struct stat st; int ret = TEMP_FAILURE_RETRY(lstat(path.c_str(), &st)); if (ret != 0 || ((st.st_mode & S_IFDIR) != S_IFDIR)) { LOGI("path is not dir"); return; } DIR *dir = opendir(path.c_str()); if (!dir) { LOGI("failed to open dir %{public}s, errno %{public}d", path.c_str(), errno); return; } for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) { if ((ent->d_type != DT_DIR)) { std::string name(ent->d_name); std::string filePath = path + "/" + name; LOGI("filePath is %{public}s", filePath.c_str()); file.push_back(filePath); continue; } else { if ((strcmp(ent->d_name, ".") == 0) || (strcmp(ent->d_name, "..") == 0)) { continue; } std::string name(ent->d_name); std::string filePath = path + "/" + name; OpenSubFile(filePath, file); } } (void)closedir(dir); } bool ReadFile(const std::string &path, std::string *str) { std::ifstream infile; int cnt = 0; std::string rpath(PATH_MAX + 1, '\0'); if ((path.length() > PATH_MAX) || (realpath(path.c_str(), rpath.data()) == nullptr)) { LOGE("realpath failed"); return false; } infile.open(rpath.c_str()); if (!infile) { LOGE("Cannot open file"); return false; } while (1) { std::string subStr; infile >> subStr; if (subStr == "") { break; } cnt++; *str = *str + subStr + '\n'; } infile.close(); return cnt == 0 ? false : true; } static std::vector<char*> FromatCmd(std::vector<std::string> &cmd) { std::vector<char*>res; res.reserve(cmd.size() + 1); for (auto& line : cmd) { LOGI("cmd %{public}s", line.c_str()); res.emplace_back(const_cast<char*>(line.c_str())); } res.emplace_back(nullptr); return res; } int ForkExec(std::vector<std::string> &cmd, std::vector<std::string> *output) { int pipe_fd[2]; pid_t pid; int status; auto args = FromatCmd(cmd); if (pipe(pipe_fd) < 0) { LOGE("creat pipe failed"); return E_ERR; } pid = fork(); if (pid == -1) { LOGE("fork failed"); return E_ERR; } else if (pid == 0) { (void)close(pipe_fd[0]); if (dup2(pipe_fd[1], STDOUT_FILENO) == -1) { LOGE("dup2 failed"); _exit(1); } (void)close(pipe_fd[1]); execvp(args[0], const_cast<char **>(args.data())); LOGE("execvp failed errno: %{public}d", errno); _exit(1); } else { (void)close(pipe_fd[1]); if (output) { char buf[BUF_LEN] = { 0 }; (void)memset_s(buf, sizeof(buf), 0, sizeof(buf)); output->clear(); while (read(pipe_fd[0], buf, BUF_LEN - 1) > 0) { LOGI("get result %{public}s", buf); output->push_back(buf); } } (void)close(pipe_fd[0]); waitpid(pid, &status, 0); if (errno == ECHILD) { return E_NO_CHILD; } if (!WIFEXITED(status)) { LOGE("Process exits abnormally"); return E_ERR; } } return E_OK; } void TraverseDirUevent(const std::string &path, bool flag) { DIR *dir = opendir(path.c_str()); if (dir == nullptr) { return; } int dirFd = dirfd(dir); int fd = openat(dirFd, "uevent", O_WRONLY | O_CLOEXEC); if (fd >= 0) { std::string writeStr = "add\n"; write(fd, writeStr.c_str(), writeStr.length()); (void)close(fd); } for (struct dirent *ent = readdir(dir); ent != nullptr; ent = readdir(dir)) { if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { continue; } if (ent->d_type != DT_DIR && !flag) { continue; } TraverseDirUevent(path + "/" + ent->d_name, false); } (void)closedir(dir); } int IsSameGidUid(const std::string &dir, uid_t uid, gid_t gid) { struct stat st; if (TEMP_FAILURE_RETRY(lstat(dir.c_str(), &st)) == E_ERR) { LOGE("failed to lstat, errno %{public}d", errno); if (errno == ENOENT) { return E_NON_EXIST; } return E_SYS_ERR; } return (st.st_uid == uid) && (st.st_gid == gid) ? E_OK : E_DIFF_UID_GID; } bool MoveDataShell(const std::string &from, const std::string &to) { LOGI("MoveDataShell start"); if (TEMP_FAILURE_RETRY(access(from.c_str(), F_OK)) != 0) { return true; } std::vector<std::string> cmd = { "/system/bin/mv", from, to }; std::vector<std::string> out; int32_t err = ForkExec(cmd, &out); if (err != 0) { LOGE("MoveDataShell failed err:%{public}d", err); } return true; } void MoveFileManagerData(const std::string &filesPath) { std::string docsPath = filesPath + "Docs/"; MoveDataShell(filesPath + "Download/", docsPath); MoveDataShell(filesPath + "Documents/", docsPath); MoveDataShell(filesPath + "Desktop/", docsPath); MoveDataShell(filesPath + ".Trash/", docsPath); } void ChownRecursion(const std::string &dir, uid_t uid, gid_t gid) { std::vector<std::string> cmd = { "/system/bin/chown", "-R", std::to_string(uid) + ":" + std::to_string(gid), dir }; std::vector<std::string> out; int32_t err = ForkExec(cmd, &out); if (err != 0) { LOGE("path: %{public}s chown failed err:%{public}d", cmd.back().c_str(), err); } } bool IsPathMounted(std::string &path) { if (path.empty()) { return true; } if (path.back() == '/') { path.pop_back(); } std::ifstream inputStream(MOUNT_POINT_INFO.c_str(), std::ios::in); if (!inputStream.is_open()) { LOGE("unable to open /proc/mounts, errno is %{public}d", errno); return false; } std::string tmpLine; while (std::getline(inputStream, tmpLine)) { std::stringstream ss(tmpLine); std::string dst; ss >> dst; ss >> dst; if (path == dst) { inputStream.close(); return true; } } inputStream.close(); return false; } void KillProcess(const std::vector<ProcessInfo> &processList, std::vector<ProcessInfo> &killFailList) { if (processList.empty()) { return; } for (const auto &item: processList) { int pid = item.pid; LOGI("kill pid %{public}d", pid); kill(pid, SIGKILL); bool isAlive = true; for (int i = 0; i < KILL_RETRY_TIME; i++) { if (!IsProcessAlive(pid)) { LOGI("kill pid %{public}d success.", pid); isAlive = false; break; } usleep(KILL_RETRY_INTERVAL_MS); } if (isAlive) { LOGE("kill pid %{public}d failed.", pid); killFailList.push_back(item); } } } bool IsProcessAlive(int pid) { std::stringstream procPath; procPath << "/proc/" << pid << "/stat"; std::ifstream statFile(procPath.str()); if (!statFile) { statFile.close(); return false; } statFile.close(); return true; } std::string ProcessToString(std::vector<ProcessInfo> &processList) { if (processList.empty()) { return ""; } std::string result; for (auto & iter : processList) { result += std::to_string(iter.pid) + "_" + iter.name + ","; } return result.empty() ? "" : result.substr(0, result.length() -1); } } // STORAGE_DAEMON } // OHOS