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 "randomaccessfile_ani.h"
17
18 #include "ani_helper.h"
19 #include "ani_signature.h"
20 #include "error_handler.h"
21 #include "filemgmt_libhilog.h"
22 #include "fs_randomaccessfile.h"
23 #include "type_converter.h"
24
25 namespace OHOS {
26 namespace FileManagement {
27 namespace ModuleFileIO {
28 namespace ANI {
29 using namespace std;
30 using namespace OHOS::FileManagement::ModuleFileIO;
31 using namespace OHOS::FileManagement::ModuleFileIO::ANI::AniSignature;
32
33 const int BUF_SIZE = 1024;
34 const string READ_STREAM_CLASS = "ReadStream";
35 const string WRITE_STREAM_CLASS = "WriteStream";
36 const string OFFSET = "offset";
37 const string LENGTH = "length";
38
Unwrap(ani_env * env,ani_object object)39 static FsRandomAccessFile *Unwrap(ani_env *env, ani_object object)
40 {
41 ani_long nativePtr;
42 auto ret = env->Object_GetFieldByName_Long(object, "nativePtr", &nativePtr);
43 if (ret != ANI_OK) {
44 HILOGE("Unwrap FsRandomAccessFile err: %{public}d", ret);
45 return nullptr;
46 }
47 uintptr_t ptrValue = static_cast<uintptr_t>(nativePtr);
48 FsRandomAccessFile *rafFile = reinterpret_cast<FsRandomAccessFile *>(ptrValue);
49 return rafFile;
50 }
51
ToReadOptions(ani_env * env,ani_object obj)52 static tuple<bool, optional<ReadOptions>> ToReadOptions(ani_env *env, ani_object obj)
53 {
54 ReadOptions options;
55 ani_boolean isUndefined;
56 env->Reference_IsUndefined(obj, &isUndefined);
57 if (isUndefined) {
58 return { true, nullopt };
59 }
60
61 auto [succOffset, offset] = AniHelper::ParseInt64Option(env, obj, OFFSET);
62 if (!succOffset) {
63 HILOGE("Illegal option.offset parameter");
64 return { false, nullopt };
65 }
66 options.offset = offset;
67
68 auto [succLength, length] = AniHelper::ParseInt64Option(env, obj, LENGTH);
69 if (!succLength) {
70 HILOGE("Illegal option.length parameter");
71 return { false, nullopt };
72 }
73 options.length = length;
74 return { true, make_optional<ReadOptions>(move(options)) };
75 }
76
ToWriteOptions(ani_env * env,ani_object obj)77 static tuple<bool, optional<WriteOptions>> ToWriteOptions(ani_env *env, ani_object obj)
78 {
79 WriteOptions options;
80 ani_boolean isUndefined;
81 env->Reference_IsUndefined(obj, &isUndefined);
82 if (isUndefined) {
83 return { true, nullopt };
84 }
85
86 auto [succOffset, offset] = AniHelper::ParseInt64Option(env, obj, OFFSET);
87 if (!succOffset) {
88 HILOGE("Illegal option.offset parameter");
89 return { false, nullopt };
90 }
91 options.offset = offset;
92
93 auto [succLength, length] = AniHelper::ParseInt64Option(env, obj, LENGTH);
94 if (!succLength) {
95 HILOGE("Illegal option.length parameter");
96 return { false, nullopt };
97 }
98 options.length = length;
99
100 auto [succEncoding, encoding] = AniHelper::ParseEncoding(env, obj);
101 if (!succEncoding) {
102 HILOGE("Illegal option.encoding parameter");
103 return { false, nullopt };
104 }
105 options.encoding = encoding;
106 return { true, make_optional<WriteOptions>(move(options)) };
107 }
108
ParseStringBuffer(ani_env * env,const ani_object & buf)109 static tuple<bool, ani_string> ParseStringBuffer(ani_env *env, const ani_object &buf)
110 {
111 ani_class cls;
112 auto classDesc = BuiltInTypes::String::classDesc.c_str();
113 env->FindClass(classDesc, &cls);
114
115 ani_boolean isString;
116 env->Object_InstanceOf(buf, cls, &isString);
117 if (!isString) {
118 return { false, {} };
119 }
120 auto result = static_cast<ani_string>(buf);
121 return { true, move(result) };
122 }
123
ParseArrayBuffer(ani_env * env,const ani_object & buf)124 static tuple<bool, ani_arraybuffer> ParseArrayBuffer(ani_env *env, const ani_object &buf)
125 {
126 ani_class cls;
127 auto classDesc = BuiltInTypes::ArrayBuffer::classDesc.c_str();
128 env->FindClass(classDesc, &cls);
129
130 ani_boolean isArrayBuffer;
131 env->Object_InstanceOf(buf, cls, &isArrayBuffer);
132 if (!isArrayBuffer) {
133 return { false, {} };
134 }
135 auto result = static_cast<ani_arraybuffer>(buf);
136 return { true, move(result) };
137 }
138
SetFilePointer(ani_env * env,ani_object object,ani_double fp)139 void RandomAccessFileAni::SetFilePointer(ani_env *env, [[maybe_unused]] ani_object object, ani_double fp)
140 {
141 auto rafFile = Unwrap(env, object);
142 if (rafFile == nullptr) {
143 HILOGE("Cannot unwrap rafFile!");
144 ErrorHandler::Throw(env, UNKNOWN_ERR);
145 return;
146 }
147 auto ret = rafFile->SetFilePointerSync(static_cast<int64_t>(fp));
148 if (!ret.IsSuccess()) {
149 HILOGE("SetFilePointerSync failed!");
150 const auto &err = ret.GetError();
151 ErrorHandler::Throw(env, err);
152 return;
153 }
154 }
155
Close(ani_env * env,ani_object object)156 void RandomAccessFileAni::Close(ani_env *env, [[maybe_unused]] ani_object object)
157 {
158 auto rafFile = Unwrap(env, object);
159 if (rafFile == nullptr) {
160 HILOGE("Cannot unwrap rafFile!");
161 ErrorHandler::Throw(env, UNKNOWN_ERR);
162 return;
163 }
164 auto ret = rafFile->CloseSync();
165 if (!ret.IsSuccess()) {
166 HILOGE("close rafFile failed!");
167 const auto &err = ret.GetError();
168 ErrorHandler::Throw(env, err);
169 return;
170 }
171 }
172
WriteSync(ani_env * env,ani_object object,ani_object buf,ani_object options)173 ani_double RandomAccessFileAni::WriteSync(
174 ani_env *env, [[maybe_unused]] ani_object object, ani_object buf, ani_object options)
175 {
176 auto rafFile = Unwrap(env, object);
177 if (rafFile == nullptr) {
178 HILOGE("Cannot unwrap rafFile!");
179 ErrorHandler::Throw(env, UNKNOWN_ERR);
180 return -1;
181 }
182
183 auto [succOp, op] = ToWriteOptions(env, options);
184 if (!succOp) {
185 HILOGE("Failed to resolve options!");
186 ErrorHandler::Throw(env, EINVAL);
187 return -1;
188 }
189
190 auto [isString, stringBuffer] = ParseStringBuffer(env, buf);
191 if (isString) {
192 auto [succBuf, buffer] = TypeConverter::ToUTF8String(env, stringBuffer);
193 if (!succBuf) {
194 HILOGE("Failed to resolve stringBuffer!");
195 ErrorHandler::Throw(env, EINVAL);
196 return -1;
197 }
198 auto ret = rafFile->WriteSync(buffer, op);
199 if (!ret.IsSuccess()) {
200 HILOGE("write buffer failed!");
201 ErrorHandler::Throw(env, ret.GetError());
202 return -1;
203 }
204 return static_cast<double>(ret.GetData().value());
205 }
206
207 auto [isArrayBuffer, arrayBuffer] = ParseArrayBuffer(env, buf);
208 if (isArrayBuffer) {
209 auto [succBuf, buffer] = TypeConverter::ToArrayBuffer(env, arrayBuffer);
210 if (!succBuf) {
211 HILOGE("Failed to resolve arrayBuffer!");
212 ErrorHandler::Throw(env, EINVAL);
213 return -1;
214 }
215 auto ret = rafFile->WriteSync(buffer, op);
216 if (!ret.IsSuccess()) {
217 HILOGE("write buffer failed!");
218 ErrorHandler::Throw(env, ret.GetError());
219 return -1;
220 }
221 return static_cast<double>(ret.GetData().value());
222 }
223 HILOGE("Unsupported buffer type!");
224 ErrorHandler::Throw(env, EINVAL);
225 return -1;
226 }
227
ReadSync(ani_env * env,ani_object object,ani_arraybuffer buf,ani_object options)228 ani_double RandomAccessFileAni::ReadSync(
229 ani_env *env, [[maybe_unused]] ani_object object, ani_arraybuffer buf, ani_object options)
230 {
231 auto rafFile = Unwrap(env, object);
232 if (rafFile == nullptr) {
233 HILOGE("Cannot unwrap rafFile!");
234 ErrorHandler::Throw(env, UNKNOWN_ERR);
235 return -1;
236 }
237
238 auto [succBuf, arrayBuffer] = TypeConverter::ToArrayBuffer(env, buf);
239 if (!succBuf) {
240 HILOGE("Failed to resolve arrayBuffer!");
241 ErrorHandler::Throw(env, EINVAL);
242 return -1;
243 }
244
245 auto [succOp, op] = ToReadOptions(env, options);
246 if (!succOp) {
247 HILOGE("Failed to resolve options!");
248 ErrorHandler::Throw(env, EINVAL);
249 return -1;
250 }
251
252 auto ret = rafFile->ReadSync(arrayBuffer, op);
253 if (!ret.IsSuccess()) {
254 HILOGE("Read file content failed!");
255 const auto &err = ret.GetError();
256 ErrorHandler::Throw(env, err);
257 return -1;
258 }
259 return static_cast<double>(ret.GetData().value());
260 }
261
GetFilePath(ani_env * env,const int fd)262 static ani_string GetFilePath(ani_env *env, const int fd)
263 {
264 auto dstFd = dup(fd);
265 if (dstFd < 0) {
266 HILOGE("Failed to get valid fd, fail reason: %{public}s, fd: %{public}d", strerror(errno), fd);
267 return nullptr;
268 }
269
270 string path = "/proc/self/fd/" + to_string(dstFd);
271 auto buf = CreateUniquePtr<char[]>(BUF_SIZE);
272 int readLinkRes = readlink(path.c_str(), buf.get(), BUF_SIZE);
273 if (readLinkRes < 0) {
274 close(dstFd);
275 return nullptr;
276 }
277
278 close(dstFd);
279 auto [succ, filePath] = TypeConverter::ToAniString(env, string(buf.get()));
280 if (!succ) {
281 return nullptr;
282 }
283 return move(filePath);
284 }
285
CreateReadStreamOptions(ani_env * env,int64_t start,int64_t end)286 static ani_object CreateReadStreamOptions(ani_env *env, int64_t start, int64_t end)
287 {
288 static const char *className = FS::ReadStreamOptionsInner::classDesc.c_str();
289 ani_class cls;
290 if (ANI_OK != env->FindClass(className, &cls)) {
291 HILOGE("Cannot find class %{public}s", className);
292 return nullptr;
293 }
294 ani_method ctor;
295 if (ANI_OK != env->Class_FindMethod(cls, FS::ReadStreamOptionsInner::ctorDesc.c_str(),
296 FS::ReadStreamOptionsInner::ctorSig.c_str(), &ctor)) {
297 HILOGE("Cannot find constructor method for class %{public}s", className);
298 return nullptr;
299 }
300 ani_object obj;
301 if (ANI_OK != env->Object_New(cls, ctor, &obj)) {
302 HILOGE("New %{public}s obj Failed", className);
303 return nullptr;
304 }
305
306 ani_field startField = nullptr;
307 ani_field endField = nullptr;
308 if (ANI_OK != env->Class_FindField(cls, "start", &startField)) {
309 HILOGE("Cannot find start in class %{public}s", className);
310 return nullptr;
311 }
312 if (ANI_OK != env->Class_FindField(cls, "end", &endField)) {
313 HILOGE("Cannot find end in class %{public}s", className);
314 return nullptr;
315 }
316
317 if (start >= 0) {
318 env->Object_SetField_Int(obj, startField, start);
319 }
320 if (end >= 0) {
321 env->Object_SetField_Int(obj, endField, end);
322 }
323 if (obj == nullptr) {
324 HILOGE("CreateReadStreamOptions is nullptr");
325 }
326
327 return move(obj);
328 }
329
CreateWriteStreamOptions(ani_env * env,int64_t start,int flags)330 static ani_object CreateWriteStreamOptions(ani_env *env, int64_t start, int flags)
331 {
332 static const char *className = FS::WriteStreamOptionsInner::classDesc.c_str();
333 ani_class cls;
334 if (ANI_OK != env->FindClass(className, &cls)) {
335 HILOGE("Cannot find class %{public}s", className);
336 return nullptr;
337 }
338 ani_method ctor;
339 if (ANI_OK != env->Class_FindMethod(cls, FS::WriteStreamOptionsInner::ctorDesc.c_str(),
340 FS::WriteStreamOptionsInner::ctorSig.c_str(), &ctor)) {
341 HILOGE("Cannot find constructor method for class %{public}s", className);
342 return nullptr;
343 }
344 ani_object obj;
345 if (ANI_OK != env->Object_New(cls, ctor, &obj)) {
346 HILOGE("New %{public}s obj Failed", className);
347 return nullptr;
348 }
349
350 ani_field modeField = nullptr;
351 ani_field startField = nullptr;
352 if (ANI_OK != env->Class_FindField(cls, "mode", &modeField)) {
353 HILOGE("Cannot find mode in class %{public}s", className);
354 return nullptr;
355 }
356 if (ANI_OK != env->Class_FindField(cls, "start", &startField)) {
357 HILOGE("Cannot find start in class %{public}s", className);
358 return nullptr;
359 }
360
361 env->Object_SetField_Int(obj, modeField, flags);
362 if (start >= 0) {
363 env->Object_SetField_Int(obj, startField, start);
364 }
365
366 return move(obj);
367 }
368
CreateReadStream(ani_env * env,ani_string filePath,ani_object options)369 static ani_object CreateReadStream(ani_env *env, ani_string filePath, ani_object options)
370 {
371 static const char *className = "L@ohos/file/fs/fileIo/ReadStream;";
372 ani_class cls;
373 if (ANI_OK != env->FindClass(className, &cls)) {
374 HILOGE("Cannot find class %{public}s", className);
375 return nullptr;
376 }
377 ani_method ctor;
378 if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", "Lstd/core/String;L@ohos/file/fs/ReadStreamOptions;:V", &ctor)) {
379 HILOGE("Cannot find constructor method for class %{public}s", className);
380 return nullptr;
381 }
382 ani_object obj;
383 if (ANI_OK != env->Object_New(cls, ctor, &obj, filePath, options)) {
384 HILOGE("New %{public}s obj Failed", className);
385 return nullptr;
386 }
387
388 return move(obj);
389 }
390
CreateWriteStream(ani_env * env,ani_string filePath,ani_object options)391 static ani_object CreateWriteStream(ani_env *env, ani_string filePath, ani_object options)
392 {
393 static const char *className = "L@ohos/file/fs/fileIo/WriteStream;";
394 ani_class cls;
395 if (ANI_OK != env->FindClass(className, &cls)) {
396 HILOGE("Cannot find class %{public}s", className);
397 return nullptr;
398 }
399 ani_method ctor;
400 if (ANI_OK !=
401 env->Class_FindMethod(cls, "<ctor>", "Lstd/core/String;L@ohos/file/fs/WriteStreamOptions;:V", &ctor)) {
402 HILOGE("Cannot find constructor method for class %{public}s", className);
403 return nullptr;
404 }
405 ani_object obj;
406 if (ANI_OK != env->Object_New(cls, ctor, &obj, filePath, options)) {
407 HILOGE("New %{public}s obj Failed", className);
408 return nullptr;
409 }
410
411 return move(obj);
412 }
413
CreateStream(ani_env * env,const string & streamName,RandomAccessFileEntity * rafEntity,int flags)414 static ani_object CreateStream(ani_env *env, const string &streamName, RandomAccessFileEntity *rafEntity, int flags)
415 {
416 ani_string filePath = GetFilePath(env, rafEntity->fd.get()->GetFD());
417 if (!filePath) {
418 HILOGE("Get file path failed, errno=%{public}d", errno);
419 ErrorHandler::Throw(env, errno);
420 return nullptr;
421 }
422
423 if (streamName == READ_STREAM_CLASS) {
424 ani_object obj = CreateReadStreamOptions(env, rafEntity->start, rafEntity->end);
425 return CreateReadStream(env, filePath, obj);
426 }
427 if (streamName == WRITE_STREAM_CLASS) {
428 ani_object obj = CreateWriteStreamOptions(env, rafEntity->start, flags);
429 return CreateWriteStream(env, filePath, obj);
430 }
431
432 return nullptr;
433 }
434
GetReadStream(ani_env * env,ani_object object)435 ani_object RandomAccessFileAni::GetReadStream(ani_env *env, [[maybe_unused]] ani_object object)
436 {
437 auto rafFile = Unwrap(env, object);
438 if (rafFile == nullptr) {
439 HILOGE("Cannot unwrap rafFile!");
440 ErrorHandler::Throw(env, UNKNOWN_ERR);
441 return nullptr;
442 }
443
444 auto entity = rafFile->GetRAFEntity();
445 if (!entity) {
446 HILOGE("Get RandomAccessFileEntity failed!");
447 ErrorHandler::Throw(env, EIO);
448 return nullptr;
449 }
450
451 int flags = fcntl(entity->fd.get()->GetFD(), F_GETFL);
452 unsigned int uflags = static_cast<unsigned int>(flags);
453 if (((uflags & O_ACCMODE) != O_RDONLY) && ((uflags & O_ACCMODE) != O_RDWR)) {
454 HILOGE("Failed to check Permission");
455 ErrorHandler::Throw(env, EACCES);
456 return nullptr;
457 }
458
459 return CreateStream(env, READ_STREAM_CLASS, entity, flags);
460 }
461
GetWriteStream(ani_env * env,ani_object object)462 ani_object RandomAccessFileAni::GetWriteStream(ani_env *env, [[maybe_unused]] ani_object object)
463 {
464 auto rafFile = Unwrap(env, object);
465 if (rafFile == nullptr) {
466 HILOGE("Cannot unwrap rafFile!");
467 ErrorHandler::Throw(env, UNKNOWN_ERR);
468 return nullptr;
469 }
470
471 auto entity = rafFile->GetRAFEntity();
472 if (!entity) {
473 HILOGE("Get RandomAccessFileEntity failed!");
474 ErrorHandler::Throw(env, EIO);
475 return nullptr;
476 }
477
478 int flags = fcntl(entity->fd.get()->GetFD(), F_GETFL);
479 unsigned int uflags = static_cast<unsigned int>(flags);
480 if (((uflags & O_ACCMODE) != O_WRONLY) && ((uflags & O_ACCMODE) != O_RDWR)) {
481 HILOGE("Failed to check Permission");
482 ErrorHandler::Throw(env, EACCES);
483 return nullptr;
484 }
485
486 return CreateStream(env, WRITE_STREAM_CLASS, entity, flags);
487 }
488
489 } // namespace ANI
490 } // namespace ModuleFileIO
491 } // namespace FileManagement
492 } // namespace OHOS