1 /*
2  * Copyright (c) 2023 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 "close.h"
17 
18 #include <cstring>
19 #include <tuple>
20 #include <unistd.h>
21 
22 #include "common_func.h"
23 #include "filemgmt_libhilog.h"
24 
25 namespace OHOS {
26 namespace FileManagement {
27 namespace ModuleFileIO {
28 using namespace std;
29 using namespace OHOS::FileManagement::LibN;
30 
GetFileEntity(napi_env env,napi_value objFile)31 static FileEntity *GetFileEntity(napi_env env, napi_value objFile)
32 {
33     auto fileEntity = NClass::GetEntityOf<FileEntity>(env, objFile);
34     if (!fileEntity) {
35         HILOGE("Failed to get file entity");
36         return nullptr;
37     }
38     if (!fileEntity->fd_) {
39         HILOGE("The fd of entity is not exist");
40         return nullptr;
41     }
42     return fileEntity;
43 }
44 
CloseFd(int fd)45 static NError CloseFd(int fd)
46 {
47     std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> close_req = {
48         new uv_fs_t, CommonFunc::fs_req_cleanup };
49     if (!close_req) {
50         HILOGE("Failed to request heap memory.");
51         return NError(ENOMEM);
52     }
53     int ret = uv_fs_close(nullptr, close_req.get(), fd, nullptr);
54     if (ret < 0) {
55         HILOGE("Failed to close file with ret: %{public}d", ret);
56         return NError(ret);
57     }
58     return NError(ERRNO_NOERR);
59 }
60 
ParseJsOperand(napi_env env,napi_value fdOrFileFromJsArg)61 static tuple<bool, FileStruct> ParseJsOperand(napi_env env, napi_value fdOrFileFromJsArg)
62 {
63     auto [isFd, fd] = NVal(env, fdOrFileFromJsArg).ToInt32();
64     if (isFd && fd >= 0) {
65         return { true, FileStruct { true, fd, nullptr } };
66     }
67     if (isFd && fd < 0) {
68         return { false, FileStruct { false, -1, nullptr } };
69     }
70     auto file = GetFileEntity(env, fdOrFileFromJsArg);
71     if (file) {
72         return { true, FileStruct { false, -1, file } };
73     }
74 
75     return { false, FileStruct { false, -1, nullptr } };
76 }
77 
Sync(napi_env env,napi_callback_info info)78 napi_value Close::Sync(napi_env env, napi_callback_info info)
79 {
80     NFuncArg funcArg(env, info);
81     if (!funcArg.InitArgs(NARG_CNT::ONE)) {
82         HILOGE("Number of arguments unmatched");
83         NError(EINVAL).ThrowErr(env);
84         return nullptr;
85     }
86 
87     auto [resGetFirstArg, fileStruct] = ParseJsOperand(env, funcArg[NARG_POS::FIRST]);
88     if (!resGetFirstArg) {
89         HILOGI("Failed to parse fd or FileEntity from JS parameter");
90         NError(EINVAL).ThrowErr(env);
91         return nullptr;
92     }
93 
94     if (fileStruct.isFd) {
95         auto err = CloseFd(fileStruct.fd);
96         if (err) {
97             err.ThrowErr(env);
98             return nullptr;
99         }
100     } else {
101         auto err = CloseFd(fileStruct.fileEntity->fd_->GetFD());
102         if (err) {
103             err.ThrowErr(env);
104             return nullptr;
105         }
106         auto fp = NClass::RemoveEntityOfFinal<FileEntity>(env, funcArg[NARG_POS::FIRST]);
107         if (!fp) {
108             NError(EINVAL).ThrowErr(env);
109             return nullptr;
110         }
111     }
112 
113     return NVal::CreateUndefined(env).val_;
114 }
115 
Async(napi_env env,napi_callback_info info)116 napi_value Close::Async(napi_env env, napi_callback_info info)
117 {
118     NFuncArg funcArg(env, info);
119     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
120         HILOGE("Number of arguments unmatched");
121         NError(EINVAL).ThrowErr(env);
122         return nullptr;
123     }
124 
125     auto [resGetFirstArg, fileStruct] = ParseJsOperand(env, funcArg[NARG_POS::FIRST]);
126     if (!resGetFirstArg) {
127         HILOGI("Failed to parse JS operand");
128         NError(EINVAL).ThrowErr(env);
129         return nullptr;
130     }
131 
132     if (!fileStruct.isFd) {
133         fileStruct.fd = fileStruct.fileEntity->fd_->GetFD();
134         fileStruct.isFd = true;
135         auto fp = NClass::RemoveEntityOfFinal<FileEntity>(env, funcArg[NARG_POS::FIRST]);
136         if (!fp) {
137             NError(EINVAL).ThrowErr(env);
138             return nullptr;
139         }
140     }
141 
142     auto cbExec = [fileStruct = fileStruct]() -> NError {
143         if (fileStruct.isFd) {
144             return CloseFd(fileStruct.fd);
145         }
146         return NError(ERRNO_NOERR);
147     };
148 
149     auto cbComplete = [](napi_env env, NError err) -> NVal {
150         if (err) {
151             return { env, err.GetNapiErr(env) };
152         } else {
153             return NVal::CreateUndefined(env);
154         }
155     };
156 
157     size_t argc = funcArg.GetArgc();
158     NVal thisVar(env, funcArg.GetThisVar());
159     if (argc == NARG_CNT::ONE) {
160         return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_CLOSE_NAME, cbExec, cbComplete).val_;
161     } else {
162         NVal cb(env, funcArg[NARG_POS::SECOND]);
163         return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_CLOSE_NAME, cbExec, cbComplete).val_;
164     }
165 }
166 } // namespace ModuleFileIO
167 } // namespace FileManagement
168 } // namespace OHOS