• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 "copy_file.h"
17 
18 #include <cstring>
19 #include <fcntl.h>
20 #include <sys/sendfile.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <tuple>
24 #include <unistd.h>
25 
26 #include "../../common/file_helper/fd_guard.h"
27 #include "../../common/napi/n_async/n_async_work_callback.h"
28 #include "../../common/napi/n_async/n_async_work_promise.h"
29 #include "../../common/napi/n_func_arg.h"
30 
31 namespace OHOS {
32 namespace DistributedFS {
33 namespace ModuleFileIO {
34 using namespace std;
35 
36 struct FileInfo {
37     bool isPath = false;
38     unique_ptr<char[]> path;
39     FDGuard fdg;
40 };
41 
CopyFileCore(FileInfo & srcFile,FileInfo & destFile)42 static UniError CopyFileCore(FileInfo &srcFile, FileInfo &destFile)
43 {
44     if (srcFile.isPath) {
45         int ret = open(srcFile.path.get(), O_RDONLY);
46         if (ret < 0) {
47             return UniError(errno);
48         }
49         srcFile.fdg.SetFD(ret, true);
50     }
51     struct stat statbf;
52     if (fstat(srcFile.fdg.GetFD(), &statbf) == -1) {
53         return UniError(errno);
54     }
55 
56     if (destFile.isPath) {
57         int ret = open(destFile.path.get(), O_WRONLY | O_CREAT, statbf.st_mode);
58         if (ret < 0) {
59             return UniError(errno);
60         }
61         destFile.fdg.SetFD(ret, true);
62     }
63 
64     int block = 4096;
65     auto copyBuf = make_unique<char[]>(block);
66     do {
67         ssize_t readSize = read(srcFile.fdg.GetFD(), copyBuf.get(), block);
68         if (readSize == -1) {
69             return UniError(errno);
70         } else if (readSize == 0) {
71             break;
72         }
73         ssize_t writeSize = write(destFile.fdg.GetFD(), copyBuf.get(), readSize);
74         if (writeSize != readSize) {
75             return UniError(errno);
76         }
77         if (readSize != block) {
78             break;
79         }
80     } while (true);
81 
82     return UniError(ERRNO_NOERR);
83 }
84 
ParseJsModeAndProm(napi_env env,const NFuncArg & funcArg)85 static tuple<bool, int, bool> ParseJsModeAndProm(napi_env env, const NFuncArg &funcArg)
86 {
87     bool succ = false;
88     bool promise = false;
89     bool hasMode = false;
90     int mode = 0;
91     if (funcArg.GetArgc() == NARG_CNT::THREE && NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_number)) {
92         promise = true;
93         hasMode = true;
94     } else if (funcArg.GetArgc() == NARG_CNT::FOUR) {
95         hasMode = true;
96     }
97     if (hasMode) {
98         tie(succ, mode) = NVal(env, funcArg[NARG_POS::THIRD]).ToInt32();
99         if (!succ) {
100             return {false, mode, promise};
101         }
102     }
103     return {true, mode, promise};
104 }
105 
ParseJsOperand(napi_env env,NVal pathOrFdFromJsArg)106 static tuple<bool, FileInfo> ParseJsOperand(napi_env env, NVal pathOrFdFromJsArg)
107 {
108     auto [isPath, path, ignore] = pathOrFdFromJsArg.ToUTF8String();
109     if (isPath) {
110         return {true, FileInfo{true, move(path), {}}};
111     }
112 
113     auto [isFd, fd] = pathOrFdFromJsArg.ToInt32();
114     if (isFd) {
115         return {true, FileInfo{false, {}, {fd, false}}};
116     }
117 
118     return {false, FileInfo{false, {}, {}}};
119 };
120 
Sync(napi_env env,napi_callback_info info)121 napi_value CopyFile::Sync(napi_env env, napi_callback_info info)
122 {
123     NFuncArg funcArg(env, info);
124     if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::THREE)) {
125         UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
126         return nullptr;
127     }
128 
129     auto [succSrc, src] = ParseJsOperand(env, {env, funcArg[NARG_POS::FIRST]});
130     auto [succDest, dest] = ParseJsOperand(env, {env, funcArg[NARG_POS::SECOND]});
131     if (!succSrc || !succDest) {
132         UniError(EINVAL).ThrowErr(env, "The first/second argument requires filepath/fd");
133         return nullptr;
134     }
135 
136     auto [succMode, mode, ignore] = ParseJsModeAndProm(env, funcArg);
137     if (!succMode) {
138         UniError(EINVAL).ThrowErr(env, "Invalid mode");
139         return nullptr;
140     }
141 
142     auto err = CopyFileCore(src, dest);
143     if (err) {
144         if (err.GetErrno(ERR_CODE_SYSTEM_POSIX) == ENAMETOOLONG) {
145             UniError(EINVAL).ThrowErr(env, "Filename too long");
146             return nullptr;
147         }
148 
149         err.ThrowErr(env);
150         return nullptr;
151     }
152 
153     return NVal::CreateUndefined(env).val_;
154 }
155 
156 class Para {
157 public:
158     FileInfo src_;
159     FileInfo dest_;
160 
Para(FileInfo src,FileInfo dest)161     Para(FileInfo src, FileInfo dest) : src_(move(src)), dest_(move(dest)) {};
162 };
163 
Async(napi_env env,napi_callback_info info)164 napi_value CopyFile::Async(napi_env env, napi_callback_info info)
165 {
166     NFuncArg funcArg(env, info);
167     if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::FOUR)) {
168         UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
169         return nullptr;
170     }
171 
172     auto [succSrc, src] = ParseJsOperand(env, {env, funcArg[NARG_POS::FIRST]});
173     auto [succDest, dest] = ParseJsOperand(env, {env, funcArg[NARG_POS::SECOND]});
174     if (!succSrc || !succDest) {
175         UniError(EINVAL).ThrowErr(env, "The first/second argument requires filepath/fd");
176         return nullptr;
177     }
178 
179     auto [succMode, mode, promise] = ParseJsModeAndProm(env, funcArg);
180     if (!succMode) {
181         UniError(EINVAL).ThrowErr(env, "Invalid mode");
182         return nullptr;
183     }
184 
185     auto cbExec = [para = make_shared<Para>(move(src), move(dest))](napi_env env) -> UniError {
186         return CopyFileCore(para->src_, para->dest_);
187     };
188 
189     auto cbCompl = [](napi_env env, UniError err) -> NVal {
190         if (err) {
191             if (err.GetErrno(ERR_CODE_SYSTEM_POSIX) == ENAMETOOLONG) {
192                 return {env, err.GetNapiErr(env, "Filename too long")};
193             }
194             return {env, err.GetNapiErr(env)};
195         }
196         return {NVal::CreateUndefined(env)};
197     };
198 
199     string procedureName = "FileIOCopyFile";
200     NVal thisVar(env, funcArg.GetThisVar());
201     if (funcArg.GetArgc() == NARG_CNT::TWO || promise) {
202         return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbCompl).val_;
203     } else {
204         NVal cb(env, funcArg[((funcArg.GetArgc() == NARG_CNT::THREE) ? NARG_POS::THIRD : NARG_POS::FOURTH)]);
205         return NAsyncWorkCallback(env, thisVar, cb).Schedule(procedureName, cbExec, cbCompl).val_;
206     }
207 }
208 } // namespace ModuleFileIO
209 } // namespace DistributedFS
210 } // namespace OHOS
211