1/*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include "file_utils.h"
16
17#include <cerrno>
18#include <cinttypes>
19#include <sys/stat.h>
20#include <unistd.h>
21
22#include "securec.h"
23#include "sensors_errors.h"
24
25#undef LOG_TAG
26#define LOG_TAG "MiscdeviceFileUtils"
27
28namespace OHOS {
29namespace Sensors {
30namespace {
31const std::string CONFIG_DIR = "/vendor/etc/vibrator/";
32constexpr int32_t FILE_SIZE_MAX = 0x5000;
33constexpr int64_t READ_DATA_BUFF_SIZE = 256;
34constexpr int32_t INVALID_FILE_SIZE = -1;
35constexpr int32_t FILE_PATH_MAX = 1024;
36} // namespace
37
38std::string ReadJsonFile(const std::string &filePath)
39{
40    if (filePath.empty()) {
41        MISC_HILOGE("Path is empty");
42        return {};
43    }
44    char realPath[PATH_MAX] = {};
45    if (realpath(filePath.c_str(), realPath) == nullptr) {
46        MISC_HILOGE("Path is error, %{public}d", errno);
47        return {};
48    }
49    if (!CheckFileDir(realPath, CONFIG_DIR)) {
50        MISC_HILOGE("File dir is invalid");
51        return {};
52    }
53    if (!CheckFileExtendName(realPath, "json")) {
54        MISC_HILOGE("Unable to parse files other than json format");
55        return {};
56    }
57    if (!IsFileExists(realPath)) {
58        MISC_HILOGE("File not exist");
59        return {};
60    }
61    if (!CheckFileSize(realPath)) {
62        MISC_HILOGE("File size out of read range");
63        return {};
64    }
65    FILE *fp = fopen(realPath, "r");
66    CHKPS(fp);
67    std::string dataStr;
68    char buf[READ_DATA_BUFF_SIZE] = { '\0' };
69    while (fgets(buf, sizeof(buf), fp) != nullptr) {
70        dataStr += buf;
71    }
72    if (fclose(fp) != 0) {
73        MISC_HILOGW("Close file failed, errno:%{public}d", errno);
74    }
75    return dataStr;
76}
77
78int32_t GetFileSize(const std::string &filePath)
79{
80    struct stat statbuf = { 0 };
81    if (stat(filePath.c_str(), &statbuf) != 0) {
82        MISC_HILOGE("Get file size error");
83        return INVALID_FILE_SIZE;
84    }
85    return statbuf.st_size;
86}
87
88int64_t GetFileSize(int32_t fd)
89{
90    if (fd < 0) {
91        MISC_HILOGE("fd is invalid, fd:%{public}d", fd);
92        return INVALID_FILE_SIZE;
93    }
94    struct stat64 statbuf = { 0 };
95    if (fstat64(fd, &statbuf) != 0) {
96        MISC_HILOGE("fstat error, errno:%{public}d", errno);
97        return INVALID_FILE_SIZE;
98    }
99    return statbuf.st_size;
100}
101
102int32_t GetFileName(const int32_t &fd, std::string &fileName)
103{
104    if (fd < 0) {
105        MISC_HILOGE("fd is invalid, fd:%{public}d", fd);
106        return ERROR;
107    }
108    char buf[FILE_PATH_MAX] = {'\0'};
109    char filePath[FILE_PATH_MAX] = {'\0'};
110
111    int ret = snprintf_s(buf, sizeof(buf), (sizeof(buf) - 1), "/proc/self/fd/%d", fd);
112    if (ret < 0) {
113        MISC_HILOGE("snprintf failed with %{public}d", errno);
114        return ERROR;
115    }
116
117    ret = readlink(buf, filePath, FILE_PATH_MAX);
118    if (ret < 0 || ret >= FILE_PATH_MAX) {
119        MISC_HILOGE("readlink failed with %{public}d", errno);
120        return ERROR;
121    }
122
123    fileName = filePath;
124    std::size_t firstSlash = fileName.rfind("/");
125    if (firstSlash == fileName.npos) {
126        MISC_HILOGE("Get error path");
127        return ERROR;
128    }
129    fileName = fileName.substr(firstSlash + 1, fileName.size() - firstSlash);
130    return SUCCESS;
131}
132
133int32_t GetFileExtName(const int32_t &fd, std::string &extName)
134{
135    if (fd < 0) {
136        MISC_HILOGE("fd is invalid, fd:%{public}d", fd);
137        return ERROR;
138    }
139    std::string fileName = "";
140    if (GetFileName(fd, fileName) == ERROR) {
141        MISC_HILOGE("GetFileName failed");
142        return ERROR;
143    }
144    extName = fileName.substr(fileName.find_last_of(".") + 1);
145    return SUCCESS;
146}
147
148bool CheckFileDir(const std::string &filePath, const std::string &dir)
149{
150    if (filePath.compare(0, CONFIG_DIR.size(), CONFIG_DIR) != 0) {
151        MISC_HILOGE("filePath dir is invalid");
152        return false;
153    }
154    return true;
155}
156
157bool CheckFileSize(const std::string &filePath)
158{
159    int32_t fileSize = GetFileSize(filePath);
160    if ((fileSize <= 0) || (fileSize > FILE_SIZE_MAX)) {
161        MISC_HILOGE("File size out of read range");
162        return false;
163    }
164    return true;
165}
166
167bool CheckFileExtendName(const std::string &filePath, const std::string &checkExtension)
168{
169    std::string::size_type pos = filePath.find_last_of('.');
170    if (pos == std::string::npos) {
171        MISC_HILOGE("File is not find extension");
172        return false;
173    }
174    return (filePath.substr(pos + 1, filePath.npos) == checkExtension);
175}
176
177bool IsFileExists(const std::string &fileName)
178{
179    return (access(fileName.c_str(), F_OK) == 0);
180}
181
182std::string ReadFd(const RawFileDescriptor &rawFd)
183{
184    if (rawFd.fd < 0) {
185        MISC_HILOGE("fd is invalid, fd:%{public}d", rawFd.fd);
186        return {};
187    }
188    int64_t fdSize = GetFileSize(rawFd.fd);
189    if ((rawFd.offset < 0) || (rawFd.offset > fdSize)) {
190        MISC_HILOGE("offset is invalid, offset:%{public}" PRId64, rawFd.offset);
191        return {};
192    }
193    if ((rawFd.length <= 0) || (rawFd.length > fdSize - rawFd.offset)) {
194        MISC_HILOGE("length is invalid, length:%{public}" PRId64, rawFd.length);
195        return {};
196    }
197    FILE *fp = fdopen(rawFd.fd, "r");
198    CHKPS(fp);
199    if (fseek(fp, rawFd.offset, SEEK_SET) != 0) {
200        MISC_HILOGE("fseek failed, errno:%{public}d", errno);
201        if (fclose(fp) != 0) {
202            MISC_HILOGW("Close file failed, errno:%{public}d", errno);
203        }
204        return {};
205    }
206    std::string dataStr;
207    char buf[READ_DATA_BUFF_SIZE] = { '\0' };
208    int64_t alreadyRead = 0;
209    while (alreadyRead < rawFd.length) {
210        int64_t onceRead = std::min(rawFd.length - alreadyRead, READ_DATA_BUFF_SIZE - 1);
211        fgets(buf, onceRead + 1, fp);
212        dataStr += buf;
213        alreadyRead = ftell(fp) - rawFd.offset;
214    }
215    if (fclose(fp) != 0) {
216        MISC_HILOGW("Close file failed, errno:%{public}d", errno);
217    }
218    return dataStr;
219}
220
221std::string GetFileSuffix(int32_t fd)
222{
223    std::string fdPath = "/proc/self/fd/" + std::to_string(fd);
224    char filePath[FILE_PATH_MAX + 1] = { '\0' };
225    ssize_t ret = readlink(fdPath.c_str(), filePath, FILE_PATH_MAX);
226    if (ret < 0 || ret > FILE_PATH_MAX) {
227        MISC_HILOGE("Readlink failed, errno:%{public}d", errno);
228        return {};
229    }
230    std::string fileAbsolutePath(filePath);
231    size_t pos = fileAbsolutePath.find_last_of('.');
232    if (pos == std::string::npos) {
233        MISC_HILOGE("File suffix is invalid, fileAbsolutePath:%{public}s", fileAbsolutePath.c_str());
234        return {};
235    }
236    return fileAbsolutePath.substr(pos + 1);
237}
238} // namespace Sensors
239} // namespace OHOS
240