u
This commit is contained in:
345
src/tools/vfs.js
345
src/tools/vfs.js
@@ -435,6 +435,193 @@ export function createVfs(vfsRoot, options = {}) {
|
||||
throw sanitizeFsError(err, p);
|
||||
}
|
||||
},
|
||||
|
||||
appendFile: (p, data, opt, cb) => {
|
||||
const callback = typeof opt === 'function' ? opt : cb;
|
||||
const options = typeof opt === 'function' ? undefined : opt;
|
||||
try {
|
||||
vfs.appendFileSync(p, data, options);
|
||||
if (typeof callback === 'function') process.nextTick(callback, null);
|
||||
} catch (err) {
|
||||
if (typeof callback === 'function') process.nextTick(callback, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
copyFileSync: (src, dest, mode) => {
|
||||
assertNotReadOnlyDatabasePath(dest);
|
||||
const hash = extractFullHash(src);
|
||||
if (hash) {
|
||||
const buf = syncFetchImage(hash);
|
||||
if (buf) {
|
||||
const destDisk = safeResolve(dest);
|
||||
fs.mkdirSync(path.dirname(destDisk), { recursive: true });
|
||||
if (typeof mode === 'number' && (mode & fs.constants.COPYFILE_EXCL) && fs.existsSync(destDisk)) {
|
||||
const err = new Error(`EEXIST: file already exists, copyfile '${src}' -> '${dest}'`);
|
||||
err.code = 'EEXIST';
|
||||
err.path = dest;
|
||||
err.syscall = 'copyfile';
|
||||
throw err;
|
||||
}
|
||||
try {
|
||||
return fs.writeFileSync(destDisk, buf);
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
const srcDisk = safeResolve(src);
|
||||
const destDisk = safeResolve(dest);
|
||||
fs.mkdirSync(path.dirname(destDisk), { recursive: true });
|
||||
try {
|
||||
return fs.copyFileSync(srcDisk, destDisk, mode);
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, `${src} -> ${dest}`);
|
||||
}
|
||||
},
|
||||
|
||||
copyFile: (src, dest, mode, cb) => {
|
||||
const callback = typeof mode === 'function' ? mode : cb;
|
||||
const m = typeof mode === 'function' ? undefined : mode;
|
||||
try {
|
||||
vfs.copyFileSync(src, dest, m);
|
||||
if (typeof callback === 'function') process.nextTick(callback, null);
|
||||
} catch (err) {
|
||||
if (typeof callback === 'function') process.nextTick(callback, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
cpSync: (src, dest, opt) => {
|
||||
assertNotReadOnlyDatabasePath(dest);
|
||||
const srcDisk = safeResolve(src);
|
||||
const destDisk = safeResolve(dest);
|
||||
fs.mkdirSync(path.dirname(destDisk), { recursive: true });
|
||||
try {
|
||||
return fs.cpSync(srcDisk, destDisk, opt);
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, `${src} -> ${dest}`);
|
||||
}
|
||||
},
|
||||
|
||||
cp: (src, dest, opt, cb) => {
|
||||
const callback = typeof opt === 'function' ? opt : cb;
|
||||
const options = typeof opt === 'function' ? undefined : opt;
|
||||
try {
|
||||
vfs.cpSync(src, dest, options);
|
||||
if (typeof callback === 'function') process.nextTick(callback, null);
|
||||
} catch (err) {
|
||||
if (typeof callback === 'function') process.nextTick(callback, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
renameSync: (oldPath, newPath) => {
|
||||
assertNotReadOnlyDatabasePath(oldPath);
|
||||
assertNotReadOnlyDatabasePath(newPath);
|
||||
const oldDisk = safeResolve(oldPath);
|
||||
const newDisk = safeResolve(newPath);
|
||||
fs.mkdirSync(path.dirname(newDisk), { recursive: true });
|
||||
try {
|
||||
return fs.renameSync(oldDisk, newDisk);
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, `${oldPath} -> ${newPath}`);
|
||||
}
|
||||
},
|
||||
|
||||
rename: (oldPath, newPath, cb) => {
|
||||
try {
|
||||
vfs.renameSync(oldPath, newPath);
|
||||
if (typeof cb === 'function') process.nextTick(cb, null);
|
||||
} catch (err) {
|
||||
if (typeof cb === 'function') process.nextTick(cb, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
truncateSync: (p, len) => {
|
||||
assertNotReadOnlyDatabasePath(p);
|
||||
const diskPath = safeResolve(p);
|
||||
try {
|
||||
return fs.truncateSync(diskPath, len);
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, p);
|
||||
}
|
||||
},
|
||||
|
||||
truncate: (p, len, cb) => {
|
||||
const callback = typeof len === 'function' ? len : cb;
|
||||
const l = typeof len === 'function' ? undefined : len;
|
||||
try {
|
||||
vfs.truncateSync(p, l);
|
||||
if (typeof callback === 'function') process.nextTick(callback, null);
|
||||
} catch (err) {
|
||||
if (typeof callback === 'function') process.nextTick(callback, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
accessSync: (p, mode) => {
|
||||
const hash = extractFullHash(p);
|
||||
if (hash) {
|
||||
const buf = syncFetchImage(hash);
|
||||
if (buf) {
|
||||
if (typeof mode === 'number' && (mode & fs.constants.W_OK)) {
|
||||
const err = new Error(`EACCES: permission denied, access '${p}'`);
|
||||
err.code = 'EACCES';
|
||||
err.path = p;
|
||||
err.syscall = 'access';
|
||||
throw err;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
const diskPath = safeResolve(p);
|
||||
try {
|
||||
return fs.accessSync(diskPath, mode);
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, p);
|
||||
}
|
||||
},
|
||||
|
||||
access: (p, mode, cb) => {
|
||||
const callback = typeof mode === 'function' ? mode : cb;
|
||||
const m = typeof mode === 'function' ? undefined : mode;
|
||||
try {
|
||||
vfs.accessSync(p, m);
|
||||
if (typeof callback === 'function') process.nextTick(callback, null);
|
||||
} catch (err) {
|
||||
if (typeof callback === 'function') process.nextTick(callback, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
realpathSync: (p, opt) => {
|
||||
const diskPath = safeResolve(p);
|
||||
try {
|
||||
const resolved = fs.realpathSync(diskPath, opt);
|
||||
const rel = path.relative(vfsRoot, resolved);
|
||||
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
||||
throw new Error(`Access denied: path "${p}" escapes virtual filesystem boundary.`);
|
||||
}
|
||||
return rel ? rel.split(path.sep).join('/') : '.';
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, p);
|
||||
}
|
||||
},
|
||||
|
||||
realpath: (p, opt, cb) => {
|
||||
const callback = typeof opt === 'function' ? opt : cb;
|
||||
const options = typeof opt === 'function' ? undefined : opt;
|
||||
try {
|
||||
const res = vfs.realpathSync(p, options);
|
||||
if (typeof callback === 'function') process.nextTick(callback, null, res);
|
||||
} catch (err) {
|
||||
if (typeof callback === 'function') process.nextTick(callback, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
existsSync: (p) => {
|
||||
const hash = extractFullHash(p);
|
||||
if (hash) {
|
||||
@@ -449,6 +636,11 @@ export function createVfs(vfsRoot, options = {}) {
|
||||
}
|
||||
},
|
||||
|
||||
exists: (p, cb) => {
|
||||
const res = vfs.existsSync(p);
|
||||
if (typeof cb === 'function') process.nextTick(cb, res);
|
||||
},
|
||||
|
||||
statSync: (p, opt) => {
|
||||
const hash = extractFullHash(p);
|
||||
if (hash) {
|
||||
@@ -466,18 +658,63 @@ export function createVfs(vfsRoot, options = {}) {
|
||||
}
|
||||
}
|
||||
const diskPath = safeResolve(p);
|
||||
return fs.statSync(diskPath, opt);
|
||||
try {
|
||||
return fs.statSync(diskPath, opt);
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, p);
|
||||
}
|
||||
},
|
||||
|
||||
stat: (p, opt, cb) => {
|
||||
const callback = typeof opt === 'function' ? opt : cb;
|
||||
const options = typeof opt === 'function' ? undefined : opt;
|
||||
try {
|
||||
const res = vfs.statSync(p, options);
|
||||
if (typeof callback === 'function') process.nextTick(callback, null, res);
|
||||
} catch (err) {
|
||||
if (typeof callback === 'function') process.nextTick(callback, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
lstatSync: (p, opt) => vfs.statSync(p, opt),
|
||||
|
||||
lstat: (p, opt, cb) => {
|
||||
const callback = typeof opt === 'function' ? opt : cb;
|
||||
const options = typeof opt === 'function' ? undefined : opt;
|
||||
try {
|
||||
const res = vfs.lstatSync(p, options);
|
||||
if (typeof callback === 'function') process.nextTick(callback, null, res);
|
||||
} catch (err) {
|
||||
if (typeof callback === 'function') process.nextTick(callback, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
readdirSync: (p = '.', opt) => {
|
||||
const clean = typeof p === 'string' ? p.trim().replace(/^([a-zA-Z]:|\/)+/, '') : '';
|
||||
if (VIRTUAL_IMAGE_FOLDERS.has(clean.toLowerCase())) {
|
||||
return [];
|
||||
}
|
||||
const diskPath = safeResolve(p);
|
||||
return fs.readdirSync(diskPath, opt);
|
||||
try {
|
||||
return fs.readdirSync(diskPath, opt);
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, p);
|
||||
}
|
||||
},
|
||||
|
||||
readdir: (p = '.', opt, cb) => {
|
||||
const callback = typeof opt === 'function' ? opt : (typeof p === 'function' ? p : cb);
|
||||
const options = typeof opt === 'function' ? undefined : opt;
|
||||
const pathArg = typeof p === 'function' ? '.' : p;
|
||||
try {
|
||||
const res = vfs.readdirSync(pathArg, options);
|
||||
if (typeof callback === 'function') process.nextTick(callback, null, res);
|
||||
} catch (err) {
|
||||
if (typeof callback === 'function') process.nextTick(callback, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
mkdirSync: (p, opt) => {
|
||||
@@ -489,22 +726,81 @@ export function createVfs(vfsRoot, options = {}) {
|
||||
throw sanitizeFsError(err, p);
|
||||
}
|
||||
},
|
||||
|
||||
mkdir: (p, opt, cb) => {
|
||||
const callback = typeof opt === 'function' ? opt : cb;
|
||||
const options = typeof opt === 'function' ? undefined : opt;
|
||||
try {
|
||||
const res = vfs.mkdirSync(p, options);
|
||||
if (typeof callback === 'function') process.nextTick(callback, null, res);
|
||||
} catch (err) {
|
||||
if (typeof callback === 'function') process.nextTick(callback, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
unlinkSync: (p) => {
|
||||
assertNotReadOnlyDatabasePath(p);
|
||||
const diskPath = safeResolve(p);
|
||||
return fs.unlinkSync(diskPath);
|
||||
try {
|
||||
return fs.unlinkSync(diskPath);
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, p);
|
||||
}
|
||||
},
|
||||
|
||||
unlink: (p, cb) => {
|
||||
try {
|
||||
vfs.unlinkSync(p);
|
||||
if (typeof cb === 'function') process.nextTick(cb, null);
|
||||
} catch (err) {
|
||||
if (typeof cb === 'function') process.nextTick(cb, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
rmdirSync: (p, opt) => {
|
||||
assertNotReadOnlyDatabasePath(p);
|
||||
const diskPath = safeResolve(p);
|
||||
return fs.rmdirSync(diskPath, opt);
|
||||
try {
|
||||
return fs.rmdirSync(diskPath, opt);
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, p);
|
||||
}
|
||||
},
|
||||
|
||||
rmdir: (p, opt, cb) => {
|
||||
const callback = typeof opt === 'function' ? opt : cb;
|
||||
const options = typeof opt === 'function' ? undefined : opt;
|
||||
try {
|
||||
vfs.rmdirSync(p, options);
|
||||
if (typeof callback === 'function') process.nextTick(callback, null);
|
||||
} catch (err) {
|
||||
if (typeof callback === 'function') process.nextTick(callback, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
rmSync: (p, opt) => {
|
||||
assertNotReadOnlyDatabasePath(p);
|
||||
const diskPath = safeResolve(p);
|
||||
return fs.rmSync(diskPath, opt);
|
||||
try {
|
||||
return fs.rmSync(diskPath, opt);
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, p);
|
||||
}
|
||||
},
|
||||
|
||||
rm: (p, opt, cb) => {
|
||||
const callback = typeof opt === 'function' ? opt : cb;
|
||||
const options = typeof opt === 'function' ? undefined : opt;
|
||||
try {
|
||||
vfs.rmSync(p, options);
|
||||
if (typeof callback === 'function') process.nextTick(callback, null);
|
||||
} catch (err) {
|
||||
if (typeof callback === 'function') process.nextTick(callback, err);
|
||||
else throw err;
|
||||
}
|
||||
},
|
||||
|
||||
promises: {
|
||||
@@ -538,11 +834,41 @@ export function createVfs(vfsRoot, options = {}) {
|
||||
},
|
||||
writeFile: async (p, data, opt) => vfs.writeFileSync(p, data, opt),
|
||||
appendFile: async (p, data, opt) => vfs.appendFileSync(p, data, opt),
|
||||
copyFile: async (src, dest, mode) => {
|
||||
assertNotReadOnlyDatabasePath(dest);
|
||||
const hash = extractFullHash(src);
|
||||
if (hash) {
|
||||
const buf = await vfs.promises.readFile(src);
|
||||
const destDisk = safeResolve(dest);
|
||||
fs.mkdirSync(path.dirname(destDisk), { recursive: true });
|
||||
if (typeof mode === 'number' && (mode & fs.constants.COPYFILE_EXCL) && fs.existsSync(destDisk)) {
|
||||
const err = new Error(`EEXIST: file already exists, copyfile '${src}' -> '${dest}'`);
|
||||
err.code = 'EEXIST';
|
||||
err.path = dest;
|
||||
err.syscall = 'copyfile';
|
||||
throw err;
|
||||
}
|
||||
try {
|
||||
return fs.writeFileSync(destDisk, buf);
|
||||
} catch (err) {
|
||||
throw sanitizeFsError(err, dest);
|
||||
}
|
||||
}
|
||||
return vfs.copyFileSync(src, dest, mode);
|
||||
},
|
||||
cp: async (src, dest, opt) => vfs.cpSync(src, dest, opt),
|
||||
rename: async (oldPath, newPath) => vfs.renameSync(oldPath, newPath),
|
||||
truncate: async (p, len) => vfs.truncateSync(p, len),
|
||||
access: async (p, mode) => vfs.accessSync(p, mode),
|
||||
realpath: async (p, opt) => vfs.realpathSync(p, opt),
|
||||
stat: async (p, opt) => vfs.statSync(p, opt),
|
||||
lstat: async (p, opt) => vfs.lstatSync(p, opt),
|
||||
readdir: async (p, opt) => vfs.readdirSync(p, opt),
|
||||
mkdir: async (p, opt) => vfs.mkdirSync(p, opt),
|
||||
mkdir: async (p, opt) => vfs.mkdirSync(p, opt),
|
||||
rmdir: async (p, opt) => vfs.rmdirSync(p, opt),
|
||||
unlink: async (p) => vfs.unlinkSync(p),
|
||||
rm: async (p, opt) => vfs.rmSync(p, opt),
|
||||
rm: async (p, opt) => vfs.rmSync(p, opt),
|
||||
constants: fs.constants,
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -573,5 +899,10 @@ export function createVfs(vfsRoot, options = {}) {
|
||||
sanitizeError: (err, virtualPath) => sanitizeFsError(err, virtualPath),
|
||||
};
|
||||
|
||||
vfs.realpathSync.native = (p, opt) => vfs.realpathSync(p, opt);
|
||||
vfs.realpath.native = (p, opt, cb) => vfs.realpath(p, opt, cb);
|
||||
vfs.promises.realpath.native = async (p, opt) => vfs.realpathSync(p, opt);
|
||||
vfs.constants = fs.constants;
|
||||
|
||||
return vfs;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user