1 /*
2  * Copyright (c) 2022-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 "truncate.h"
17 
18 #include <cstring>
19 #include <tuple>
20 #include <unistd.h>
21 
22 #include "common_func.h"
23 #include "file_utils.h"
24 #include "filemgmt_libhilog.h"
25 
26 namespace OHOS::FileManagement::ModuleFileIO {
27 using namespace std;
28 using namespace OHOS::FileManagement::LibN;
29 
ParseJsFile(napi_env env,napi_value pathOrFdFromJsArg)30 static tuple<bool, FileInfo> ParseJsFile(napi_env env, napi_value pathOrFdFromJsArg)
31 {
32     auto [isPath, path, ignore] = NVal(env, pathOrFdFromJsArg).ToUTF8StringPath();
33     if (isPath) {
34         return { true, FileInfo { true, move(path), {} } };
35     }
36     auto [isFd, fd] = NVal(env, pathOrFdFromJsArg).ToInt32();
37     if (!isFd || fd < 0) {
38         HILOGE("Invalid fd");
39         NError(EINVAL).ThrowErr(env);
40         return { false, FileInfo { false, {}, {} } };
41     }
42     auto fdg = CreateUniquePtr<DistributedFS::FDGuard>(fd, false);
43     if (fdg == nullptr) {
44         HILOGE("Failed to request heap memory.");
45         NError(ENOMEM).ThrowErr(env);
46         return { false, FileInfo { false, {}, {} } };
47     }
48     return { true, FileInfo { false, {}, move(fdg) } };
49 };
50 
TruncateCore(napi_env env,FileInfo & fileInfo,int64_t truncateLen)51 static NError TruncateCore(napi_env env, FileInfo &fileInfo, int64_t truncateLen)
52 {
53     if (fileInfo.isPath) {
54         std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> open_req = {
55             new uv_fs_t, CommonFunc::fs_req_cleanup };
56         if (!open_req) {
57             HILOGE("Failed to request heap memory.");
58             return NError(ENOMEM);
59         }
60         int ret = uv_fs_open(nullptr, open_req.get(), fileInfo.path.get(), O_RDWR,
61                              S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, nullptr);
62         if (ret < 0) {
63             return NError(ret);
64         }
65         std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> ftruncate_req = {
66             new uv_fs_t, CommonFunc::fs_req_cleanup };
67         if (!ftruncate_req) {
68             HILOGE("Failed to request heap memory.");
69             return NError(ENOMEM);
70         }
71         ret = uv_fs_ftruncate(nullptr, ftruncate_req.get(), ret, truncateLen, nullptr);
72         if (ret < 0) {
73             HILOGE("Failed to truncate file by path");
74             return NError(ret);
75         }
76     } else {
77         std::unique_ptr<uv_fs_t, decltype(CommonFunc::fs_req_cleanup)*> ftruncate_req = {
78             new uv_fs_t, CommonFunc::fs_req_cleanup };
79         if (!ftruncate_req) {
80             HILOGE("Failed to request heap memory.");
81             return NError(ENOMEM);
82         }
83         int ret = uv_fs_ftruncate(nullptr, ftruncate_req.get(), fileInfo.fdg->GetFD(), truncateLen, nullptr);
84         if (ret < 0) {
85             HILOGE("Failed to truncate file by fd for libuv error %{public}d", ret);
86             return NError(ret);
87         }
88     }
89     return NError(ERRNO_NOERR);
90 }
91 
Sync(napi_env env,napi_callback_info info)92 napi_value Truncate::Sync(napi_env env, napi_callback_info info)
93 {
94     NFuncArg funcArg(env, info);
95     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
96         HILOGE("Number of arguments unmatched");
97         NError(EINVAL).ThrowErr(env);
98         return nullptr;
99     }
100     auto [succ, fileInfo] = ParseJsFile(env, funcArg[NARG_POS::FIRST]);
101     if (!succ) {
102         NError(EINVAL).ThrowErr(env);
103         return nullptr;
104     }
105     int64_t truncateLen = 0;
106     if (funcArg.GetArgc() == NARG_CNT::TWO) {
107         tie(succ, truncateLen) = NVal(env, funcArg[NARG_POS::SECOND]).ToInt64(truncateLen);
108         if (!succ || truncateLen < 0) {
109             HILOGE("Invalid truncate length");
110             NError(EINVAL).ThrowErr(env);
111             return nullptr;
112         }
113     }
114     auto err = TruncateCore(env, fileInfo, truncateLen);
115     if (err) {
116         err.ThrowErr(env);
117         return nullptr;
118     }
119 
120     return NVal::CreateUndefined(env).val_;
121 }
122 
Async(napi_env env,napi_callback_info info)123 napi_value Truncate::Async(napi_env env, napi_callback_info info)
124 {
125     NFuncArg funcArg(env, info);
126     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::THREE)) {
127         HILOGE("Number of arguments unmatched");
128         NError(EINVAL).ThrowErr(env);
129         return nullptr;
130     }
131     auto [succ, fileInfo] = ParseJsFile(env, funcArg[NARG_POS::FIRST]);
132     if (!succ) {
133         return nullptr;
134     }
135     int64_t truncateLen = 0;
136     if (funcArg.GetArgc() >= NARG_CNT::TWO) {
137         tie(succ, truncateLen) = NVal(env, funcArg[NARG_POS::SECOND]).ToInt64(truncateLen);
138         if (!succ || truncateLen < 0) {
139             HILOGE("Invalid truncate length");
140             NError(EINVAL).ThrowErr(env);
141             return nullptr;
142         }
143     }
144     auto cbExec = [fileInfo = make_shared<FileInfo>(move(fileInfo)), truncateLen, env = env]() -> NError {
145         return TruncateCore(env, *fileInfo, truncateLen);
146     };
147     auto cbCompl = [](napi_env env, NError err) -> NVal {
148         if (err) {
149             return { env, err.GetNapiErr(env) };
150         } else {
151             return NVal::CreateUndefined(env);
152         }
153     };
154     NVal thisVar(env, funcArg.GetThisVar());
155     if (funcArg.GetArgc() == NARG_CNT::ONE || (funcArg.GetArgc() == NARG_CNT::TWO &&
156         !NVal(env, funcArg[NARG_POS::SECOND]).TypeIs(napi_function))) {
157         return NAsyncWorkPromise(env, thisVar).Schedule(PROCEDURE_TRUNCATE_NAME, cbExec, cbCompl).val_;
158     } else {
159         NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::TWO) ? NARG_POS::SECOND : NARG_POS::THIRD)]);
160         return NAsyncWorkCallback(env, thisVar, cb).Schedule(PROCEDURE_TRUNCATE_NAME, cbExec, cbCompl).val_;
161     }
162 }
163 } // namespace OHOS::FileManagement::ModuleFileIO