1df226684Sopenharmony_ci/*
2df226684Sopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
3df226684Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4df226684Sopenharmony_ci * you may not use this file except in compliance with the License.
5df226684Sopenharmony_ci * You may obtain a copy of the License at
6df226684Sopenharmony_ci *
7df226684Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8df226684Sopenharmony_ci *
9df226684Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10df226684Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11df226684Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12df226684Sopenharmony_ci * See the License for the specific language governing permissions and
13df226684Sopenharmony_ci * limitations under the License.
14df226684Sopenharmony_ci */
15df226684Sopenharmony_ci
16df226684Sopenharmony_ciimport UIAbility from '@ohos.app.ability.UIAbility';
17df226684Sopenharmony_ciimport picker from '@ohos.file.picker';
18df226684Sopenharmony_ciimport Want from '@ohos.app.ability.Want';
19df226684Sopenharmony_ciimport AbilityConstant from '@ohos.app.ability.AbilityConstant';
20df226684Sopenharmony_ciimport window from '@ohos.window';
21df226684Sopenharmony_ciimport { BusinessError } from '@ohos.base';
22df226684Sopenharmony_ciimport ability from '@ohos.ability.ability';
23df226684Sopenharmony_ciimport dlpPermission from '@ohos.dlpPermission';
24df226684Sopenharmony_ciimport fs from '@ohos.file.fs';
25df226684Sopenharmony_ciimport fileUri from '@ohos.file.fileuri';
26df226684Sopenharmony_ciimport abilityManager from '@ohos.app.ability.abilityManager';
27df226684Sopenharmony_ciimport { getFileUriByPath, getFileFd, getAppId, isValidPath, defaultDlpFile } from '../common/utils';
28df226684Sopenharmony_ciimport Constants from '../common/constant';
29df226684Sopenharmony_ciimport GlobalContext from '../common/GlobalContext';
30df226684Sopenharmony_ciimport zlib from '@ohos.zlib';
31df226684Sopenharmony_ciimport { GetAlertMessage } from '../common/GetAlertMessage';
32df226684Sopenharmony_ciimport { HiLog } from '../common/HiLog';
33df226684Sopenharmony_ciimport dialogRequest from '@ohos.app.ability.dialogRequest';
34df226684Sopenharmony_ci
35df226684Sopenharmony_ciconst TAG = 'SaveAs';
36df226684Sopenharmony_ci
37df226684Sopenharmony_ciclass ChangeOption {
38df226684Sopenharmony_ci  public offset: number = 0
39df226684Sopenharmony_ci  public length: number = 0
40df226684Sopenharmony_ci}
41df226684Sopenharmony_ci
42df226684Sopenharmony_ciconst SUFFIX_INDEX = 2;
43df226684Sopenharmony_ciconst HEAD_LENGTH_IN_BYTE = 20;
44df226684Sopenharmony_ciconst HEAD_LENGTH_IN_U32 = 5;
45df226684Sopenharmony_ciconst TXT_OFFSET = 3;
46df226684Sopenharmony_ciconst SIZE_OFFSET = 4;
47df226684Sopenharmony_ciconst ARGS_ZERO = 0;
48df226684Sopenharmony_ciconst ARGS_ONE = 1;
49df226684Sopenharmony_ciconst ARGS_TWO = 2;
50df226684Sopenharmony_ciconst FILENAME_MAX_LEN = 255;
51df226684Sopenharmony_ciconst FOPEN_EXCEPTION_IS_DIR = 13900019;
52df226684Sopenharmony_ciconst SRC_URI_OFFSET = 4;
53df226684Sopenharmony_ciconst ACTION: Record<string, string> = {
54df226684Sopenharmony_ci  'SELECT_ACTION': 'ohos.want.action.OPEN_FILE',
55df226684Sopenharmony_ci  'SELECT_ACTION_MODAL': 'ohos.want.action.OPEN_FILE_SERVICE',
56df226684Sopenharmony_ci  'SAVE_ACTION': 'ohos.want.action.CREATE_FILE',
57df226684Sopenharmony_ci  'SAVE_ACTION_MODAL': 'ohos.want.action.CREATE_FILE_SERVICE',
58df226684Sopenharmony_ci};
59df226684Sopenharmony_ci
60df226684Sopenharmony_ciconst errCode: Record<string, number> = {
61df226684Sopenharmony_ci  'INVALID_ARGS': 13900020,
62df226684Sopenharmony_ci  'RESULT_ERROR': 13900042,
63df226684Sopenharmony_ci  'NAME_TOO_LONG': 13900030,
64df226684Sopenharmony_ci};
65df226684Sopenharmony_ci
66df226684Sopenharmony_ciconst ERRCODE_MAP = new Map([
67df226684Sopenharmony_ci  [errCode.INVALID_ARGS, 'Invalid argument'],
68df226684Sopenharmony_ci  [errCode.RESULT_ERROR, 'Unknown error'],
69df226684Sopenharmony_ci  [errCode.NAME_TOO_LONG, 'File name too long'],
70df226684Sopenharmony_ci]);
71df226684Sopenharmony_ciexport default class SaveAsAbility extends UIAbility {
72df226684Sopenharmony_ci  private result: ability.AbilityResult = {
73df226684Sopenharmony_ci    resultCode: -1,
74df226684Sopenharmony_ci    want: {
75df226684Sopenharmony_ci      bundleName: '',
76df226684Sopenharmony_ci      abilityName: '',
77df226684Sopenharmony_ci      parameters: {
78df226684Sopenharmony_ci        pick_path_return: [],
79df226684Sopenharmony_ci        pick_fd_return: 0
80df226684Sopenharmony_ci      }
81df226684Sopenharmony_ci    }
82df226684Sopenharmony_ci  };
83df226684Sopenharmony_ci  private dlpFile: dlpPermission.DLPFile = defaultDlpFile;
84df226684Sopenharmony_ci  private sandboxBundleName: string = '';
85df226684Sopenharmony_ci  private resultUri: string = '';
86df226684Sopenharmony_ci  private tokenId: number = -1;
87df226684Sopenharmony_ci  private requestCode: number = -1;
88df226684Sopenharmony_ci  private fileName: string = '';
89df226684Sopenharmony_ci  private suffix: string = '';
90df226684Sopenharmony_ci  private authPerm: dlpPermission.DLPFileAccess = dlpPermission.DLPFileAccess.READ_ONLY;
91df226684Sopenharmony_ci  private isOK: boolean = true; // use with startAlertAbility
92df226684Sopenharmony_ci  private zipFlag: boolean = false;
93df226684Sopenharmony_ci  private random: string = String(Math.random()).substring(Constants.RAND_START, Constants.RAND_END);
94df226684Sopenharmony_ci  private filePath: string = '';
95df226684Sopenharmony_ci  private dirPath: string = '';
96df226684Sopenharmony_ci  private encFile: string = '';
97df226684Sopenharmony_ci  private zipPath: string = '';
98df226684Sopenharmony_ci  private ff: fs.File | undefined = undefined;
99df226684Sopenharmony_ci  private zf2: fs.File | undefined = undefined;
100df226684Sopenharmony_ci  private zipName: string = '';
101df226684Sopenharmony_ci  private file: fs.File | undefined = undefined;
102df226684Sopenharmony_ci  private dstFdPicker: number = -1;
103df226684Sopenharmony_ci  private newDlpFile: dlpPermission.DLPFile = defaultDlpFile;
104df226684Sopenharmony_ci  private srcFdPicker: number = -1;
105df226684Sopenharmony_ci  private linkFileName: string = '';
106df226684Sopenharmony_ci
107df226684Sopenharmony_ci  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
108df226684Sopenharmony_ci    HiLog.info(TAG, `onCreate`);
109df226684Sopenharmony_ci    await this.prepareDlpFile(want);
110df226684Sopenharmony_ci    try {
111df226684Sopenharmony_ci      await abilityManager.notifySaveAsResult(this.result, this.requestCode);
112df226684Sopenharmony_ci    } catch (err) {
113df226684Sopenharmony_ci      HiLog.error(TAG, `notifySaveAsResult failed: ${JSON.stringify(err)}`);
114df226684Sopenharmony_ci    }
115df226684Sopenharmony_ci    if (this.isOK) {
116df226684Sopenharmony_ci      this.context.terminateSelf();
117df226684Sopenharmony_ci    }
118df226684Sopenharmony_ci  }
119df226684Sopenharmony_ci
120df226684Sopenharmony_ci  onDestroy(): void {
121df226684Sopenharmony_ci    HiLog.info(TAG, `onDestroy`);
122df226684Sopenharmony_ci  }
123df226684Sopenharmony_ci
124df226684Sopenharmony_ci  async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
125df226684Sopenharmony_ci    // Main window is created, set main page for this ability
126df226684Sopenharmony_ci    HiLog.info(TAG, `onWindowStageCreate`);
127df226684Sopenharmony_ci  }
128df226684Sopenharmony_ci
129df226684Sopenharmony_ci  onWindowStageDestroy(): void {
130df226684Sopenharmony_ci    // Main window is destroyed, release UI related resources
131df226684Sopenharmony_ci    HiLog.info(TAG, `onWindowStageDestroy`);
132df226684Sopenharmony_ci  }
133df226684Sopenharmony_ci
134df226684Sopenharmony_ci  onForeground(): void {
135df226684Sopenharmony_ci    // Ability has brought to foreground
136df226684Sopenharmony_ci    HiLog.info(TAG, `onForeground`);
137df226684Sopenharmony_ci  }
138df226684Sopenharmony_ci
139df226684Sopenharmony_ci  onBackground(): void {
140df226684Sopenharmony_ci    // Ability has back to background
141df226684Sopenharmony_ci    HiLog.info(TAG, `onBackground`);
142df226684Sopenharmony_ci  }
143df226684Sopenharmony_ci
144df226684Sopenharmony_ci  async parseParams(want: Want): Promise<boolean | void> {
145df226684Sopenharmony_ci    if (want.parameters === undefined) {
146df226684Sopenharmony_ci      HiLog.error(TAG, `invalid want`);
147df226684Sopenharmony_ci      return false;
148df226684Sopenharmony_ci    }
149df226684Sopenharmony_ci
150df226684Sopenharmony_ci    this.requestCode = want.parameters?.['requestCode'] as number;
151df226684Sopenharmony_ci    if (this.requestCode === undefined) {
152df226684Sopenharmony_ci      HiLog.error(TAG, `invalid requestCode`);
153df226684Sopenharmony_ci      return false;
154df226684Sopenharmony_ci    }
155df226684Sopenharmony_ci
156df226684Sopenharmony_ci    this.tokenId = want.parameters?.['ohos.aafwk.param.callerToken'] as number;
157df226684Sopenharmony_ci    if (this.tokenId === undefined) {
158df226684Sopenharmony_ci      HiLog.error(TAG, `invalid tokenId`);
159df226684Sopenharmony_ci      return false;
160df226684Sopenharmony_ci    }
161df226684Sopenharmony_ci    try {
162df226684Sopenharmony_ci      await this.checkParseParams(want);
163df226684Sopenharmony_ci      return true;
164df226684Sopenharmony_ci    } catch {
165df226684Sopenharmony_ci      return false;
166df226684Sopenharmony_ci    }
167df226684Sopenharmony_ci  }
168df226684Sopenharmony_ci
169df226684Sopenharmony_ci  checkParseParams(want: Want): Promise<void> {
170df226684Sopenharmony_ci    return new Promise(async (resolve, reject) => {
171df226684Sopenharmony_ci      this.authPerm = (GlobalContext.load('token2File') as
172df226684Sopenharmony_ci      Map<number, (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[]>)
173df226684Sopenharmony_ci        .get(this.tokenId)?.[3] as dlpPermission.DLPFileAccess;
174df226684Sopenharmony_ci      let dlpFileInfo = (GlobalContext.load('token2File') as
175df226684Sopenharmony_ci      Map<number, (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[]>)
176df226684Sopenharmony_ci        .get(this.tokenId)?.[0] as dlpPermission.DLPFile;
177df226684Sopenharmony_ci      let contactAccount = dlpFileInfo.dlpProperty.contactAccount as string;
178df226684Sopenharmony_ci      if (this.authPerm != dlpPermission.DLPFileAccess.CONTENT_EDIT &&
179df226684Sopenharmony_ci        this.authPerm != dlpPermission.DLPFileAccess.FULL_CONTROL) {
180df226684Sopenharmony_ci        HiLog.error(TAG, `invalid authPerm: ${this.authPerm}`);
181df226684Sopenharmony_ci        this.isOK = false;
182df226684Sopenharmony_ci        AppStorage.setOrCreate('contactAccount', contactAccount);
183df226684Sopenharmony_ci        let accountFlag = await GetAlertMessage.checkAccountInfo(contactAccount);
184df226684Sopenharmony_ci        if (!accountFlag) {
185df226684Sopenharmony_ci          await GetAlertMessage.startAlertAbility(this.context, {
186df226684Sopenharmony_ci            code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
187df226684Sopenharmony_ci          reject();
188df226684Sopenharmony_ci        }
189df226684Sopenharmony_ci        await GetAlertMessage.startAlertAbility(this.context,
190df226684Sopenharmony_ci          { code: Constants.ERR_JS_DLP_FILE_READ_ONLY } as BusinessError);
191df226684Sopenharmony_ci        reject();
192df226684Sopenharmony_ci      }
193df226684Sopenharmony_ci      if (!(GlobalContext.load('token2File') as Map<number, Object[]>).has(this.tokenId)) {
194df226684Sopenharmony_ci        HiLog.error(TAG, `invalid token2File`);
195df226684Sopenharmony_ci        reject();
196df226684Sopenharmony_ci      }
197df226684Sopenharmony_ci      let newFileName = want.parameters?.['key_pick_file_name'] as [];
198df226684Sopenharmony_ci      if (newFileName === undefined) {
199df226684Sopenharmony_ci        HiLog.error(TAG, `invalid newFileName`);
200df226684Sopenharmony_ci        reject();
201df226684Sopenharmony_ci      }
202df226684Sopenharmony_ci      this.fileName = newFileName.join('');
203df226684Sopenharmony_ci      let splitNames = this.fileName.split('.');
204df226684Sopenharmony_ci      HiLog.info(TAG, `splitNames: ${splitNames}`);
205df226684Sopenharmony_ci      if (splitNames.length <= SUFFIX_INDEX) {
206df226684Sopenharmony_ci        HiLog.error(TAG, `get suffix failed`);
207df226684Sopenharmony_ci        reject();
208df226684Sopenharmony_ci      }
209df226684Sopenharmony_ci      this.suffix = splitNames[splitNames.length - SUFFIX_INDEX];
210df226684Sopenharmony_ci      HiLog.info(TAG, `suffix is: ${this.suffix}`);
211df226684Sopenharmony_ci      resolve();
212df226684Sopenharmony_ci    })
213df226684Sopenharmony_ci  }
214df226684Sopenharmony_ci
215df226684Sopenharmony_ci  deleteFile(file:string) {
216df226684Sopenharmony_ci    try {
217df226684Sopenharmony_ci      let res = fs.accessSync(file);
218df226684Sopenharmony_ci      if (res) {
219df226684Sopenharmony_ci        fs.unlinkSync(file);
220df226684Sopenharmony_ci      }
221df226684Sopenharmony_ci    } catch (err) {
222df226684Sopenharmony_ci      HiLog.error(TAG, `deleteFile: ${JSON.stringify(err)}`);
223df226684Sopenharmony_ci    }
224df226684Sopenharmony_ci  }
225df226684Sopenharmony_ci
226df226684Sopenharmony_ci  rmDir(file:string) {
227df226684Sopenharmony_ci    try {
228df226684Sopenharmony_ci      let res = fs.accessSync(file);
229df226684Sopenharmony_ci      if (res) {
230df226684Sopenharmony_ci        fs.rmdirSync(file);
231df226684Sopenharmony_ci      }
232df226684Sopenharmony_ci    } catch (err) {
233df226684Sopenharmony_ci      HiLog.error(TAG, `rmdirSync: ${JSON.stringify(err)}`);
234df226684Sopenharmony_ci    }
235df226684Sopenharmony_ci  }
236df226684Sopenharmony_ci
237df226684Sopenharmony_ci  async copyDlpHead(srcFd: number, dstFd: number) {
238df226684Sopenharmony_ci    return new Promise<boolean>(async (resolve, reject) => {
239df226684Sopenharmony_ci      let appDir = this.context.filesDir + '/';
240df226684Sopenharmony_ci      this.filePath = appDir + 'saveAs' + this.random;
241df226684Sopenharmony_ci      this.dirPath = appDir + 'saveAsUnzip' + this.random;
242df226684Sopenharmony_ci      this.encFile = this.dirPath + '/encrypted_data';
243df226684Sopenharmony_ci      this.zipPath = appDir + 'saveAsZip' + this.random;
244df226684Sopenharmony_ci      this.zipName = this.zipPath + '.zip';
245df226684Sopenharmony_ci      try {
246df226684Sopenharmony_ci        let success = await this.copyDlpHeadData(srcFd, dstFd);
247df226684Sopenharmony_ci        resolve(success);
248df226684Sopenharmony_ci      } catch (err) {
249df226684Sopenharmony_ci        HiLog.error(TAG, `copyDlpHead: ${JSON.stringify(err)}, message: ${JSON.stringify(err.message)}`);
250df226684Sopenharmony_ci        try {
251df226684Sopenharmony_ci          await fs.close(this.ff);
252df226684Sopenharmony_ci          await fs.close(this.zf2);
253df226684Sopenharmony_ci        } catch (error) {
254df226684Sopenharmony_ci          HiLog.error(TAG, `close: ${JSON.stringify(error)}`);
255df226684Sopenharmony_ci        }
256df226684Sopenharmony_ci        resolve(false);
257df226684Sopenharmony_ci      }
258df226684Sopenharmony_ci      if (this.zipFlag) {
259df226684Sopenharmony_ci        this.deleteFile(this.dirPath + '/encrypted_data');
260df226684Sopenharmony_ci        this.deleteFile(this.dirPath + '/dlp_general_info');
261df226684Sopenharmony_ci        this.deleteFile(this.dirPath + '/dlp_cert');
262df226684Sopenharmony_ci        this.rmDir(this.dirPath);
263df226684Sopenharmony_ci        this.deleteFile(this.filePath);
264df226684Sopenharmony_ci        this.deleteFile(this.zipName);
265df226684Sopenharmony_ci      }
266df226684Sopenharmony_ci    })
267df226684Sopenharmony_ci  }
268df226684Sopenharmony_ci
269df226684Sopenharmony_ci  async copyDlpHeadData(srcFd: number, dstFd: number) {
270df226684Sopenharmony_ci    return new Promise<boolean>(async (resolve, reject) => {
271df226684Sopenharmony_ci      let z = new ArrayBuffer(HEAD_LENGTH_IN_BYTE);
272df226684Sopenharmony_ci      let option: ChangeOption = {
273df226684Sopenharmony_ci        offset: 0,
274df226684Sopenharmony_ci        length: HEAD_LENGTH_IN_BYTE
275df226684Sopenharmony_ci      };
276df226684Sopenharmony_ci      let num = fs.readSync(srcFd, z, option);
277df226684Sopenharmony_ci      let buf = new Uint32Array(z, 0, HEAD_LENGTH_IN_U32);
278df226684Sopenharmony_ci      let magic = buf[0];
279df226684Sopenharmony_ci
280df226684Sopenharmony_ci      if (magic !== Constants.DLP_ZIP_MAGIC) {
281df226684Sopenharmony_ci        let txtOffset = buf[TXT_OFFSET];
282df226684Sopenharmony_ci        let head = new ArrayBuffer(txtOffset);
283df226684Sopenharmony_ci        option = {
284df226684Sopenharmony_ci          offset: 0,
285df226684Sopenharmony_ci          length: txtOffset
286df226684Sopenharmony_ci        };
287df226684Sopenharmony_ci        num = fs.readSync(srcFd, head, option);
288df226684Sopenharmony_ci        let buf2 = new Uint32Array(head, 0, HEAD_LENGTH_IN_U32);
289df226684Sopenharmony_ci        buf2[SIZE_OFFSET] = 0;
290df226684Sopenharmony_ci        num = fs.writeSync(dstFd, head, option);
291df226684Sopenharmony_ci        resolve(true);
292df226684Sopenharmony_ci        return;
293df226684Sopenharmony_ci      }
294df226684Sopenharmony_ci
295df226684Sopenharmony_ci      try {
296df226684Sopenharmony_ci        this.zipFlag = true;
297df226684Sopenharmony_ci        this.ff = await fs.open(this.filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
298df226684Sopenharmony_ci        await fs.copyFile(srcFd, this.ff.fd);
299df226684Sopenharmony_ci        await fs.mkdir(this.dirPath);
300df226684Sopenharmony_ci        let options: zlib.Options = {
301df226684Sopenharmony_ci          level: zlib.CompressLevel.COMPRESS_LEVEL_NO_COMPRESSION,
302df226684Sopenharmony_ci          memLevel:zlib.MemLevel.MEM_LEVEL_DEFAULT,
303df226684Sopenharmony_ci          strategy:zlib.CompressStrategy.COMPRESS_STRATEGY_DEFAULT_STRATEGY,
304df226684Sopenharmony_ci        };
305df226684Sopenharmony_ci        await zlib.decompressFile(this.filePath, this.dirPath, options);
306df226684Sopenharmony_ci        await fs.truncate(this.encFile);
307df226684Sopenharmony_ci        await zlib.compressFile(this.dirPath, this.zipPath, options);
308df226684Sopenharmony_ci        this.zf2 = await fs.open(this.zipName, fs.OpenMode.READ_WRITE);
309df226684Sopenharmony_ci        await fs.copyFile(this.zf2.fd, dstFd);
310df226684Sopenharmony_ci        resolve(true);
311df226684Sopenharmony_ci      } catch (err) {
312df226684Sopenharmony_ci        HiLog.error(TAG, `open failed: ${JSON.stringify(err)}`);
313df226684Sopenharmony_ci        resolve(false);
314df226684Sopenharmony_ci      } finally {
315df226684Sopenharmony_ci        if (this.ff || this.zf2) {
316df226684Sopenharmony_ci          await fs.close(this.ff);
317df226684Sopenharmony_ci          await fs.close(this.zf2);
318df226684Sopenharmony_ci        }
319df226684Sopenharmony_ci      }
320df226684Sopenharmony_ci    })
321df226684Sopenharmony_ci  }
322df226684Sopenharmony_ci
323df226684Sopenharmony_ci  isValidDirUri(uri: string | undefined) {
324df226684Sopenharmony_ci    if (uri == undefined) {
325df226684Sopenharmony_ci      return true;
326df226684Sopenharmony_ci    }
327df226684Sopenharmony_ci    let uriObj = new fileUri.FileUri(uri);
328df226684Sopenharmony_ci    let dirUri = uriObj.getFullDirectoryUri(); // get directory path of a uri
329df226684Sopenharmony_ci    let file: fs.File | undefined = undefined;
330df226684Sopenharmony_ci    try {
331df226684Sopenharmony_ci      file = fs.openSync(dirUri, fs.OpenMode.READ_ONLY);
332df226684Sopenharmony_ci      let stat = fs.statSync(file.fd);
333df226684Sopenharmony_ci      let isDir = stat.isDirectory();
334df226684Sopenharmony_ci      HiLog.info(TAG, `test dir ${isDir ? 'is' : 'not'} a directory`);
335df226684Sopenharmony_ci      return isDir;
336df226684Sopenharmony_ci    } catch (e) {
337df226684Sopenharmony_ci      let error = e as BusinessError;
338df226684Sopenharmony_ci      if (error.code === FOPEN_EXCEPTION_IS_DIR) {
339df226684Sopenharmony_ci        HiLog.info(TAG, `test dir is a directory`);
340df226684Sopenharmony_ci        return true;
341df226684Sopenharmony_ci      }
342df226684Sopenharmony_ci      return false;
343df226684Sopenharmony_ci    } finally {
344df226684Sopenharmony_ci      if (file != undefined) {
345df226684Sopenharmony_ci        fs.closeSync(file.fd);
346df226684Sopenharmony_ci      }
347df226684Sopenharmony_ci    }
348df226684Sopenharmony_ci  }
349df226684Sopenharmony_ci  
350df226684Sopenharmony_ci  getSourceFileName() {
351df226684Sopenharmony_ci    try {
352df226684Sopenharmony_ci      let token2FileValue: (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[] =
353df226684Sopenharmony_ci        (GlobalContext
354df226684Sopenharmony_ci          .load('token2File') as Map<number, (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[]>)
355df226684Sopenharmony_ci          .get(this.tokenId) as (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[];
356df226684Sopenharmony_ci      let srcUri: string = token2FileValue[SRC_URI_OFFSET] as string;
357df226684Sopenharmony_ci      let uriObj = new fileUri.FileUri(srcUri);
358df226684Sopenharmony_ci      let dir = uriObj.getFullDirectoryUri(); // get directory path
359df226684Sopenharmony_ci      let name = srcUri.replace(dir, '').replace('/', ''); // strip directory path and '/'
360df226684Sopenharmony_ci      HiLog.info(TAG, 'getSourceFileName: ' + name);
361df226684Sopenharmony_ci      return name;
362df226684Sopenharmony_ci    } catch (e) {
363df226684Sopenharmony_ci      let error = e as BusinessError;
364df226684Sopenharmony_ci      HiLog.error(TAG, 'getSourceFileName error:' + error.message);
365df226684Sopenharmony_ci      return '';
366df226684Sopenharmony_ci    }
367df226684Sopenharmony_ci  }
368df226684Sopenharmony_ci
369df226684Sopenharmony_ci  parseDocumentPickerSaveOption(args: picker.DocumentSaveOptions[], action: string) {
370df226684Sopenharmony_ci    let config: Record<string, string | Record<string, Object>> = {
371df226684Sopenharmony_ci      'action': action,
372df226684Sopenharmony_ci      'parameters': {
373df226684Sopenharmony_ci        'startMode': 'save',
374df226684Sopenharmony_ci      } as Record<string, Object>
375df226684Sopenharmony_ci    };
376df226684Sopenharmony_ci
377df226684Sopenharmony_ci    if (args.length > ARGS_ZERO && typeof args[ARGS_ZERO] === 'object') {
378df226684Sopenharmony_ci      let option: picker.DocumentSaveOptions = args[ARGS_ZERO];
379df226684Sopenharmony_ci      if ((option.newFileNames !== undefined) && option.newFileNames.length > 0) {
380df226684Sopenharmony_ci        if (option.newFileNames[0] == undefined) { // if option name not provided, default to empty string
381df226684Sopenharmony_ci          option.newFileNames[0] = '';
382df226684Sopenharmony_ci        }
383df226684Sopenharmony_ci        if (option.newFileNames[0].length > FILENAME_MAX_LEN) {
384df226684Sopenharmony_ci          throw new Error('file name exceeds max length');
385df226684Sopenharmony_ci        }
386df226684Sopenharmony_ci        let srcName = this.getSourceFileName();
387df226684Sopenharmony_ci        if (option.newFileNames[0].length > 0 && srcName != option.newFileNames[0]) {
388df226684Sopenharmony_ci          HiLog.error(TAG, `src file name is not same with newFileName provided`);
389df226684Sopenharmony_ci          throw new Error('picker filename error');
390df226684Sopenharmony_ci        }
391df226684Sopenharmony_ci        config.parameters['key_pick_file_name'] = option.newFileNames;
392df226684Sopenharmony_ci        config.parameters['saveFile'] = option.newFileNames[0];
393df226684Sopenharmony_ci      }
394df226684Sopenharmony_ci
395df226684Sopenharmony_ci      let isDir = this.isValidDirUri(option.defaultFilePathUri); // undefined returns true
396df226684Sopenharmony_ci      if (isDir) {
397df226684Sopenharmony_ci        config.parameters['key_pick_dir_path'] = option.defaultFilePathUri;
398df226684Sopenharmony_ci      } else {
399df226684Sopenharmony_ci        throw new Error(`defaultFilePathUri is not a valid directory uri`);
400df226684Sopenharmony_ci      }
401df226684Sopenharmony_ci      if ((option.fileSuffixChoices !== undefined) && option.fileSuffixChoices.length > 0) {
402df226684Sopenharmony_ci        config.parameters['key_file_suffix_choices'] = option.fileSuffixChoices;
403df226684Sopenharmony_ci      }
404df226684Sopenharmony_ci    }
405df226684Sopenharmony_ci
406df226684Sopenharmony_ci    HiLog.info(TAG, `[picker] Save config: ${JSON.stringify(config)}`);
407df226684Sopenharmony_ci    return config;
408df226684Sopenharmony_ci  }
409df226684Sopenharmony_ci
410df226684Sopenharmony_ci  getDocumentPickerSaveResult(args: dialogRequest.RequestResult | ability.AbilityResult) {
411df226684Sopenharmony_ci    let saveResult: Record<string, BusinessError | string[]> = {
412df226684Sopenharmony_ci      'error': {} as BusinessError,
413df226684Sopenharmony_ci      'data': []
414df226684Sopenharmony_ci    };
415df226684Sopenharmony_ci
416df226684Sopenharmony_ci    if (((args as dialogRequest.RequestResult).result !== undefined &&
417df226684Sopenharmony_ci        (args as dialogRequest.RequestResult).result === 0) ||
418df226684Sopenharmony_ci        ((args as ability.AbilityResult).resultCode !== undefined &&
419df226684Sopenharmony_ci        (args as ability.AbilityResult).resultCode === 0)) {
420df226684Sopenharmony_ci      if (args.want && args.want.parameters) {
421df226684Sopenharmony_ci        if (args.want.parameters.pick_path_return) {
422df226684Sopenharmony_ci          saveResult.data = args.want.parameters.pick_path_return as string[];
423df226684Sopenharmony_ci        } else {
424df226684Sopenharmony_ci          saveResult.data = args.want.parameters['ability.params.stream'] as string[];
425df226684Sopenharmony_ci        }
426df226684Sopenharmony_ci      }
427df226684Sopenharmony_ci    } else if (((args as dialogRequest.RequestResult).result !== undefined &&
428df226684Sopenharmony_ci        (args as dialogRequest.RequestResult).result === 0) ||
429df226684Sopenharmony_ci        ((args as ability.AbilityResult).resultCode !== undefined &&
430df226684Sopenharmony_ci        (args as ability.AbilityResult).resultCode === -1)) {
431df226684Sopenharmony_ci      saveResult.data = [];
432df226684Sopenharmony_ci    } else {
433df226684Sopenharmony_ci      saveResult.error = this.getErr(errCode.RESULT_ERROR) as BusinessError;
434df226684Sopenharmony_ci    }
435df226684Sopenharmony_ci
436df226684Sopenharmony_ci    HiLog.info(TAG, `[picker] Save saveResult: ${JSON.stringify(saveResult)}`);
437df226684Sopenharmony_ci    return saveResult;
438df226684Sopenharmony_ci  }
439df226684Sopenharmony_ci
440df226684Sopenharmony_ci  getErr(errCode: number) {
441df226684Sopenharmony_ci    return {code: errCode, message: ERRCODE_MAP.get(errCode)} as BusinessError;
442df226684Sopenharmony_ci  }
443df226684Sopenharmony_ci
444df226684Sopenharmony_ci  async documentPickerSave(...args: Object[]): Promise<BusinessError | string[] | undefined> {
445df226684Sopenharmony_ci    let config: Record<string, string | Record<string, Object>>;
446df226684Sopenharmony_ci    let result: dialogRequest.RequestResult | ability.AbilityResult;
447df226684Sopenharmony_ci    try {
448df226684Sopenharmony_ci      config = this.parseDocumentPickerSaveOption(args, ACTION.SAVE_ACTION);
449df226684Sopenharmony_ci      result = await this.context.startAbilityForResult(config, {windowMode: 0});
450df226684Sopenharmony_ci    } catch (error) {
451df226684Sopenharmony_ci      HiLog.info(TAG, `startAbilityForResult${JSON.stringify(error)}`);
452df226684Sopenharmony_ci      return undefined;
453df226684Sopenharmony_ci    }
454df226684Sopenharmony_ci    HiLog.info(TAG, `[picker] Save result: ${JSON.stringify(result)}`);
455df226684Sopenharmony_ci    try {
456df226684Sopenharmony_ci      const saveResult: Record<string, BusinessError | string[]> = this.getDocumentPickerSaveResult(result);
457df226684Sopenharmony_ci      if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') {
458df226684Sopenharmony_ci        return (args[ARGS_ONE] as Function)(saveResult.error, saveResult.data);
459df226684Sopenharmony_ci      } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') {
460df226684Sopenharmony_ci        return (args[ARGS_ZERO] as Function)(saveResult.error, saveResult.data);
461df226684Sopenharmony_ci      }
462df226684Sopenharmony_ci      return new Promise<BusinessError | string[]>((resolve, reject) => {
463df226684Sopenharmony_ci        if (saveResult.data !== undefined) {
464df226684Sopenharmony_ci          resolve(saveResult.data);
465df226684Sopenharmony_ci        } else {
466df226684Sopenharmony_ci          reject(saveResult.error);
467df226684Sopenharmony_ci        }
468df226684Sopenharmony_ci      })
469df226684Sopenharmony_ci    } catch (resultError) {
470df226684Sopenharmony_ci      HiLog.info(TAG, `[picker] Result error: ${resultError}`);
471df226684Sopenharmony_ci    }
472df226684Sopenharmony_ci    return undefined;
473df226684Sopenharmony_ci  }
474df226684Sopenharmony_ci
475df226684Sopenharmony_ci  async prepareDlpFile(want: Want): Promise<void> {
476df226684Sopenharmony_ci    HiLog.info(TAG, `getFile start prepareDlpFile`);
477df226684Sopenharmony_ci    let uri = '';
478df226684Sopenharmony_ci    let displayName = '';
479df226684Sopenharmony_ci    let ret = await this.parseParams(want);
480df226684Sopenharmony_ci    if (!ret) {
481df226684Sopenharmony_ci      HiLog.info(TAG, `parseParams failed`);
482df226684Sopenharmony_ci      return;
483df226684Sopenharmony_ci    }
484df226684Sopenharmony_ci    let documentSaveOptions = new picker.DocumentSaveOptions();
485df226684Sopenharmony_ci    displayName = this.fileName;
486df226684Sopenharmony_ci    documentSaveOptions.newFileNames = [displayName];
487df226684Sopenharmony_ci    documentSaveOptions.fileSuffixChoices = [`.${this.suffix}.dlp`];
488df226684Sopenharmony_ci    try {
489df226684Sopenharmony_ci      let saveRes: BusinessError | string[] | undefined = await this.documentPickerSave(documentSaveOptions);
490df226684Sopenharmony_ci      if (saveRes == undefined || (saveRes instanceof Array && saveRes.length == 0)) {
491df226684Sopenharmony_ci        HiLog.error(TAG, `fail to get uri`);
492df226684Sopenharmony_ci        return;
493df226684Sopenharmony_ci      }
494df226684Sopenharmony_ci      uri = saveRes[0]
495df226684Sopenharmony_ci      if (!isValidPath(uri)) {
496df226684Sopenharmony_ci        HiLog.error(TAG, `invalid uri`);
497df226684Sopenharmony_ci        return;
498df226684Sopenharmony_ci      }
499df226684Sopenharmony_ci      try {
500df226684Sopenharmony_ci        await this.fileOpen(uri);
501df226684Sopenharmony_ci        await this.pickerDialog(uri, want);
502df226684Sopenharmony_ci      } catch {
503df226684Sopenharmony_ci        HiLog.info(TAG, `fileOpen or pickerDialog start failed`);
504df226684Sopenharmony_ci        return;
505df226684Sopenharmony_ci      }
506df226684Sopenharmony_ci    } catch (err) {
507df226684Sopenharmony_ci      HiLog.error(TAG, `DocumentViewPicker failed: ${JSON.stringify(err)}`);
508df226684Sopenharmony_ci      try {
509df226684Sopenharmony_ci        if (this.file != undefined) {
510df226684Sopenharmony_ci          await fs.close(this.file);
511df226684Sopenharmony_ci        }
512df226684Sopenharmony_ci        await this.deleteUri(uri);
513df226684Sopenharmony_ci      } catch (err) {
514df226684Sopenharmony_ci        HiLog.info(TAG, `close fail: ${JSON.stringify(err)}`);
515df226684Sopenharmony_ci      }
516df226684Sopenharmony_ci      this.isOK = false;
517df226684Sopenharmony_ci      await GetAlertMessage.startAlertAbility(this.context,
518df226684Sopenharmony_ci        { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
519df226684Sopenharmony_ci      return;
520df226684Sopenharmony_ci    }
521df226684Sopenharmony_ci  }
522df226684Sopenharmony_ci
523df226684Sopenharmony_ci  pickerDialog(uri: string, want: Want): Promise<void> {
524df226684Sopenharmony_ci    return new Promise(async (resolve, reject) => {
525df226684Sopenharmony_ci      let token2FileValue: (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[] =
526df226684Sopenharmony_ci        (GlobalContext
527df226684Sopenharmony_ci        .load('token2File') as Map<number, (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[]>)
528df226684Sopenharmony_ci        .get(this.tokenId) as (number | string | dlpPermission.DLPFile | dlpPermission.DLPFileAccess)[];
529df226684Sopenharmony_ci      this.dlpFile = token2FileValue[0] as dlpPermission.DLPFile;
530df226684Sopenharmony_ci      this.sandboxBundleName = token2FileValue[1] as string;
531df226684Sopenharmony_ci      let appId: number = token2FileValue[2] as number;
532df226684Sopenharmony_ci      let srcUri: string = token2FileValue[4] as string;
533df226684Sopenharmony_ci      this.srcFdPicker = getFileFd(srcUri);
534df226684Sopenharmony_ci      let success = await this.copyDlpHead(this.srcFdPicker, this.dstFdPicker);
535df226684Sopenharmony_ci      if (!success) {
536df226684Sopenharmony_ci        try {
537df226684Sopenharmony_ci          await fs.close(this.file);
538df226684Sopenharmony_ci          await this.deleteUri(uri);
539df226684Sopenharmony_ci        } catch (err) {
540df226684Sopenharmony_ci          HiLog.info(TAG, `close fail: ${JSON.stringify(err)}`);
541df226684Sopenharmony_ci        }
542df226684Sopenharmony_ci        fs.closeSync(this.srcFdPicker);
543df226684Sopenharmony_ci        this.isOK = false;
544df226684Sopenharmony_ci        await GetAlertMessage.startAlertAbility(this.context,
545df226684Sopenharmony_ci          { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
546df226684Sopenharmony_ci        reject();
547df226684Sopenharmony_ci      }
548df226684Sopenharmony_ci      try {
549df226684Sopenharmony_ci        await this.openDLPFile(uri, want);
550df226684Sopenharmony_ci        await this.addDLPLinkFile(appId, uri);
551df226684Sopenharmony_ci      } catch {
552df226684Sopenharmony_ci        reject();
553df226684Sopenharmony_ci      }
554df226684Sopenharmony_ci      let linkFilePath = Constants.FUSE_PATH + this.linkFileName;
555df226684Sopenharmony_ci      let linkUri = getFileUriByPath(linkFilePath);
556df226684Sopenharmony_ci      (GlobalContext.load('token2File') as Map<number, Object[]>)
557df226684Sopenharmony_ci        .set(this.tokenId, [this.dlpFile, this.sandboxBundleName, appId, this.authPerm, srcUri]);
558df226684Sopenharmony_ci      let sandbox2linkFile: Map<string, (number | string | dlpPermission.DLPFile)[][]> =
559df226684Sopenharmony_ci        GlobalContext.load('sandbox2linkFile') as Map<string, (number | string | dlpPermission.DLPFile)[][]>;
560df226684Sopenharmony_ci      sandbox2linkFile.get(this.sandboxBundleName + appId)
561df226684Sopenharmony_ci        ?.push([this.newDlpFile, this.linkFileName, this.dstFdPicker, this.tokenId]);
562df226684Sopenharmony_ci      (GlobalContext.load('fileOpenHistory') as Map<string, Object[]>)
563df226684Sopenharmony_ci        .set(uri, [this.sandboxBundleName, appId, this.linkFileName, linkUri]);
564df226684Sopenharmony_ci      (GlobalContext.load('linkSet') as Set<string>).add(linkUri);
565df226684Sopenharmony_ci      this.resultUri = getFileUriByPath(linkFilePath);
566df226684Sopenharmony_ci      (this.result.want?.parameters?.pick_path_return as string[]).push(this.resultUri);
567df226684Sopenharmony_ci      this.result.resultCode = 0;
568df226684Sopenharmony_ci      fs.closeSync(this.srcFdPicker);
569df226684Sopenharmony_ci      resolve();
570df226684Sopenharmony_ci    })
571df226684Sopenharmony_ci  }
572df226684Sopenharmony_ci
573df226684Sopenharmony_ci  fileOpen(uri: string): Promise<void> {
574df226684Sopenharmony_ci    return new Promise(async (resolve, reject) => {
575df226684Sopenharmony_ci      try {
576df226684Sopenharmony_ci        this.file = await fs.open(uri, fs.OpenMode.READ_WRITE);
577df226684Sopenharmony_ci        this.dstFdPicker = this.file.fd;
578df226684Sopenharmony_ci        resolve();
579df226684Sopenharmony_ci      } catch (err) {
580df226684Sopenharmony_ci        HiLog.error(TAG, `open: ${uri}, failed: ${JSON.stringify(err)}`);
581df226684Sopenharmony_ci        try {
582df226684Sopenharmony_ci          if (this.file != undefined) {
583df226684Sopenharmony_ci            await fs.close(this.file);
584df226684Sopenharmony_ci          }
585df226684Sopenharmony_ci          await this.deleteUri(uri);
586df226684Sopenharmony_ci        } catch (err) {
587df226684Sopenharmony_ci          HiLog.info(TAG, `close fail: ${JSON.stringify(err)}`);
588df226684Sopenharmony_ci        }
589df226684Sopenharmony_ci        this.isOK = false;
590df226684Sopenharmony_ci        await GetAlertMessage.startAlertAbility(this.context,
591df226684Sopenharmony_ci          { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
592df226684Sopenharmony_ci        reject();
593df226684Sopenharmony_ci      }
594df226684Sopenharmony_ci    })
595df226684Sopenharmony_ci  }
596df226684Sopenharmony_ci
597df226684Sopenharmony_ci  openDLPFile(uri: string, want: Want): Promise<void> {
598df226684Sopenharmony_ci    return new Promise(async (resolve, reject) => {
599df226684Sopenharmony_ci      let callerAppId: string;
600df226684Sopenharmony_ci      try {
601df226684Sopenharmony_ci        let callerBundleName = want.parameters?.['ohos.aafwk.param.callerBundleName'] as string;
602df226684Sopenharmony_ci        callerAppId = await getAppId(callerBundleName);
603df226684Sopenharmony_ci        HiLog.info(TAG, `get AppId: ${callerAppId}`);
604df226684Sopenharmony_ci      } catch {
605df226684Sopenharmony_ci        reject();
606df226684Sopenharmony_ci        return;
607df226684Sopenharmony_ci      }
608df226684Sopenharmony_ci      try {
609df226684Sopenharmony_ci        this.newDlpFile = await dlpPermission.openDLPFile(this.dstFdPicker, callerAppId);
610df226684Sopenharmony_ci      } catch (err) {
611df226684Sopenharmony_ci        HiLog.error(TAG, `generateDlpFile: ${this.dstFdPicker}, failed: ${JSON.stringify(err)}`);
612df226684Sopenharmony_ci        await GetAlertMessage.startAlertAbility(this.context, err);
613df226684Sopenharmony_ci        try {
614df226684Sopenharmony_ci          await fs.close(this.file);
615df226684Sopenharmony_ci          await this.deleteUri(uri);
616df226684Sopenharmony_ci        } catch (err) {
617df226684Sopenharmony_ci          HiLog.info(TAG, `close fail: ${JSON.stringify(err)}`);
618df226684Sopenharmony_ci        }
619df226684Sopenharmony_ci        fs.closeSync(this.srcFdPicker);
620df226684Sopenharmony_ci        reject();
621df226684Sopenharmony_ci      }
622df226684Sopenharmony_ci      resolve();
623df226684Sopenharmony_ci    })
624df226684Sopenharmony_ci  }
625df226684Sopenharmony_ci
626df226684Sopenharmony_ci  async addDLPLinkFile(appId: number, uri: string): Promise<void> {
627df226684Sopenharmony_ci    return new Promise(async (resolve, reject) => {
628df226684Sopenharmony_ci      let date = new Date();
629df226684Sopenharmony_ci      let timestamp = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(),
630df226684Sopenharmony_ci        date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getMilliseconds()).getTime();
631df226684Sopenharmony_ci
632df226684Sopenharmony_ci      this.linkFileName = String(this.sandboxBundleName).substring(0, Constants.BUNDLE_LEN) + '_' + appId +
633df226684Sopenharmony_ci        '_' + timestamp + String(Math.random()).substring(Constants.RAND_START, Constants.RAND_END) + '.dlp.link.' +
634df226684Sopenharmony_ci      this.suffix;
635df226684Sopenharmony_ci
636df226684Sopenharmony_ci      try {
637df226684Sopenharmony_ci        await this.newDlpFile.addDLPLinkFile(this.linkFileName);
638df226684Sopenharmony_ci      } catch (err) {
639df226684Sopenharmony_ci        HiLog.error(TAG, `addDlpLinkFile failed: ${JSON.stringify(err)}`);
640df226684Sopenharmony_ci        try {
641df226684Sopenharmony_ci          await this.newDlpFile.closeDLPFile();
642df226684Sopenharmony_ci        } catch (err) {
643df226684Sopenharmony_ci          HiLog.error(TAG, `closeDlpFile failed: ${JSON.stringify(err)}`);
644df226684Sopenharmony_ci        }
645df226684Sopenharmony_ci        try {
646df226684Sopenharmony_ci          await fs.close(this.file);
647df226684Sopenharmony_ci          await this.deleteUri(uri);
648df226684Sopenharmony_ci        } catch (err) {
649df226684Sopenharmony_ci          HiLog.info(TAG, `close fail: ${JSON.stringify(err)}`);
650df226684Sopenharmony_ci        }
651df226684Sopenharmony_ci        fs.closeSync(this.srcFdPicker);
652df226684Sopenharmony_ci        this.isOK = false;
653df226684Sopenharmony_ci        await GetAlertMessage.startAlertAbility(this.context,
654df226684Sopenharmony_ci          { code: Constants.ERR_JS_APP_INSIDE_ERROR } as BusinessError);
655df226684Sopenharmony_ci        reject();
656df226684Sopenharmony_ci      }
657df226684Sopenharmony_ci      resolve();
658df226684Sopenharmony_ci    })
659df226684Sopenharmony_ci  }
660df226684Sopenharmony_ci
661df226684Sopenharmony_ci  deleteUri(uri: string): Promise<void> {
662df226684Sopenharmony_ci    return new Promise(async (resolve, reject) => {
663df226684Sopenharmony_ci      let uriInfo: fileUri.FileUri = new fileUri.FileUri('');
664df226684Sopenharmony_ci      try {
665df226684Sopenharmony_ci        uriInfo = new fileUri.FileUri(uri);
666df226684Sopenharmony_ci      } catch (err) {
667df226684Sopenharmony_ci        HiLog.error(TAG, `fileUri fail: ${JSON.stringify(err)}`);
668df226684Sopenharmony_ci        reject();
669df226684Sopenharmony_ci      }
670df226684Sopenharmony_ci      try {
671df226684Sopenharmony_ci        await fs.unlink(uriInfo.path);
672df226684Sopenharmony_ci      } catch (err) {
673df226684Sopenharmony_ci        HiLog.error(TAG, `unlink fail: ${JSON.stringify(err)}`);
674df226684Sopenharmony_ci        reject();
675df226684Sopenharmony_ci      }
676df226684Sopenharmony_ci      resolve();
677df226684Sopenharmony_ci    })
678df226684Sopenharmony_ci  }
679df226684Sopenharmony_ci};
680