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