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 "common_func.h"
17 
18 #include <dirent.h>
19 #include <fcntl.h>
20 #include <sstream>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include <vector>
25 
26 #include "class_stat/stat_entity.h"
27 #include "class_stat/stat_n_exporter.h"
28 #ifndef WIN_PLATFORM
29 #include "class_file/file_entity.h"
30 #include "class_file/file_n_exporter.h"
31 #include "class_stream/stream_entity.h"
32 #include "class_stream/stream_n_exporter.h"
33 #endif
34 #include "filemgmt_libhilog.h"
35 #include "filemgmt_libn.h"
36 #include "file_utils.h"
37 
38 namespace OHOS {
39 namespace FileManagement {
40 namespace ModuleFileIO {
41 using namespace std;
42 using namespace OHOS::FileManagement::LibN;
43 
44 namespace {
45     const std::vector<std::string> PUBLIC_DIR_PATHS = {
46         "/Documents"
47     };
48 }
49 
InitAccessModeType(napi_env env,napi_value exports)50 void InitAccessModeType(napi_env env, napi_value exports)
51 {
52     char propertyName[] = "AccessModeType";
53     napi_property_descriptor desc[] = {
54         DECLARE_NAPI_STATIC_PROPERTY("EXIST", NVal::CreateInt32(env, MODE_EXIST).val_),
55         DECLARE_NAPI_STATIC_PROPERTY("WRITE", NVal::CreateInt32(env, MODE_WRITE).val_),
56         DECLARE_NAPI_STATIC_PROPERTY("READ", NVal::CreateInt32(env, MODE_READ).val_),
57         DECLARE_NAPI_STATIC_PROPERTY("READ_WRITE", NVal::CreateInt32(env, MODE_READ_WRITE).val_),
58     };
59     napi_value obj = nullptr;
60     napi_status status = napi_create_object(env, &obj);
61     if (status != napi_ok) {
62         HILOGE("Failed to create object at initializing openMode");
63         return;
64     }
65     status = napi_define_properties(env, obj, sizeof(desc) / sizeof(desc[0]), desc);
66     if (status != napi_ok) {
67         HILOGE("Failed to set properties of character at initializing openMode");
68         return;
69     }
70     status = napi_set_named_property(env, exports, propertyName, obj);
71     if (status != napi_ok) {
72         HILOGE("Failed to set direction property at initializing openMode");
73         return;
74     }
75 }
76 
InitOpenMode(napi_env env,napi_value exports)77 void InitOpenMode(napi_env env, napi_value exports)
78 {
79     char propertyName[] = "OpenMode";
80     napi_property_descriptor desc[] = {
81         DECLARE_NAPI_STATIC_PROPERTY("READ_ONLY", NVal::CreateInt32(env, USR_READ_ONLY).val_),
82         DECLARE_NAPI_STATIC_PROPERTY("WRITE_ONLY", NVal::CreateInt32(env, USR_WRITE_ONLY).val_),
83         DECLARE_NAPI_STATIC_PROPERTY("READ_WRITE", NVal::CreateInt32(env, USR_RDWR).val_),
84         DECLARE_NAPI_STATIC_PROPERTY("CREATE", NVal::CreateInt32(env, USR_CREATE).val_),
85         DECLARE_NAPI_STATIC_PROPERTY("TRUNC", NVal::CreateInt32(env, USR_TRUNC).val_),
86         DECLARE_NAPI_STATIC_PROPERTY("APPEND", NVal::CreateInt32(env, USR_APPEND).val_),
87         DECLARE_NAPI_STATIC_PROPERTY("NONBLOCK", NVal::CreateInt32(env, USR_NONBLOCK).val_),
88         DECLARE_NAPI_STATIC_PROPERTY("DIR", NVal::CreateInt32(env, USR_DIRECTORY).val_),
89         DECLARE_NAPI_STATIC_PROPERTY("NOFOLLOW", NVal::CreateInt32(env, USR_NOFOLLOW).val_),
90         DECLARE_NAPI_STATIC_PROPERTY("SYNC", NVal::CreateInt32(env, USR_SYNC).val_),
91     };
92     napi_value obj = nullptr;
93     napi_status status = napi_create_object(env, &obj);
94     if (status != napi_ok) {
95         HILOGE("Failed to create object at initializing openMode");
96         return;
97     }
98     status = napi_define_properties(env, obj, sizeof(desc) / sizeof(desc[0]), desc);
99     if (status != napi_ok) {
100         HILOGE("Failed to set properties of character at initializing openMode");
101         return;
102     }
103     status = napi_set_named_property(env, exports, propertyName, obj);
104     if (status != napi_ok) {
105         HILOGE("Failed to set direction property at initializing openMode");
106         return;
107     }
108 }
109 
InitWhenceType(napi_env env,napi_value exports)110 void InitWhenceType(napi_env env, napi_value exports)
111 {
112     char propertyName[] = "WhenceType";
113     napi_property_descriptor desc[] = {
114         DECLARE_NAPI_STATIC_PROPERTY("SEEK_SET", NVal::CreateInt32(env, SEEK_SET).val_),
115         DECLARE_NAPI_STATIC_PROPERTY("SEEK_CUR", NVal::CreateInt32(env, SEEK_CUR).val_),
116         DECLARE_NAPI_STATIC_PROPERTY("SEEK_END", NVal::CreateInt32(env, SEEK_END).val_),
117     };
118     napi_value obj = nullptr;
119     napi_status status = napi_create_object(env, &obj);
120     if (status != napi_ok) {
121         HILOGE("Failed to create object at initializing whenceType");
122         return;
123     }
124     status = napi_define_properties(env, obj, sizeof(desc) / sizeof(desc[0]), desc);
125     if (status != napi_ok) {
126         HILOGE("Failed to set properties of character at initializing whenceType");
127         return;
128     }
129     status = napi_set_named_property(env, exports, propertyName, obj);
130     if (status != napi_ok) {
131         HILOGE("Failed to set direction property at initializing whenceType");
132         return;
133     }
134 }
135 
GetActualLen(napi_env env,size_t bufLen,size_t bufOff,NVal op)136 static tuple<bool, size_t> GetActualLen(napi_env env, size_t bufLen, size_t bufOff, NVal op)
137 {
138     bool succ = false;
139     size_t retLen = bufLen - bufOff;
140 
141     if (op.HasProp("length")) {
142         int64_t opLength = 0;
143 #ifdef WIN_PLATFORM
144         tie(succ, opLength) = op.GetPropValue("length").ToInt64(static_cast<int64_t>(retLen));
145 #else
146         tie(succ, opLength) = op.GetProp("length").ToInt64(static_cast<int64_t>(retLen));
147 #endif
148         if (!succ || opLength < 0 || static_cast<size_t>(opLength) > retLen) {
149             HILOGE("Invalid option.length");
150             NError(EINVAL).ThrowErr(env);
151             return { false, 0 };
152         }
153         retLen = static_cast<size_t>(opLength);
154     }
155     return { true, retLen };
156 }
157 
ConvertJsFlags(unsigned int & flags)158 unsigned int CommonFunc::ConvertJsFlags(unsigned int &flags)
159 {
160     // default value is usrReadOnly 00
161     unsigned int flagsABI = 0;
162     flagsABI |= ((flags & USR_WRITE_ONLY) == USR_WRITE_ONLY) ? WRONLY : 0;
163     flagsABI |= ((flags & USR_RDWR) == USR_RDWR) ? RDWR : 0;
164     flagsABI |= ((flags & USR_CREATE) == USR_CREATE) ? CREATE : 0;
165     flagsABI |= ((flags & USR_TRUNC) == USR_TRUNC) ? TRUNC : 0;
166     flagsABI |= ((flags & USR_APPEND) == USR_APPEND) ? APPEND : 0;
167     flagsABI |= ((flags & USR_NONBLOCK) == USR_NONBLOCK) ? NONBLOCK : 0;
168     flagsABI |= ((flags & USR_DIRECTORY) == USR_DIRECTORY) ? DIRECTORY : 0;
169     flagsABI |= ((flags & USR_NOFOLLOW) == USR_NOFOLLOW) ? NOFOLLOW : 0;
170     flagsABI |= ((flags & USR_SYNC) == USR_SYNC) ? SYNC : 0;
171     flags = flagsABI;
172     return flagsABI;
173 }
174 
175 #if !defined(WIN_PLATFORM) && !defined(IOS_PLATFORM)
InstantiateStat(napi_env env,const uv_stat_t & buf,shared_ptr<FileInfo> fileInfo,bool async)176 NVal CommonFunc::InstantiateStat(napi_env env, const uv_stat_t &buf, shared_ptr<FileInfo> fileInfo, bool async)
177 {
178     napi_value objStat = NClass::InstantiateClass(env, StatNExporter::className_, {});
179     if (!objStat) {
180         HILOGE("Failed to instantiate stat class");
181         if (async) {
182             return {env, NError(EIO).GetNapiErr(env)};
183         }
184         NError(EIO).ThrowErr(env);
185         return NVal();
186     }
187 
188     auto statEntity = NClass::GetEntityOf<StatEntity>(env, objStat);
189     if (!statEntity) {
190         HILOGE("Failed to get stat entity");
191         if (async) {
192             return {env, NError(EIO).GetNapiErr(env)};
193         }
194         NError(EIO).ThrowErr(env);
195         return NVal();
196     }
197 
198     statEntity->stat_ = buf;
199     statEntity->fileInfo_ = fileInfo;
200     return { env, objStat };
201 }
202 #endif
203 
InstantiateStat(napi_env env,const uv_stat_t & buf,bool async)204 NVal CommonFunc::InstantiateStat(napi_env env, const uv_stat_t &buf, bool async)
205 {
206     napi_value objStat = NClass::InstantiateClass(env, StatNExporter::className_, {});
207     if (!objStat) {
208         HILOGE("Failed to instantiate stat class");
209         if (async) {
210             return {env, NError(EIO).GetNapiErr(env)};
211         }
212         NError(EIO).ThrowErr(env);
213         return NVal();
214     }
215 
216     auto statEntity = NClass::GetEntityOf<StatEntity>(env, objStat);
217     if (!statEntity) {
218         HILOGE("Failed to get stat entity");
219         if (async) {
220             return {env, NError(EIO).GetNapiErr(env)};
221         }
222         NError(EIO).ThrowErr(env);
223         return NVal();
224     }
225 
226     statEntity->stat_ = buf;
227     return { env, objStat };
228 }
229 
230 #ifndef WIN_PLATFORM
InstantiateFile(napi_env env,int fd,const string & pathOrUri,bool isUri)231 NVal CommonFunc::InstantiateFile(napi_env env, int fd, const string &pathOrUri, bool isUri)
232 {
233     napi_value objFile = NClass::InstantiateClass(env, FileNExporter::className_, {});
234     if (!objFile) {
235         HILOGE("Failed to instantiate class");
236         NError(EIO).ThrowErr(env);
237         close(fd);
238         return NVal();
239     }
240 
241     auto fileEntity = NClass::GetEntityOf<FileEntity>(env, objFile);
242     if (!fileEntity) {
243         HILOGE("Failed to get fileEntity");
244         NError(EIO).ThrowErr(env);
245         close(fd);
246         return NVal();
247     }
248     auto fdg = CreateUniquePtr<DistributedFS::FDGuard>(fd, false);
249     if (fdg == nullptr) {
250         HILOGE("Failed to request heap memory.");
251         close(fd);
252         NError(ENOMEM).ThrowErr(env);
253         return NVal();
254     }
255     fileEntity->fd_.swap(fdg);
256     if (isUri) {
257         fileEntity->path_ = "";
258         fileEntity->uri_ = pathOrUri;
259     } else {
260         fileEntity->path_ = pathOrUri;
261         fileEntity->uri_ = "";
262     }
263     return { env, objFile };
264 }
265 
InstantiateStream(napi_env env,shared_ptr<FILE> fp,bool async)266 NVal CommonFunc::InstantiateStream(napi_env env, shared_ptr<FILE> fp, bool async)
267 {
268     napi_value objStream = NClass::InstantiateClass(env, StreamNExporter::className_, {});
269     if (!objStream) {
270         HILOGE("INNER BUG. Cannot instantiate stream");
271         if (async) {
272             return {env, NError(EIO).GetNapiErr(env)};
273         }
274         NError(EIO).ThrowErr(env);
275         return NVal();
276     }
277 
278     auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, objStream);
279     if (!streamEntity) {
280         HILOGE("Cannot instantiate stream because of void entity");
281         if (async) {
282             return {env, NError(EIO).GetNapiErr(env)};
283         }
284         NError(EIO).ThrowErr(env);
285         return NVal();
286     }
287     streamEntity->fp.swap(fp);
288     return { env, objStream };
289 }
290 #endif
291 
fs_req_cleanup(uv_fs_t * req)292 void CommonFunc::fs_req_cleanup(uv_fs_t* req)
293 {
294     uv_fs_req_cleanup(req);
295     if (req) {
296         delete req;
297         req = nullptr;
298     }
299 }
300 
GetModeFromFlags(unsigned int flags)301 string CommonFunc::GetModeFromFlags(unsigned int flags)
302 {
303     const string readMode = "r";
304     const string writeMode = "w";
305     const string appendMode = "a";
306     const string truncMode = "t";
307     string mode = readMode;
308     mode += (((flags & O_RDWR) == O_RDWR) ? writeMode : "");
309     mode = (((flags & O_WRONLY) == O_WRONLY) ? writeMode : mode);
310     if (mode != readMode) {
311         mode += ((flags & O_TRUNC) ? truncMode : "");
312         mode += ((flags & O_APPEND) ? appendMode : "");
313     }
314     return mode;
315 }
316 
CheckPublicDirPath(const std::string & sandboxPath)317 bool CommonFunc::CheckPublicDirPath(const std::string &sandboxPath)
318 {
319     for (const std::string &path : PUBLIC_DIR_PATHS) {
320         if (sandboxPath.find(path) == 0) {
321             return true;
322         }
323     }
324     return false;
325 }
326 
Decode(const std::string & uri)327 string CommonFunc::Decode(const std::string &uri)
328 {
329     std::ostringstream outPutStream;
330     const int32_t encodeLen = 2;
331     size_t index = 0;
332     while (index < uri.length()) {
333         if (uri[index] == '%') {
334             int hex = 0;
335             std::istringstream inputStream(uri.substr(index + 1, encodeLen));
336             inputStream >> std::hex >> hex;
337             outPutStream << static_cast<char>(hex);
338             index += encodeLen + 1;
339         } else {
340             outPutStream << uri[index];
341             index++;
342         }
343     }
344 
345     return outPutStream.str();
346 }
347 
GetCopyPathArg(napi_env env,napi_value srcPath,napi_value dstPath)348 tuple<bool, unique_ptr<char[]>, unique_ptr<char[]>> CommonFunc::GetCopyPathArg(napi_env env,
349                                                                                napi_value srcPath,
350                                                                                napi_value dstPath)
351 {
352     bool succ = false;
353     unique_ptr<char[]> src = nullptr;
354     tie(succ, src, ignore) = NVal(env, srcPath).ToUTF8StringPath();
355     if (!succ) {
356         HILOGE("Failed to convert the src path to UTF-8 string");
357         return { false, nullptr, nullptr };
358     }
359 
360     unique_ptr<char[]> dest = nullptr;
361     tie(succ, dest, ignore) = NVal(env, dstPath).ToUTF8StringPath();
362     if (!succ) {
363         HILOGE("Failed to convert the dest path to UTF-8 string");
364         return { false, nullptr, nullptr };
365     }
366     return make_tuple(true, move(src), move(dest));
367 }
368 
DecodeString(napi_env env,NVal jsStr,NVal encoding)369 static tuple<bool, unique_ptr<char[]>, size_t> DecodeString(napi_env env, NVal jsStr, NVal encoding)
370 {
371     if (!jsStr.TypeIs(napi_string)) {
372         return { false, nullptr, 0 };
373     }
374     if (!encoding) {
375         return jsStr.ToUTF8String();
376     }
377 
378     bool succ = false;
379     unique_ptr<char[]> encodingBuf = nullptr;
380     tie(succ, encodingBuf, ignore) = encoding.ToUTF8String("utf-8");
381     if (!succ) {
382         HILOGE("Failed to convert encoding to UTF8");
383         return { false, nullptr, 0 };
384     }
385 
386     string_view encodingStr(encodingBuf.get());
387     if (encodingStr == "utf-8") {
388         return jsStr.ToUTF8String();
389     } else if (encodingStr == "utf-16") {
390         return jsStr.ToUTF16String();
391     } else {
392         HILOGE("Failed to recognize the str type");
393         return { false, nullptr, 0 };
394     }
395 }
396 
GetReadArg(napi_env env,napi_value readBuf,napi_value option)397 tuple<bool, void *, size_t, int64_t> CommonFunc::GetReadArg(napi_env env,
398     napi_value readBuf, napi_value option)
399 {
400     size_t retLen = 0;
401     int64_t offset = -1;
402     bool succ = false;
403 
404     NVal txt(env, readBuf);
405     void *buf = nullptr;
406     size_t bufLen = 0;
407     tie(succ, buf, bufLen) = txt.ToArraybuffer();
408     if (!succ || bufLen > UINT_MAX) {
409         HILOGE("Invalid arraybuffer");
410         NError(EINVAL).ThrowErr(env);
411         return { false, nullptr, retLen, offset };
412     }
413     NVal op = NVal(env, option);
414     tie(succ, retLen) = GetActualLen(env, bufLen, 0, op);
415     if (!succ) {
416         HILOGE("Failed to get actual length");
417         return { false, nullptr, retLen, offset };
418     }
419 #ifdef WIN_PLATFORM
420     if (op.HasProp("offset") && !op.GetPropValue("offset").TypeIs(napi_undefined)) {
421         tie(succ, offset) = op.GetPropValue("offset").ToInt64();
422 #else
423     if (op.HasProp("offset") && !op.GetProp("offset").TypeIs(napi_undefined)) {
424         tie(succ, offset) = op.GetProp("offset").ToInt64();
425 #endif
426         if (!succ || offset < 0) {
427             HILOGE("option.offset shall be positive number");
428             NError(EINVAL).ThrowErr(env);
429             return { false, nullptr, retLen, offset };
430         }
431     }
432     return { true, buf, retLen, offset };
433 }
434 
435 tuple<bool, unique_ptr<char[]>, void *, size_t, int64_t> CommonFunc::GetWriteArg(napi_env env,
436     napi_value argWBuf, napi_value argOption)
437 {
438     size_t bufLen = 0;
439     int64_t offset = -1;
440     bool succ = false;
441     void *buf = nullptr;
442     NVal op(env, argOption);
443     NVal jsBuffer(env, argWBuf);
444     unique_ptr<char[]> bufferGuard = nullptr;
445 #ifdef WIN_PLATFORM
446     tie(succ, bufferGuard, bufLen) = DecodeString(env, jsBuffer, op.GetPropValue("encoding"));
447 #else
448     tie(succ, bufferGuard, bufLen) = DecodeString(env, jsBuffer, op.GetProp("encoding"));
449 #endif
450     if (!succ) {
451         tie(succ, buf, bufLen) = NVal(env, argWBuf).ToArraybuffer();
452         if (!succ) {
453             HILOGE("Illegal write buffer or encoding");
454             NError(EINVAL).ThrowErr(env);
455             return { false, nullptr, nullptr, 0, offset };
456         }
457     } else {
458         buf = bufferGuard.get();
459     }
460     if (bufLen > UINT_MAX) {
461         HILOGE("The Size of buffer is too large");
462         NError(EINVAL).ThrowErr(env);
463         return { false, nullptr, nullptr, 0, offset } ;
464     }
465     size_t retLen = 0;
466     tie(succ, retLen) = GetActualLen(env, bufLen, 0, op);
467     if (!succ) {
468         HILOGE("Failed to get actual length");
469         return { false, nullptr, nullptr, 0, offset };
470     }
471 
472 #ifdef WIN_PLATFORM
473     if (op.HasProp("offset") && !op.GetPropValue("offset").TypeIs(napi_undefined)) {
474         tie(succ, offset) = op.GetPropValue("offset").ToInt64();
475 #else
476     if (op.HasProp("offset") && !op.GetProp("offset").TypeIs(napi_undefined)) {
477         tie(succ, offset) = op.GetProp("offset").ToInt64();
478 #endif
479         if (!succ || offset < 0) {
480             HILOGE("option.offset shall be positive number");
481             NError(EINVAL).ThrowErr(env);
482             return { false, nullptr, nullptr, 0, offset };
483         }
484     }
485     return { true, move(bufferGuard), buf, retLen, offset };
486 }
487 } // namespace ModuleFileIO
488 } // namespace FileManagement
489 } // namespace OHOS
490