mirror of
https://github.com/osnr/TabFS.git
synced 2024-06-08 00:42:16 +02:00
extension: fix truncate in defineFile; refactor/encap defineFile
truncate was still assuming fixed path instead of passing whole req object on to setData/getData.
This commit is contained in:
parent
383096da00
commit
8fbfa9a7c9
|
@ -61,89 +61,93 @@ const utf8ArrayToString = (function() {
|
||||||
return utf8 => decoder.decode(utf8);
|
return utf8 => decoder.decode(utf8);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const Cache = {
|
// Helper function: generates a full set of file operations that you
|
||||||
// used when you open a file to cache the content we got from the
|
// can use as a route handler (so clients can read and write
|
||||||
// browser until you close that file. (so we can respond to
|
// sections of the file, stat it to get its size and see it show up
|
||||||
// individual chunk read() and write() requests without doing a
|
// in ls, etc), given getData and setData functions that define the
|
||||||
// whole new conversation with the browser and regenerating the
|
// contents of the entire file.
|
||||||
// content -- important for taking a screenshot, for instance)
|
const defineFile = (function() {
|
||||||
store: {}, nextHandle: 0,
|
const Cache = {
|
||||||
storeObject(object) {
|
// used when you open a file to cache the content we got from the
|
||||||
const handle = ++this.nextHandle;
|
// browser until you close that file. (so we can respond to
|
||||||
this.store[handle] = object;
|
// individual chunk read() and write() requests without doing a
|
||||||
return handle;
|
// whole new conversation with the browser and regenerating the
|
||||||
},
|
// content -- important for taking a screenshot, for instance)
|
||||||
getObjectForHandle(handle) { return this.store[handle]; },
|
store: {}, nextHandle: 0,
|
||||||
setObjectForHandle(handle, object) { this.store[handle] = object; },
|
storeObject(object) {
|
||||||
removeObjectForHandle(handle) { delete this.store[handle]; }
|
const handle = ++this.nextHandle;
|
||||||
};
|
this.store[handle] = object;
|
||||||
function toUtf8Array(stringOrArray) {
|
return handle;
|
||||||
if (typeof stringOrArray == 'string') { return stringToUtf8Array(stringOrArray); }
|
},
|
||||||
else { return stringOrArray; }
|
getObjectForHandle(handle) { return this.store[handle]; },
|
||||||
}
|
setObjectForHandle(handle, object) { this.store[handle] = object; },
|
||||||
const defineFile = (getData, setData) => ({
|
removeObjectForHandle(handle) { delete this.store[handle]; }
|
||||||
// Generates a full set of file operations (so clients can read and
|
};
|
||||||
// write sections of the file, stat it to get its size and see it
|
|
||||||
// show up in ls, etc), given getData and setData functions that
|
|
||||||
// define the contents of the entire file.
|
|
||||||
|
|
||||||
// getData: (req: Request U Vars) -> Promise<contentsOfFile: String|Uint8Array>
|
function toUtf8Array(stringOrArray) {
|
||||||
// setData [optional]: (req: Request U Vars, newContentsOfFile: String) -> Promise<>
|
if (typeof stringOrArray == 'string') { return stringToUtf8Array(stringOrArray); }
|
||||||
|
else { return stringOrArray; }
|
||||||
// You can override file operations (like `truncate` or `getattr`)
|
|
||||||
// in the returned set if you want different behavior from what's
|
|
||||||
// defined here.
|
|
||||||
|
|
||||||
async getattr(req) {
|
|
||||||
return {
|
|
||||||
st_mode: unix.S_IFREG | 0444 | (setData ? 0222 : 0),
|
|
||||||
st_nlink: 1,
|
|
||||||
// you'll want to override this if getData() is slow, because
|
|
||||||
// getattr() gets called a lot more cavalierly than open().
|
|
||||||
st_size: toUtf8Array(await getData(req)).length
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
// We call getData() once when the file is opened, then cache that
|
|
||||||
// data for all subsequent reads from that application.
|
|
||||||
async open(req) {
|
|
||||||
const data = !(req.flags & unix.O_TRUNC) ? await getData(req) : "";
|
|
||||||
return { fh: Cache.storeObject(toUtf8Array(data)) };
|
|
||||||
},
|
|
||||||
async read({fh, size, offset}) {
|
|
||||||
return { buf: String.fromCharCode(...Cache.getObjectForHandle(fh).slice(offset, offset + size)) }
|
|
||||||
},
|
|
||||||
async write(req) {
|
|
||||||
const {fh, offset, buf} = req;
|
|
||||||
let arr = Cache.getObjectForHandle(fh);
|
|
||||||
const bufarr = stringToUtf8Array(buf);
|
|
||||||
if (offset + bufarr.length > arr.length) {
|
|
||||||
const newArr = new Uint8Array(offset + bufarr.length);
|
|
||||||
newArr.set(arr.slice(0, Math.min(offset, arr.length)));
|
|
||||||
arr = newArr;
|
|
||||||
Cache.setObjectForHandle(fh, arr);
|
|
||||||
}
|
|
||||||
arr.set(bufarr, offset);
|
|
||||||
// I guess caller should override write() if they want to actually
|
|
||||||
// patch and not just re-set the whole string (for example,
|
|
||||||
// if they want to hot-reload just one function the user modified)
|
|
||||||
await setData(req, utf8ArrayToString(arr)); return { size: bufarr.length };
|
|
||||||
},
|
|
||||||
async release({fh}) { Cache.removeObjectForHandle(fh); return {}; },
|
|
||||||
|
|
||||||
async truncate({path, size}) {
|
|
||||||
// TODO: weird case if they truncate while the file is open
|
|
||||||
// (but `echo hi > foo.txt`, the main thing I care about, uses
|
|
||||||
// O_TRUNC which thankfully doesn't do that)
|
|
||||||
let arr = toUtf8Array(await getData(path));
|
|
||||||
if (size !== arr.length) {
|
|
||||||
const newArr = new Uint8Array(size);
|
|
||||||
newArr.set(arr.slice(0, Math.min(size, arr.length)));
|
|
||||||
arr = newArr;
|
|
||||||
}
|
|
||||||
await setData(path, utf8ArrayToString(arr)); return {};
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
return (getData, setData) => ({
|
||||||
|
// getData: (req: Request U Vars) -> Promise<contentsOfFile: String|Uint8Array>
|
||||||
|
// setData [optional]: (req: Request U Vars, newContentsOfFile: String) -> Promise<>
|
||||||
|
|
||||||
|
// You can override file operations (like `truncate` or `getattr`)
|
||||||
|
// in the returned set if you want different behavior from what's
|
||||||
|
// defined here.
|
||||||
|
|
||||||
|
async getattr(req) {
|
||||||
|
return {
|
||||||
|
st_mode: unix.S_IFREG | 0444 | (setData ? 0222 : 0),
|
||||||
|
st_nlink: 1,
|
||||||
|
// you'll want to override this if getData() is slow, because
|
||||||
|
// getattr() gets called a lot more cavalierly than open().
|
||||||
|
st_size: toUtf8Array(await getData(req)).length
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// We call getData() once when the file is opened, then cache that
|
||||||
|
// data for all subsequent reads from that application.
|
||||||
|
async open(req) {
|
||||||
|
const data = !(req.flags & unix.O_TRUNC) ? await getData(req) : "";
|
||||||
|
return { fh: Cache.storeObject(toUtf8Array(data)) };
|
||||||
|
},
|
||||||
|
async read({fh, size, offset}) {
|
||||||
|
return { buf: String.fromCharCode(...Cache.getObjectForHandle(fh).slice(offset, offset + size)) }
|
||||||
|
},
|
||||||
|
async write(req) {
|
||||||
|
const {fh, offset, buf} = req;
|
||||||
|
let arr = Cache.getObjectForHandle(fh);
|
||||||
|
const bufarr = stringToUtf8Array(buf);
|
||||||
|
if (offset + bufarr.length > arr.length) {
|
||||||
|
const newArr = new Uint8Array(offset + bufarr.length);
|
||||||
|
newArr.set(arr.slice(0, Math.min(offset, arr.length)));
|
||||||
|
arr = newArr;
|
||||||
|
Cache.setObjectForHandle(fh, arr);
|
||||||
|
}
|
||||||
|
arr.set(bufarr, offset);
|
||||||
|
// I guess caller should override write() if they want to actually
|
||||||
|
// patch and not just re-set the whole string (for example,
|
||||||
|
// if they want to hot-reload just one function the user modified)
|
||||||
|
await setData(req, utf8ArrayToString(arr)); return { size: bufarr.length };
|
||||||
|
},
|
||||||
|
async release({fh}) { Cache.removeObjectForHandle(fh); return {}; },
|
||||||
|
|
||||||
|
async truncate(req) {
|
||||||
|
// TODO: weird case if they truncate while the file is open
|
||||||
|
// (but `echo hi > foo.txt`, the main thing I care about, uses
|
||||||
|
// O_TRUNC which thankfully doesn't do that)
|
||||||
|
let arr = toUtf8Array(await getData(req));
|
||||||
|
if (req.size !== arr.length) {
|
||||||
|
const newArr = new Uint8Array(req.size);
|
||||||
|
newArr.set(arr.slice(0, Math.min(req.size, arr.length)));
|
||||||
|
arr = newArr;
|
||||||
|
}
|
||||||
|
await setData(req, utf8ArrayToString(arr)); return {};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
const Routes = {};
|
const Routes = {};
|
||||||
|
|
||||||
|
@ -183,10 +187,12 @@ Routes["/tabs/by-id"] = {
|
||||||
|
|
||||||
// echo true > mnt/tabs/by-id/1644/active
|
// echo true > mnt/tabs/by-id/1644/active
|
||||||
// cat mnt/tabs/by-id/1644/active
|
// cat mnt/tabs/by-id/1644/active
|
||||||
Routes["/tabs/by-id/#TAB_ID/active"] = withTab(tab => JSON.stringify(tab.active) + '\n',
|
Routes["/tabs/by-id/#TAB_ID/active"] = withTab(
|
||||||
// WEIRD: we do startsWith because you might end up with buf
|
tab => JSON.stringify(tab.active) + '\n',
|
||||||
// being "truee" (if it was "false", then someone wrote "true")
|
// WEIRD: we do startsWith because you might end up with buf
|
||||||
buf => ({ active: buf.startsWith("true") }));
|
// being "truee" (if it was "false", then someone wrote "true")
|
||||||
|
buf => ({ active: buf.startsWith("true") })
|
||||||
|
);
|
||||||
})();
|
})();
|
||||||
(function() {
|
(function() {
|
||||||
const evals = {};
|
const evals = {};
|
||||||
|
|
Loading…
Reference in a new issue