• 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 "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