mirror of
https://github.com/falk-werner/webfuse-provider
synced 2024-10-27 20:44:10 +00:00
extracted JavaScript provider API
This commit is contained in:
parent
f22a1dfd5e
commit
85ae74a57a
@ -234,7 +234,7 @@ module.exports = {
|
|||||||
"quotes": "off",
|
"quotes": "off",
|
||||||
"radix": "error",
|
"radix": "error",
|
||||||
"require-atomic-updates": "error",
|
"require-atomic-updates": "error",
|
||||||
"require-await": "error",
|
"require-await": "off",
|
||||||
"require-jsdoc": "off",
|
"require-jsdoc": "off",
|
||||||
"require-unicode-regexp": "off",
|
"require-unicode-regexp": "off",
|
||||||
"rest-spread-spacing": "error",
|
"rest-spread-spacing": "error",
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
export class Connection {
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.ws = null;
|
|
||||||
this.isConnected = false;
|
|
||||||
this.onopen = () => {};
|
|
||||||
this.onclose = () => {};
|
|
||||||
this.onmessage = () => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
connectTo(url) {
|
|
||||||
if (this.ws) { this.close(); }
|
|
||||||
|
|
||||||
this.ws = new WebSocket(url, "fs");
|
|
||||||
this.ws.onopen = () => {
|
|
||||||
this.isConnected = true;
|
|
||||||
this.onopen();
|
|
||||||
};
|
|
||||||
this.ws.onclose = () => {
|
|
||||||
this.isConnected = false;
|
|
||||||
this.onclose();
|
|
||||||
};
|
|
||||||
this.ws.onmessage = (message) => {
|
|
||||||
this.onmessage(message);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
send(message) {
|
|
||||||
this.ws.send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.ws.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,8 +1,6 @@
|
|||||||
export class ConnectionView {
|
export class ConnectionView {
|
||||||
constructor(connection) {
|
constructor(client) {
|
||||||
this.connection = connection;
|
this.connection = client;
|
||||||
this.connection.onclose = () => { this.onConnectionClosed(); };
|
|
||||||
this.connection.onopen = () => { this.onConnectionOpened(); };
|
|
||||||
|
|
||||||
this.element = document.createElement("div");
|
this.element = document.createElement("div");
|
||||||
|
|
||||||
@ -28,7 +26,7 @@ export class ConnectionView {
|
|||||||
this.connection.connectTo(url);
|
this.connection.connectTo(url);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.connection.close();
|
this.connection.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onConnectionOpened() {
|
onConnectionOpened() {
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
/* eslint no-console: "off" */
|
|
||||||
|
|
||||||
export class FileSystemHandler {
|
|
||||||
|
|
||||||
constructor(filesystem, connection) {
|
|
||||||
this._fs = filesystem;
|
|
||||||
this._connection = connection;
|
|
||||||
this._connection.onmessage = (message) => {
|
|
||||||
this._onmessage(message);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_onmessage(message) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
let request = JSON.parse(message.data);
|
|
||||||
let result = -42;
|
|
||||||
let response;
|
|
||||||
|
|
||||||
console.log(request);
|
|
||||||
if (("string" === typeof(request.method)) &&
|
|
||||||
("number" === typeof(request.id)) &&
|
|
||||||
(request.params)) {
|
|
||||||
switch(request.method)
|
|
||||||
{
|
|
||||||
case "lookup":
|
|
||||||
{
|
|
||||||
const parent = request.params[0];
|
|
||||||
const name = request.params[1];
|
|
||||||
result = this._fs.lookup(parent, name);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "getattr":
|
|
||||||
{
|
|
||||||
const inode = request.params[0];
|
|
||||||
result = this._fs.getattr(inode);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "readdir":
|
|
||||||
{
|
|
||||||
const inode = request.params[0];
|
|
||||||
result = this._fs.readdir(inode);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "open":
|
|
||||||
{
|
|
||||||
const inode = request.params[0];
|
|
||||||
const mode = request.params[1];
|
|
||||||
result = this._fs.open(inode, mode);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "close":
|
|
||||||
{
|
|
||||||
const inode = request.params[0];
|
|
||||||
const handle = request.params[1];
|
|
||||||
const mode = request.params[2];
|
|
||||||
this._fs.close(inode, handle, mode);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "read":
|
|
||||||
{
|
|
||||||
const inode = request.params[0];
|
|
||||||
const handle = request.params[1];
|
|
||||||
const offset = request.params[2];
|
|
||||||
const length = request.params[3];
|
|
||||||
result = this._fs.read(inode, handle, offset, length);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("number" == typeof(request.id))
|
|
||||||
{
|
|
||||||
if ("number" !== typeof(result)) {
|
|
||||||
response = {result: result, id: request.id};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
response = {error: {code: result}, id: request.id};
|
|
||||||
}
|
|
||||||
console.log(response);
|
|
||||||
this._connection.send(JSON.stringify(response));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ex) { console.log(ex, message); }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +1,31 @@
|
|||||||
/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */
|
/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */
|
||||||
|
|
||||||
export class FileSystem {
|
import { BadState } from "./wsfs/bad_state.js";
|
||||||
static get GOOD() { return 0; }
|
import { FileMode } from "./wsfs/file_mode.js";
|
||||||
static get BAD() { return 1; }
|
import { Provider } from "./wsfs/provider.js";
|
||||||
|
|
||||||
static get BAD_NOTIMPLEMENTED() { return 2; }
|
|
||||||
static get BAD_TIMEOUT() { return 3; }
|
|
||||||
static get BAD_FORMAT() { return 4; }
|
|
||||||
|
|
||||||
static get BAD_NOENTRY() { return 101; }
|
|
||||||
static get BAD_NOACCESS() { return 102; }
|
|
||||||
|
|
||||||
static get O_ACCMODE() { return 0x003; }
|
|
||||||
static get O_RDONLY() { return 0x000; }
|
|
||||||
static get O_WRONLY() { return 0x001; }
|
|
||||||
static get O_RDWR() { return 0x002; }
|
|
||||||
static get O_CREAT() { return 0x040; }
|
|
||||||
static get O_EXCL() { return 0x080; }
|
|
||||||
static get O_TRUNK() { return 0x200; }
|
|
||||||
static get O_APPEND() { return 0x400; }
|
|
||||||
|
|
||||||
|
export class FileSystemProvider extends Provider {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
|
super();
|
||||||
|
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this._inodes = { };
|
this._inodes = { };
|
||||||
|
|
||||||
this._walk(this.root, (entry) => { this._inodes[entry.inode] = entry; });
|
this._walk(this.root, (entry) => { this._inodes[entry.inode] = entry; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setView(view) {
|
||||||
|
this._view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
connected() {
|
||||||
|
if (this._view) { this._view.onConnectionOpened(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnected() {
|
||||||
|
if (this._view) { this._view.onConnectionClosed(); }
|
||||||
|
}
|
||||||
|
|
||||||
_walk(node, callback) {
|
_walk(node, callback) {
|
||||||
callback(node);
|
callback(node);
|
||||||
|
|
||||||
@ -39,7 +38,7 @@ export class FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
lookup(parent, name) {
|
async lookup(parent, name) {
|
||||||
const parentEntry = this._inodes[parent];
|
const parentEntry = this._inodes[parent];
|
||||||
const entry = (parentEntry && parentEntry.entries && parentEntry.entries[name]) || null;
|
const entry = (parentEntry && parentEntry.entries && parentEntry.entries[name]) || null;
|
||||||
if (entry) {
|
if (entry) {
|
||||||
@ -54,12 +53,12 @@ export class FileSystem {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return FileSystem.BAD_NOENTRY;
|
throw new BadState(BadState.NO_ENTRY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getattr(inode) {
|
async getattr(inode) {
|
||||||
let entry = this._inodes[inode];
|
let entry = this._inodes[inode];
|
||||||
if (entry) {
|
if (entry) {
|
||||||
return {
|
return {
|
||||||
@ -72,16 +71,15 @@ export class FileSystem {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return FileSystem.BAD_NOENTRY;
|
throw new BadState(BadState.NO_ENTRY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readdir(inode) {
|
async readdir(inode) {
|
||||||
let result = FileSystem.BAD_NOENTRY;
|
|
||||||
let entry = this._inodes[inode];
|
let entry = this._inodes[inode];
|
||||||
|
|
||||||
if ((entry) && ("dir" === entry.type)) {
|
if ((entry) && ("dir" === entry.type)) {
|
||||||
result = [
|
let result = [
|
||||||
{name: ".", inode: entry.inode},
|
{name: ".", inode: entry.inode},
|
||||||
{name: "..", inode: entry.inode}
|
{name: "..", inode: entry.inode}
|
||||||
];
|
];
|
||||||
@ -90,20 +88,29 @@ export class FileSystem {
|
|||||||
const inode = subdir[1].inode;
|
const inode = subdir[1].inode;
|
||||||
result.push({name, inode});
|
result.push({name, inode});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new BadState(BadState.NO_ENTRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open(inode, mode) {
|
async open(inode, mode) {
|
||||||
let result = FileSystem.BAD_NOENTRY;
|
|
||||||
let entry = this._inodes[inode];
|
let entry = this._inodes[inode];
|
||||||
|
|
||||||
if (entry.type === "file") {
|
if (entry.type === "file") {
|
||||||
result = ((mode & FileSystem.O_ACCMODE) === FileSystem.O_RDONLY) ? {handle: 1337} : FileSystem.BAD_NOACCESS;
|
if ((mode & FileMode.ACCESS_MODE) === FileMode.READONLY) {
|
||||||
}
|
return {handle: 1337};
|
||||||
|
}
|
||||||
return result;
|
else {
|
||||||
|
throw new BadState(BadState.NO_ACCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new BadState(BadState.NO_ENTRY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close(_inode, _handle, _mode) {
|
close(_inode, _handle, _mode) {
|
||||||
@ -111,20 +118,17 @@ export class FileSystem {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
read(inode, handle, offset, length) {
|
async read(inode, handle, offset, length) {
|
||||||
let result = FileSystem.BAD_NOENTRY;
|
|
||||||
let entry = this._inodes[inode];
|
let entry = this._inodes[inode];
|
||||||
|
|
||||||
if (entry.type === "file") {
|
if (entry.type === "file") {
|
||||||
let end = Math.min(offset + length, entry.contents.length);
|
const end = Math.min(offset + length, entry.contents.length);
|
||||||
let data = (offset < entry.contents.length) ? entry.contents.substring(offset, end) : "";
|
const data = (offset < entry.contents.length) ? entry.contents.substring(offset, end) : "";
|
||||||
result = {
|
|
||||||
data: btoa(data),
|
|
||||||
format: "base64",
|
|
||||||
count: data.length
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return data;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new BadState(BadState.NO_ENTRY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
import { Connection } from "./connection.js";
|
import { Client } from "./wsfs/client.js";
|
||||||
import { ConnectionView } from "./connection_view.js";
|
import { ConnectionView } from "./connection_view.js";
|
||||||
import { FileSystem } from "./filesystem.js";
|
import { FileSystemProvider } from "./filesystem_provider.js";
|
||||||
import { FileSystemHandler } from "./filesystem_handler.js";
|
|
||||||
|
|
||||||
|
|
||||||
function mode(value) {
|
function mode(value) {
|
||||||
@ -9,11 +8,7 @@ function mode(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function startup() {
|
function startup() {
|
||||||
let connection = new Connection();
|
let provider = new FileSystemProvider({
|
||||||
let connectionView = new ConnectionView(connection);
|
|
||||||
document.getElementById('connection').appendChild(connectionView.element);
|
|
||||||
|
|
||||||
let fs = new FileSystem({
|
|
||||||
inode: 1,
|
inode: 1,
|
||||||
mode: mode("0755"),
|
mode: mode("0755"),
|
||||||
type: "dir",
|
type: "dir",
|
||||||
@ -22,8 +17,10 @@ function startup() {
|
|||||||
"say_hello.sh": { inode: 3, mode: mode("0555"), type: "file", contents: "#!/bin/sh\necho hello\n"}
|
"say_hello.sh": { inode: 3, mode: mode("0555"), type: "file", contents: "#!/bin/sh\necho hello\n"}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let client = new Client(provider);
|
||||||
let handler = new FileSystemHandler(fs, connection);
|
let connectionView = new ConnectionView(client);
|
||||||
|
document.getElementById('connection').appendChild(connectionView.element);
|
||||||
|
provider.setView(connectionView);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onload = startup;
|
window.onload = startup;
|
||||||
|
15
example/daemon/www/js/wsfs/bad_state.js
Normal file
15
example/daemon/www/js/wsfs/bad_state.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export class BadState extends Error {
|
||||||
|
static get BAD() { return 1; }
|
||||||
|
|
||||||
|
static get NOT_IMPLEMENTED() { return 2; }
|
||||||
|
static get TIMEOUT() { return 3; }
|
||||||
|
static get FORMAT() { return 4; }
|
||||||
|
|
||||||
|
static get NO_ENTRY() { return 101; }
|
||||||
|
static get NO_ACCESS() { return 102; }
|
||||||
|
|
||||||
|
constructor(code) {
|
||||||
|
super("Bad State");
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
}
|
156
example/daemon/www/js/wsfs/client.js
Normal file
156
example/daemon/www/js/wsfs/client.js
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import { BadState } from "./bad_state.js";
|
||||||
|
|
||||||
|
export class Client {
|
||||||
|
static get _PROTOCOL() { return "fs"; }
|
||||||
|
|
||||||
|
constructor(provider) {
|
||||||
|
this._provider = provider;
|
||||||
|
this._ws = null;
|
||||||
|
this.isConnected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
connectTo(url) {
|
||||||
|
this.disconnect();
|
||||||
|
|
||||||
|
this._ws = new WebSocket(url, Client._PROTOCOL);
|
||||||
|
this._ws.onopen = () => {
|
||||||
|
this.isConnected = true;
|
||||||
|
this._provider.connected();
|
||||||
|
};
|
||||||
|
this._ws.onclose = () => {
|
||||||
|
this.isConnected = false;
|
||||||
|
this._provider.disconnected();
|
||||||
|
};
|
||||||
|
this._ws.onmessage = (message) => {
|
||||||
|
this._onmessage(message);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect() {
|
||||||
|
if (this._ws) {
|
||||||
|
this._ws.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onmessage(message) {
|
||||||
|
try {
|
||||||
|
const request = JSON.parse(message.data);
|
||||||
|
const method = request.method;
|
||||||
|
const id = request.id;
|
||||||
|
const params = request.params;
|
||||||
|
|
||||||
|
if ("string" !== typeof(method)) {
|
||||||
|
throw new Error("parse error: missing field: \"method\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params) {
|
||||||
|
throw new Error("parse error: missing field: \"params\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("number" === typeof(request.id)) {
|
||||||
|
this._invoke(method, params, id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._notify(method, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
// swallow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_invoke(method, params, id) {
|
||||||
|
this._invokeAsync(method, params).then((result) => {
|
||||||
|
const response = { result, id };
|
||||||
|
this._ws.send(JSON.stringify(response));
|
||||||
|
}).
|
||||||
|
catch((ex) => {
|
||||||
|
const code = ex.code || BadState.BAD;
|
||||||
|
const response = {error: {code}, id};
|
||||||
|
this._ws.send(JSON.stringify(response));
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async _invokeAsync(method, params) {
|
||||||
|
switch(method)
|
||||||
|
{
|
||||||
|
case "lookup":
|
||||||
|
return this._lookup(params);
|
||||||
|
case "getattr":
|
||||||
|
return this._getattr(params);
|
||||||
|
case "readdir":
|
||||||
|
return this._readdir(params);
|
||||||
|
case "open":
|
||||||
|
return this._open(params);
|
||||||
|
case "read":
|
||||||
|
return this._read(params);
|
||||||
|
default:
|
||||||
|
throw new BadState(BadState.NOT_IMPLEMENTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_notify(method, params) {
|
||||||
|
switch(method) {
|
||||||
|
case 'close':
|
||||||
|
this._close(params);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Invalid method: "${method}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _lookup(params) {
|
||||||
|
const parent = params[0];
|
||||||
|
const name = params[1];
|
||||||
|
|
||||||
|
return this._provider.lookup(parent, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _getattr(params) {
|
||||||
|
const inode = params[0];
|
||||||
|
|
||||||
|
return this._provider.getattr(inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _readdir(params) {
|
||||||
|
const inode = params[0];
|
||||||
|
|
||||||
|
return this._provider.readdir(inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _open(params) {
|
||||||
|
const inode = params[0];
|
||||||
|
const mode = params[1];
|
||||||
|
|
||||||
|
return this._provider.open(inode, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
_close(params) {
|
||||||
|
const inode = params[0];
|
||||||
|
const handle = params[1];
|
||||||
|
const mode = params[2];
|
||||||
|
|
||||||
|
this._provider.close(inode, handle, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _read(params) {
|
||||||
|
const inode = params[0];
|
||||||
|
const handle = params[1];
|
||||||
|
const offset = params[2];
|
||||||
|
const length = params[3];
|
||||||
|
|
||||||
|
const data = await this._provider.read(inode, handle, offset, length);
|
||||||
|
|
||||||
|
if ("string" === typeof(data)) {
|
||||||
|
return {
|
||||||
|
data: btoa(data),
|
||||||
|
format: "base64",
|
||||||
|
count: data.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new BadState(BadState.BAD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
example/daemon/www/js/wsfs/file_mode.js
Normal file
10
example/daemon/www/js/wsfs/file_mode.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
export class FileMode {
|
||||||
|
static get ACCESS_MODE() { return 0x003; }
|
||||||
|
static get READONLY() { return 0x000; }
|
||||||
|
static get WRITEONLY() { return 0x001; }
|
||||||
|
static get READWRITE() { return 0x002; }
|
||||||
|
static get CREATE() { return 0x040; }
|
||||||
|
static get EXCLUSIVE() { return 0x080; }
|
||||||
|
static get TRUNKATE() { return 0x200; }
|
||||||
|
static get APPEND() { return 0x400; }
|
||||||
|
}
|
38
example/daemon/www/js/wsfs/provider.js
Normal file
38
example/daemon/www/js/wsfs/provider.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/* eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }] */
|
||||||
|
|
||||||
|
import { BadState } from "./bad_state.js";
|
||||||
|
|
||||||
|
export class Provider {
|
||||||
|
|
||||||
|
connected() {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnected() {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
async lookup(_parent, _name) {
|
||||||
|
throw new BadState(BadState.NOT_IMPLEMENTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getattr(_inode) {
|
||||||
|
throw new BadState(BadState.NOT_IMPLEMENTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
async readdir(_inode) {
|
||||||
|
throw new BadState(BadState.NOT_IMPLEMENTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
async open(_inode, _mode) {
|
||||||
|
throw new BadState(BadState.NOT_IMPLEMENTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(_inode, _handle, _mode) {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
async read(_inode, _handle, _offset, _length) {
|
||||||
|
throw new BadState(BadState.NOT_IMPLEMENTED);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user