• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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