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 "atomicfile_ani.h"
17
18 #include <filesystem>
19
20 #include "ani_signature.h"
21 #include "error_handler.h"
22 #include "file_wrapper.h"
23 #include "filemgmt_libhilog.h"
24 #include "fs_atomicfile.h"
25 #include "type_converter.h"
26
27 namespace OHOS {
28 namespace FileManagement {
29 namespace ModuleFileIO {
30 namespace ANI {
31 namespace fs = filesystem;
32 using namespace std;
33 using namespace OHOS::FileManagement::ModuleFileIO;
34 using namespace OHOS::FileManagement::ModuleFileIO::ANI::AniSignature;
35
36 const string READ_STREAM_CLASS = "ReadStream";
37 const string WRITE_STREAM_CLASS = "WriteStream";
38 const string TEMP_FILE_SUFFIX = "_XXXXXX";
39
Constructor(ani_env * env,ani_object obj,ani_string pathObj)40 void AtomicFileAni::Constructor(ani_env *env, ani_object obj, ani_string pathObj)
41 {
42 auto [succ, filePath] = TypeConverter::ToUTF8String(env, pathObj);
43 if (!succ) {
44 HILOGE("Invalid path");
45 ErrorHandler::Throw(env, E_PARAMS);
46 return;
47 }
48
49 auto ret = FsAtomicFile::Constructor(filePath);
50 if (!ret.IsSuccess()) {
51 const auto &err = ret.GetError();
52 ErrorHandler::Throw(env, err);
53 return;
54 }
55
56 if (ANI_OK !=
57 env->Object_SetFieldByName_Long(obj, "nativePtr",
58 static_cast<ani_long>(reinterpret_cast<uintptr_t>(ret.GetData().value())))) {
59 HILOGE("Failed to wrap entity for obj AtomicFile");
60 ErrorHandler::Throw(env, EIO);
61 return;
62 }
63 }
64
Unwrap(ani_env * env,ani_object object)65 static FsAtomicFile *Unwrap(ani_env *env, ani_object object)
66 {
67 ani_long file;
68 auto ret = env->Object_GetFieldByName_Long(object, "nativePtr", &file);
69 if (ret != ANI_OK) {
70 HILOGE("Unwrap file err: %{private}d", ret);
71 return nullptr;
72 }
73
74 return reinterpret_cast<FsAtomicFile *>(file);
75 }
76
GetPath(ani_env * env,ani_object object)77 ani_string AtomicFileAni::GetPath(ani_env *env, [[maybe_unused]] ani_object object)
78 {
79 auto file = Unwrap(env, object);
80 if (file == nullptr) {
81 ErrorHandler::Throw(env, E_PARAMS);
82 return nullptr;
83 }
84
85 string path = file->GetPath();
86 auto [succ, result] = TypeConverter::ToAniString(env, path);
87 if (!succ) {
88 HILOGE("ToAniString failed");
89 ErrorHandler::Throw(env, UNKNOWN_ERR);
90 return nullptr;
91 }
92
93 return result;
94 }
95
GetBaseFile(ani_env * env,ani_object object)96 ani_object AtomicFileAni::GetBaseFile(ani_env *env, [[maybe_unused]] ani_object object)
97 {
98 auto atomicFile = Unwrap(env, object);
99 if (atomicFile == nullptr) {
100 HILOGE("Failed to get atomicFile");
101 ErrorHandler::Throw(env, UNKNOWN_ERR);
102 return nullptr;
103 }
104
105 auto ret = atomicFile->GetBaseFile();
106 if (!ret.IsSuccess()) {
107 HILOGE("Failed to GetBaseFile");
108 const auto &err = ret.GetError();
109 ErrorHandler::Throw(env, err);
110 return nullptr;
111 }
112
113 const FsFile *fsFile = ret.GetData().value();
114 auto result = FileWrapper::Wrap(env, move(fsFile));
115 if (result == nullptr) {
116 HILOGE("Failed to wrap");
117 ErrorHandler::Throw(env, UNKNOWN_ERR);
118 return nullptr;
119 }
120
121 return result;
122 }
123
CreateReadStream(ani_env * env,ani_string filePath)124 static ani_object CreateReadStream(ani_env *env, ani_string filePath)
125 {
126 auto classDesc = FS::ReadStream::classDesc.c_str();
127 ani_class cls;
128 if (ANI_OK != env->FindClass(classDesc, &cls)) {
129 HILOGE("Cannot find class %s", classDesc);
130 return nullptr;
131 }
132
133 auto ctorDesc = FS::ReadStream::ctorDesc.c_str();
134 auto ctorSig = FS::ReadStream::ctorSig.c_str();
135 ani_method ctor;
136 if (ANI_OK != env->Class_FindMethod(cls, ctorDesc, ctorSig, &ctor)) {
137 HILOGE("Cannot find constructor method for class %s", classDesc);
138 return nullptr;
139 }
140
141 ani_object obj;
142 if (ANI_OK != env->Object_New(cls, ctor, &obj, filePath)) {
143 HILOGE("New %s obj Failed", classDesc);
144 return nullptr;
145 }
146
147 return move(obj);
148 }
149
CreateWriteStream(ani_env * env,ani_string filePath)150 static ani_object CreateWriteStream(ani_env *env, ani_string filePath)
151 {
152 auto classDesc = FS::WriteStream::classDesc.c_str();
153 ani_class cls;
154 if (ANI_OK != env->FindClass(classDesc, &cls)) {
155 HILOGE("Cannot find class %s", classDesc);
156 return nullptr;
157 }
158
159 auto ctorDesc = FS::WriteStream::ctorDesc.c_str();
160 auto ctorSig = FS::WriteStream::ctorSig.c_str();
161 ani_method ctor;
162 if (ANI_OK != env->Class_FindMethod(cls, ctorDesc, ctorSig, &ctor)) {
163 HILOGE("Cannot find constructor method for class %s", classDesc);
164 return nullptr;
165 }
166
167 ani_object obj;
168 if (ANI_OK != env->Object_New(cls, ctor, &obj, filePath)) {
169 HILOGE("New %s obj Failed", classDesc);
170 return nullptr;
171 }
172
173 return move(obj);
174 }
175
CreateStream(ani_env * env,ani_object object,const string & streamName,const string & fileName)176 static ani_object CreateStream(
177 ani_env *env, [[maybe_unused]] ani_object object, const string &streamName, const string &fileName)
178 {
179 auto [succ, filePath] = TypeConverter::ToAniString(env, fileName);
180 if (!succ) {
181 HILOGE("Failed to ani_string");
182 ErrorHandler::Throw(env, UNKNOWN_ERR);
183 return nullptr;
184 }
185
186 if (streamName == READ_STREAM_CLASS) {
187 auto stream = CreateReadStream(env, filePath);
188 if (stream == nullptr) {
189 HILOGE("Failed to create read stream");
190 ErrorHandler::Throw(env, UNKNOWN_ERR);
191 return nullptr;
192 }
193 return move(stream);
194 }
195 if (streamName == WRITE_STREAM_CLASS) {
196 auto stream = CreateWriteStream(env, filePath);
197 if (stream == nullptr) {
198 HILOGE("Failed to create write stream");
199 ErrorHandler::Throw(env, UNKNOWN_ERR);
200 return nullptr;
201 }
202 return move(stream);
203 }
204
205 ErrorHandler::Throw(env, UNKNOWN_ERR);
206 return nullptr;
207 }
208
OpenRead(ani_env * env,ani_object object)209 ani_object AtomicFileAni::OpenRead(ani_env *env, [[maybe_unused]] ani_object object)
210 {
211 auto atomicFile = Unwrap(env, object);
212 if (atomicFile == nullptr) {
213 HILOGE("Failed to get atomicFile");
214 ErrorHandler::Throw(env, UNKNOWN_ERR);
215 return nullptr;
216 }
217
218 auto entity = atomicFile->GetEntity();
219 if (entity == nullptr) {
220 ErrorHandler::Throw(env, UNKNOWN_ERR);
221 return nullptr;
222 }
223
224 return CreateStream(env, object, READ_STREAM_CLASS, entity->baseFileName);
225 }
226
ReadFully(ani_env * env,ani_object object)227 ani_arraybuffer AtomicFileAni::ReadFully(ani_env *env, [[maybe_unused]] ani_object object)
228 {
229 auto atomicFile = Unwrap(env, object);
230 if (atomicFile == nullptr) {
231 HILOGE("Failed to get atomicFile");
232 ErrorHandler::Throw(env, UNKNOWN_ERR);
233 return nullptr;
234 }
235
236 auto ret = atomicFile->ReadFully();
237 if (!ret.IsSuccess()) {
238 HILOGE("Failed to read fully");
239 ErrorHandler::Throw(env, ret.GetError());
240 return nullptr;
241 }
242
243 auto &bufferData = ret.GetData().value();
244 uint8_t *buffer = bufferData->buffer;
245 size_t length = bufferData->length;
246 auto [succ, obj] = TypeConverter::ToAniArrayBuffer(env, buffer, length);
247 if (!succ) {
248 HILOGE("Failed to ani_arrayBuffer");
249 ErrorHandler::Throw(env, UNKNOWN_ERR);
250 return nullptr;
251 }
252
253 return obj;
254 }
255
StartWrite(ani_env * env,ani_object object)256 ani_object AtomicFileAni::StartWrite(ani_env *env, [[maybe_unused]] ani_object object)
257 {
258 auto atomicFile = Unwrap(env, object);
259 if (atomicFile == nullptr) {
260 HILOGE("Failed to get atomicFile");
261 ErrorHandler::Throw(env, UNKNOWN_ERR);
262 return nullptr;
263 }
264
265 auto entity = atomicFile->GetEntity();
266 if (entity == nullptr) {
267 HILOGE("Failed to get atomicFile entity");
268 ErrorHandler::Throw(env, UNKNOWN_ERR);
269 return nullptr;
270 }
271
272 fs::path filePath = entity->newFileName;
273 fs::path parentPath = filePath.parent_path();
274 if (access(parentPath.c_str(), F_OK) != 0) {
275 HILOGE("Parent directory does not exist, err:%{public}d", errno);
276 ErrorHandler::Throw(env, ENOENT);
277 return nullptr;
278 }
279
280 char *tmpfile = const_cast<char *>(entity->newFileName.c_str());
281 if (mkstemp(tmpfile) == -1) {
282 HILOGE("Fail to create tmp file err:%{public}d!", errno);
283 ErrorHandler::Throw(env, ENOENT);
284 return nullptr;
285 }
286
287 ani_object writeStream = CreateStream(env, object, WRITE_STREAM_CLASS, entity->newFileName);
288 if (writeStream == nullptr) {
289 HILOGE("Failed to create write stream");
290 return nullptr;
291 }
292
293 return writeStream;
294 }
295
FinishWrite(ani_env * env,ani_object object)296 void AtomicFileAni::FinishWrite(ani_env *env, [[maybe_unused]] ani_object object)
297 {
298 auto atomicFile = Unwrap(env, object);
299 if (atomicFile == nullptr) {
300 HILOGE("Failed to get atomicFile");
301 ErrorHandler::Throw(env, UNKNOWN_ERR);
302 return;
303 }
304
305 auto entity = atomicFile->GetEntity();
306 if (entity == nullptr) {
307 HILOGE("Failed to get atomicFile entity");
308 ErrorHandler::Throw(env, UNKNOWN_ERR);
309 return;
310 }
311
312 auto ret = atomicFile->FinishWrite();
313 if (!ret.IsSuccess()) {
314 HILOGE("Failed to finish write");
315 const auto &err = ret.GetError();
316 ErrorHandler::Throw(env, err);
317 }
318 return;
319 }
320
FailWrite(ani_env * env,ani_object object)321 void AtomicFileAni::FailWrite(ani_env *env, [[maybe_unused]] ani_object object)
322 {
323 auto atomicFile = Unwrap(env, object);
324 if (atomicFile == nullptr) {
325 HILOGE("Failed to get atomicFile");
326 ErrorHandler::Throw(env, UNKNOWN_ERR);
327 return;
328 }
329
330 auto entity = atomicFile->GetEntity();
331 if (entity == nullptr) {
332 HILOGE("Failed to get atomicFile entity");
333 ErrorHandler::Throw(env, UNKNOWN_ERR);
334 return;
335 }
336
337 auto ret = atomicFile->FailWrite();
338 if (!ret.IsSuccess()) {
339 HILOGE("Failed to fail write");
340 const auto &err = ret.GetError();
341 ErrorHandler::Throw(env, err);
342 }
343 return;
344 }
345
Delete(ani_env * env,ani_object object)346 void AtomicFileAni::Delete(ani_env *env, [[maybe_unused]] ani_object object)
347 {
348 auto atomicFile = Unwrap(env, object);
349 if (atomicFile == nullptr) {
350 HILOGE("Failed to get atomicFile");
351 ErrorHandler::Throw(env, UNKNOWN_ERR);
352 return;
353 }
354
355 auto ret = atomicFile->Delete();
356 if (!ret.IsSuccess()) {
357 HILOGE("Failed to delete");
358 ErrorHandler::Throw(env, ret.GetError());
359 return;
360 }
361
362 return;
363 }
364 } // namespace ANI
365 } // namespace ModuleFileIO
366 } // namespace FileManagement
367 } // namespace OHOS