Skip to content

Commit deeb1f4

Browse files
committed
adding phoenix fslib_native pieces
1 parent 6f54138 commit deeb1f4

4 files changed

Lines changed: 381 additions & 5 deletions

File tree

dist/virtualfs.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/virtualfs.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/fslib_native.js

Lines changed: 378 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,378 @@
1+
/*
2+
* GNU AGPL-3.0 License
3+
*
4+
* Copyright (c) 2021 - present core.ai . All rights reserved.
5+
*
6+
* This program is free software: you can redistribute it and/or modify it under
7+
* the terms of the GNU Affero General Public License as published by the Free
8+
* Software Foundation, either version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11+
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12+
* See the GNU Affero General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Affero General Public License along
15+
* with this program. If not, see https://opensource.org/licenses/AGPL-3.0.
16+
*
17+
*/
18+
19+
// jshint ignore: start
20+
/*global TextDecoder, buffer*/
21+
/*eslint no-console: 0*/
22+
/*eslint strict: ["error", "global"]*/
23+
24+
const {Mounts} = require('./fslib_mounts');
25+
const {Errors} = require('./errno');
26+
const {Constants} = require('./constants');
27+
const {Utils} =require('./utils');
28+
29+
30+
async function _listDir(handle, callback) {
31+
let dirEntryNames = [];
32+
for await (const [key] of handle.entries()) {
33+
dirEntryNames.push(key);
34+
}
35+
if(callback){
36+
callback(null, dirEntryNames);
37+
}
38+
return dirEntryNames;
39+
}
40+
41+
42+
async function _mkdir(paretDirHandle, dirName, callback) {
43+
try {
44+
let childDirHandle = await paretDirHandle.getDirectoryHandle(dirName, { create: true });
45+
if(callback){
46+
callback(null);
47+
}
48+
return childDirHandle;
49+
} catch (e) {
50+
if(callback){
51+
callback(new Errors.EIO('Filer native fs function not yet supported.', e));
52+
}
53+
throw new Errors.EIO('Filer native fs function not yet supported.', e);
54+
}
55+
}
56+
57+
58+
function mkdir(path, mode, callback) {
59+
if (arguments.length < 4) {
60+
callback = mode;
61+
}
62+
63+
path = window.path.normalize(path);
64+
let dirname= window.path.dirname(path);
65+
let subdirName= window.path.basename(path);
66+
Mounts.getHandleFromPath(dirname, (err, handle) => {
67+
if(err){
68+
callback(err);
69+
} else if (handle.kind === Constants.KIND_FILE) {
70+
callback(new Errors.ENOTDIR('Parent path is not a directory.'));
71+
}else {
72+
_mkdir(handle, subdirName, callback);
73+
}
74+
});
75+
}
76+
77+
78+
function readdir(path, options, callback) {
79+
path = window.path.normalize(path);
80+
if (typeof options !== 'function') {
81+
throw new Errors.ENOSYS('Filer readdir options are not yet supported');
82+
}
83+
callback = options;
84+
85+
if(path === Constants.MOUNT_POINT_ROOT ) {
86+
let mountedFolders = Object.keys(Mounts.getMountPoints());
87+
callback(null, mountedFolders);
88+
} else {
89+
Mounts.getHandleFromPath(path, (err, handle) => {
90+
if(err){
91+
callback(err);
92+
} else if (handle.kind === Constants.KIND_FILE) {
93+
callback(new Errors.ENOTDIR('Path is not a directory.'));
94+
}else {
95+
_listDir(handle, callback);
96+
}
97+
});
98+
}
99+
}
100+
101+
function _getDecodedString(buffer, encoding) {
102+
try {
103+
return new TextDecoder(encoding).decode(buffer);
104+
} catch (e) {
105+
return null;
106+
}
107+
}
108+
109+
async function _getFileContents(fileHandle, encoding, callback) {
110+
encoding = encoding || 'utf-8';
111+
try {
112+
let file = await fileHandle.getFile();
113+
let buffer = await file.arrayBuffer();
114+
if(encoding === BYTE_ARRAY_ENCODING) {
115+
callback(null, buffer, encoding);
116+
return;
117+
}
118+
let decodedString = _getDecodedString(buffer, encoding);
119+
if(decodedString !== null){
120+
callback(null, decodedString, encoding);
121+
} else {
122+
callback(new Errors.EIO(`Encoding ${encoding} no supported`));
123+
}
124+
} catch (e) {
125+
callback(e);
126+
}
127+
}
128+
129+
function _validateFileOptions(options, enc, fileMode){
130+
if(!options) {
131+
options = { encoding: enc, flag: fileMode };
132+
} else if(typeof options === 'function') {
133+
options = { encoding: enc, flag: fileMode };
134+
} else if(typeof options === 'string') {
135+
options = { encoding: options, flag: fileMode };
136+
}
137+
return options;
138+
}
139+
140+
function readFile(path, options, callback) {
141+
path = window.path.normalize(path);
142+
143+
callback = arguments[arguments.length - 1];
144+
options = _validateFileOptions(options, null, 'r');
145+
146+
Mounts.getHandleFromPath(path, (err, handle) => {
147+
if(err){
148+
callback(err);
149+
} else if (handle.kind === Constants.KIND_DIRECTORY) {
150+
callback(new Errors.EISDIR('Path is a directory.'));
151+
}else {
152+
_getFileContents(handle, options.encoding, callback);
153+
}
154+
});
155+
}
156+
157+
158+
function stat(path, callback) {
159+
path = window.path.normalize(path);
160+
Mounts.getHandleFromPath(path, (err, handle) => {
161+
if(err){
162+
callback(err);
163+
} else {
164+
Utils.createStatObject(path, handle).then(pathStat => {
165+
callback(null, pathStat);
166+
}).catch( error => {
167+
callback(error);
168+
});
169+
}
170+
});
171+
}
172+
173+
174+
async function _writeFileWithName(paretDirHandle, fileName, encoding, data, callback) {
175+
try {
176+
const newFileHandle = await paretDirHandle.getFileHandle(fileName, { create: true });
177+
const writable = await newFileHandle.createWritable();
178+
await writable.write(data);
179+
await writable.close();
180+
callback(null);
181+
} catch (e) {
182+
callback(e);
183+
}
184+
}
185+
186+
function writeFile (path, data, options, callback) {
187+
callback = arguments[arguments.length - 1];
188+
options = _validateFileOptions(options, 'utf8', 'w');
189+
if(!buffer.Buffer.isBuffer(data)) {
190+
if(typeof data === 'number') {
191+
data = '' + data;
192+
}
193+
data = data || '';
194+
if(typeof data !== 'string') {
195+
data = buffer.Buffer.from(data.toString());
196+
} else {
197+
data = buffer.Buffer.from(data || '', options.encoding || 'utf8');
198+
}
199+
}
200+
201+
path = window.path.normalize(path);
202+
let dirname= window.path.dirname(path);
203+
let fileName= window.path.basename(path);
204+
Mounts.getHandleFromPath(dirname, (err, handle) => {
205+
if(err){
206+
callback(err);
207+
} else if (handle.kind === Constants.KIND_FILE) {
208+
callback(new Errors.ENOTDIR('Parent path is not a directory.'));
209+
}else {
210+
_writeFileWithName(handle, fileName, options.encoding, data, callback);
211+
}
212+
});
213+
}
214+
215+
async function _deleteEntry(dirHandle, entryNameToDelete, callback, recursive=true){
216+
try {
217+
await dirHandle.removeEntry(entryNameToDelete, { recursive: recursive });
218+
callback(null);
219+
} catch (err) {
220+
callback(err);
221+
}
222+
}
223+
224+
async function unlink(path, callback) {
225+
path = window.path.normalize(path);
226+
let dirPath= window.path.dirname(path);
227+
let baseName= window.path.basename(path);
228+
Mounts.getHandleFromPath(dirPath, async (err, dirHandle) => {
229+
if(err){
230+
callback(err);
231+
} else {
232+
_deleteEntry(dirHandle, baseName, callback);
233+
}
234+
});
235+
}
236+
237+
async function _getDestinationHandleForCopy(dst, srcBaseName, handleKindToCreate) {
238+
return new Promise(async (resolve, reject) => {
239+
dst = window.path.normalize(dst);
240+
let dirPath= window.path.dirname(dst);
241+
let dstBaseName= window.path.basename(dst);
242+
let dstHandle = await Mounts.getHandleFromPathIfPresent(dst);
243+
let dstParentHandle = await Mounts.getHandleFromPathIfPresent(dirPath);
244+
if (dstHandle && dstHandle.kind === Constants.KIND_FILE) {
245+
reject(new Errors.EEXIST(`Destination file already exists: ${dst}`));
246+
} else if (dstHandle && dstHandle.kind === Constants.KIND_DIRECTORY
247+
&& handleKindToCreate === Constants.KIND_FILE) {
248+
const fileHandle = await dstHandle.getFileHandle(srcBaseName, {create: true});
249+
resolve(fileHandle);
250+
} else if (dstHandle && dstHandle.kind === Constants.KIND_DIRECTORY
251+
&& handleKindToCreate === Constants.KIND_DIRECTORY) {
252+
let dstChildHandle = await Mounts.getHandleFromPathIfPresent(`${dst}/${srcBaseName}`);
253+
if(dstChildHandle){
254+
reject(new Errors.EEXIST(`Copy destination already exists: ${dst}/${srcBaseName}`));
255+
return;
256+
}
257+
const directoryHandle = await dstHandle.getDirectoryHandle(srcBaseName, {create: true});
258+
resolve(directoryHandle);
259+
} else if (!dstHandle && dstParentHandle && dstParentHandle.kind === Constants.KIND_DIRECTORY
260+
&& handleKindToCreate === Constants.KIND_FILE) {
261+
const fileHandle = await dstParentHandle.getFileHandle(dstBaseName, {create: true});
262+
resolve(fileHandle);
263+
} else if (!dstHandle && dstParentHandle && dstParentHandle.kind === Constants.KIND_DIRECTORY
264+
&& handleKindToCreate === Constants.KIND_DIRECTORY) {
265+
const fileHandle = await dstParentHandle.getDirectoryHandle(dstBaseName, {create: true});
266+
resolve(fileHandle);
267+
} else {
268+
reject(new Errors.ENOENT(`Copy destination doesnt exist: ${dst}`));
269+
}
270+
});
271+
}
272+
273+
async function _copyFileFromHandles(srcFileHandle, dstHandle, optionalName) {
274+
// TODO Add retry mechanisms when copying large folders
275+
try {
276+
if(optionalName){
277+
dstHandle = await dstHandle.getFileHandle(optionalName, {create: true});
278+
}
279+
const srcFile = await srcFileHandle.getFile();
280+
const srcStream = await srcFile.stream();
281+
const writable = await dstHandle.createWritable();
282+
await srcStream.pipeTo(writable);
283+
} catch (e) {
284+
console.error(`Error while copying ${dstHandle.name}/${optionalName} : ${e}`);
285+
throw e;
286+
}
287+
}
288+
289+
async function _copyFileWithHandle(srcFileHandle, dst, srcFileName, callback) {
290+
try {
291+
let dstHandle = await _getDestinationHandleForCopy(dst, srcFileName, Constants.KIND_FILE);
292+
await _copyFileFromHandles(srcFileHandle, dstHandle);
293+
callback(null);
294+
} catch (e) {
295+
callback(e);
296+
}
297+
}
298+
299+
async function _treeCopy(srcFolderHandle, dstFolderHandle, recursive) {
300+
let allDonePromises = [];
301+
for await (const [key, srcHandle] of srcFolderHandle.entries()) {
302+
if (srcHandle.kind === Constants.KIND_FILE) {
303+
allDonePromises.push(_copyFileFromHandles(srcHandle, dstFolderHandle, key));
304+
} else if (srcHandle.kind === Constants.KIND_DIRECTORY) {
305+
const childDirHandle = await _mkdir(dstFolderHandle, key);
306+
if(recursive && childDirHandle){
307+
allDonePromises.push(_treeCopy(srcHandle, childDirHandle, recursive));
308+
}
309+
}
310+
}
311+
await Promise.all(allDonePromises);
312+
}
313+
314+
async function _copyFolderWithHandle(srcFolderHandle, dst, srcFileName, callback, recursive) {
315+
try {
316+
let dstFolderHandle = await _getDestinationHandleForCopy(dst, srcFileName, Constants.KIND_DIRECTORY);
317+
await _treeCopy(srcFolderHandle, dstFolderHandle, recursive);
318+
callback(null);
319+
} catch (e) {
320+
callback(e);
321+
}
322+
}
323+
324+
async function copy(src, dst, callback, recursive = true) {
325+
let srcFile = window.path.normalize(src);
326+
let srcFileName= window.path.basename(srcFile);
327+
Mounts.getHandleFromPath(srcFile, async (err, srcHandle) => {
328+
if(err){
329+
callback(err);
330+
} else if (srcHandle.kind === Constants.KIND_FILE) {
331+
return _copyFileWithHandle(srcHandle, dst, srcFileName, callback);
332+
} else if (srcHandle.kind === Constants.KIND_DIRECTORY) {
333+
return _copyFolderWithHandle(srcHandle, dst, srcFileName, callback, recursive);
334+
} else {
335+
callback(new Errors.EIO(`Cannot copy src: ${srcFile}`));
336+
}
337+
});
338+
}
339+
340+
async function rename(oldPath, newPath, cb) {
341+
copy(oldPath, newPath, err => {
342+
if(err) {
343+
cb(err);
344+
} else {
345+
setTimeout(()=>{
346+
unlink(oldPath, cb);
347+
}, 0);
348+
}
349+
});
350+
}
351+
352+
function mountNativeFolder(...args) {
353+
Mounts.mountNativeFolder(...args);
354+
}
355+
356+
function refreshMountPoints() {
357+
Mounts.refreshMountPoints();
358+
}
359+
360+
const BYTE_ARRAY_ENCODING = 'byte-array';
361+
362+
const NativeFS = {
363+
mountNativeFolder,
364+
refreshMountPoints,
365+
mkdir,
366+
readdir,
367+
stat,
368+
readFile,
369+
writeFile,
370+
unlink,
371+
copy,
372+
rename,
373+
BYTE_ARRAY_ENCODING
374+
};
375+
376+
module.exports ={
377+
NativeFS
378+
};

src/virtualfs.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,9 @@ globalObject.virtualfs = {
3333
env: env
3434
};
3535

36-
console.log('virtual fs started on environment: ', virtualfs.env);
36+
console.log(`virtual fs started in debugMode:${virtualfs.debugMode} on environment: ${virtualfs.env}`);
3737
if(!virtualfs.debugMode){
3838
console.log = function () {
3939
// dont log anything
4040
};
41-
} else {
42-
console.log('virtualFs started in debug mode');
4341
}

0 commit comments

Comments
 (0)