1 /*
2 * Copyright (c) 2025 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
16 #include "fs_atomicfile.h"
17
18 #include <cstdio>
19 #include <cstdlib>
20 #include <cstring>
21 #include <fcntl.h>
22 #include <filesystem>
23 #include <memory>
24 #include <sys/stat.h>
25
26 #include "fs_atomicfile_entity.h"
27 #include "filemgmt_libfs.h"
28 #include "file_instantiator.h"
29 #include "file_utils.h"
30
31 namespace OHOS {
32 namespace FileManagement {
33 namespace ModuleFileIO {
34 namespace fs = std::filesystem;
35
36 const std::string READ_STREAM_CLASS = "ReadStream";
37 const std::string WRITE_STREAM_CLASS = "WriteStream";
38 const std::string TEMP_FILE_SUFFIX = "_XXXXXX";
39
GetEntity()40 FsAtomicFileEntity *FsAtomicFile::GetEntity()
41 {
42 if (!entity) {
43 return nullptr;
44 }
45 return entity.get();
46 }
47
FinalizeCallback(void * finalizeData,void * finalizeHint)48 void FsAtomicFile::FinalizeCallback(void *finalizeData, [[maybe_unused]] void *finalizeHint)
49 {
50 BufferData *bufferData = static_cast<BufferData *>(finalizeData);
51 delete bufferData;
52 }
53
GetPath()54 string FsAtomicFile::GetPath()
55 {
56 return entity->baseFileName;
57 }
58
GetBaseFile()59 FsResult<FsFile *> FsAtomicFile::GetBaseFile()
60 {
61 if (entity == nullptr) {
62 HILOGE("Failed to get atomicFileEntity");
63 return FsResult<FsFile *>::Error(UNKNOWN_ERR);
64 }
65
66 if (entity->baseFileName.size() >= PATH_MAX) {
67 HILOGE("Base file name is too long");
68 return FsResult<FsFile *>::Error(UNKNOWN_ERR);
69 }
70
71 auto absolutePath = std::make_unique<char[]>(PATH_MAX);
72 char *result = realpath(entity->baseFileName.c_str(), absolutePath.get());
73 if (result == nullptr) {
74 HILOGE("Failed to resolve real path, err:%{public}d", errno);
75 return FsResult<FsFile *>::Error(errno);
76 }
77
78 int fd = open(result, O_RDONLY);
79 if (fd < 0) {
80 HILOGE("Failed to open file, err:%{public}d", errno);
81 return FsResult<FsFile *>::Error(errno);
82 }
83
84 return FileInstantiator::InstantiateFile(fd, entity->baseFileName, false);
85 }
86
ReadFileToBuffer(FILE * fp)87 static std::tuple<std::unique_ptr<BufferData>, int32_t> ReadFileToBuffer(FILE *fp)
88 {
89 int fd = fileno(fp);
90 if (fd < 0) {
91 HILOGE("Failed to get file descriptor, err:%{public}d", errno);
92 return { nullptr, UNKNOWN_ERR };
93 }
94
95 struct stat fileStat {};
96 if (fstat(fd, &fileStat) < 0) {
97 HILOGE("Failed to get file stats, err:%{public}d", errno);
98 return { nullptr, errno };
99 }
100
101 long fileSize = fileStat.st_size;
102 if (fileSize <= 0) {
103 HILOGE("Invalid file size");
104 return { nullptr, EIO };
105 }
106
107 auto bufferData = std::make_unique<BufferData>();
108 bufferData->buffer = new (std::nothrow) uint8_t[fileSize];
109 if (bufferData->buffer == nullptr) {
110 HILOGE("Failed to allocate memory");
111 return { nullptr, ENOMEM };
112 }
113
114 bufferData->length = fread(bufferData->buffer, sizeof(uint8_t), fileSize, fp);
115 if ((bufferData->length != static_cast<size_t>(fileSize) && !feof(fp)) || ferror(fp)) {
116 HILOGE("Failed to read file, actual length is:%zu, fileSize:%ld", bufferData->length, fileSize);
117 delete[] bufferData->buffer;
118 bufferData->buffer = nullptr;
119 bufferData->length = 0;
120 return { nullptr, EIO };
121 }
122 return { std::move(bufferData), ERRNO_NOERR };
123 }
124
ReadFully()125 FsResult<unique_ptr<BufferData>> FsAtomicFile::ReadFully()
126 {
127 if (entity == nullptr) {
128 HILOGE("Failed to get atomicFileEntity");
129 return FsResult<unique_ptr<BufferData>>::Error(UNKNOWN_ERR);
130 }
131
132 auto absolutePath = std::make_unique<char[]>(PATH_MAX);
133 char *result = realpath(entity->baseFileName.c_str(), absolutePath.get());
134 if (result == nullptr) {
135 HILOGE("Failed to resolve file real path, err:%{public}d", errno);
136 return FsResult<unique_ptr<BufferData>>::Error(errno);
137 }
138
139 auto file = std::unique_ptr<FILE, decltype(&std::fclose)>(std::fopen(result, "rb"), &std::fclose);
140 if (!file) {
141 HILOGE("Failed to open file, err:%{public}d", errno);
142 return FsResult<unique_ptr<BufferData>>::Error(errno);
143 }
144
145 auto [bufferData, errCode] = ReadFileToBuffer(file.get());
146 if (errCode != ERRNO_NOERR) {
147 return FsResult<unique_ptr<BufferData>>::Error(errCode);
148 }
149 return FsResult<unique_ptr<BufferData>>::Success(move(bufferData));
150 }
151
StartWrite()152 FsResult<string> FsAtomicFile::StartWrite()
153 {
154 fs::path filePath = entity->newFileName;
155 fs::path parentPath = filePath.parent_path();
156 if (access(parentPath.c_str(), F_OK) != 0) {
157 HILOGE("Parent directory does not exist, err:%{public}d", errno);
158 return FsResult<string>::Error(ENOENT);
159 }
160
161 char *tmpfile = const_cast<char *>(entity->newFileName.c_str());
162 if (mkstemp(tmpfile) == -1) {
163 HILOGE("Fail to create tmp file err:%{public}d!", errno);
164 return FsResult<string>::Error(ENOENT);
165 }
166
167 return FsResult<string>::Success(entity->newFileName);
168 }
169
FinishWrite()170 FsResult<void> FsAtomicFile::FinishWrite()
171 {
172 if (std::rename(entity->newFileName.c_str(), entity->baseFileName.c_str()) != 0) {
173 HILOGE("Rename failed");
174 return FsResult<void>::Error(errno);
175 }
176 std::string tmpNewFileName = entity->baseFileName;
177 entity->newFileName = tmpNewFileName.append(TEMP_FILE_SUFFIX);
178
179 return FsResult<void>::Success();
180 }
181
FailWrite()182 FsResult<void> FsAtomicFile::FailWrite()
183 {
184 if (!fs::remove(entity->newFileName)) {
185 HILOGW("Failed to remove file");
186 return FsResult<void>::Error(errno);
187 }
188 std::string tmpNewFileName = entity->baseFileName;
189 entity->newFileName = tmpNewFileName.append(TEMP_FILE_SUFFIX);
190
191 return FsResult<void>::Success();
192 }
193
Delete()194 FsResult<void> FsAtomicFile::Delete()
195 {
196 auto rafentity = GetEntity();
197 if (rafentity == nullptr) {
198 HILOGE("Failed to get atomicFileEntity");
199 return FsResult<void>::Error(UNKNOWN_ERR);
200 }
201
202 bool errFlag = false;
203 std::error_code fsErrcode;
204 if (fs::exists(rafentity->newFileName, fsErrcode) && !fs::remove(rafentity->newFileName, fsErrcode)) {
205 errFlag = true;
206 }
207 if (fs::exists(rafentity->baseFileName, fsErrcode) && !fs::remove(rafentity->baseFileName, fsErrcode)) {
208 errFlag = true;
209 }
210 if (errFlag) {
211 HILOGE("Failed to remove file, err:%{public}s", fsErrcode.message().c_str());
212 return FsResult<void>::Error(fsErrcode.value());
213 }
214
215 rafentity->newFileName.clear();
216 rafentity->baseFileName.clear();
217 return FsResult<void>::Success();
218 }
219
Constructor(string path)220 FsResult<FsAtomicFile *> FsAtomicFile::Constructor(string path)
221 {
222 auto atomicFileEntity = CreateUniquePtr<FsAtomicFileEntity>();
223 if (atomicFileEntity == nullptr) {
224 HILOGE("Failed to request heap memory");
225 return FsResult<FsAtomicFile *>::Error(ENOMEM);
226 }
227 atomicFileEntity->baseFileName = path;
228 atomicFileEntity->newFileName = path.append(TEMP_FILE_SUFFIX);
229
230 auto file = new FsAtomicFile(move(atomicFileEntity));
231
232 return FsResult<FsAtomicFile *>::Success(file);
233 }
234 } // namespace ModuleFileIO
235 } // namespace FileManagement
236 } // namespace OHOS