const __openFiles = Object.create(null);
/**
- Map of dir names to FileSystemDirectoryHandle objects.
+ Expects an OPFS file path. It gets resolved, such that ".."
+ components are properly expanded, and returned. If the 2nd
+ are is true, it's returned as an array of path elements,
+ else it's returned as an absolute path string.
*/
-const __dirCache = new Map;
+const getResolvedPath = function(filename,splitIt){
+ const p = new URL(
+ filename, 'file://irrelevant'
+ ).pathname;
+ return splitIt ? p.split('/').filter((v)=>!!v) : p;
+}
/**
Takes the absolute path to a filesystem element. Returns an array
along the way. Throws if any creation or resolution fails.
*/
const getDirForPath = async function f(absFilename, createDirs = false){
- const url = new URL(
- absFilename, 'file://xyz'
- ) /* use URL to resolve path pieces such as a/../b */;
- const path = url.pathname.split('/').filter((v)=>!!v);
+ const path = getResolvedPath(absFilename, true);
const filename = path.pop();
- const allDirs = '/'+path.join('/');
- let dh = __dirCache.get(allDirs);
- if(!dh){
- dh = state.rootDir;
- for(const dirName of path){
- if(dirName){
- dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs});
- }
+ let dh = state.rootDir;
+ for(const dirName of path){
+ if(dirName){
+ dh = await dh.getDirectoryHandle(dirName, {create: !!createDirs});
}
- __dirCache.set(allDirs, dh);
}
return [dh, filename];
};
Atomics.notify(state.opSABView, state.opIds[opName]);
};
-const isInt32 = function(n){
- return ('bigint'!==typeof n /*TypeError: can't convert BigInt to number*/)
- && !!(n===(n|0) && n<=2147483647 && n>=-2147483648);
-};
-const affirm32Bits = function(n){
- return isInt32(n) || toss("Number is too large (>31 bits) (FIXME!):",n);
-};
-
/**
Throws if fh is a file-holding object which is flagged as read-only.
*/
to simplify finding them.
*/
const vfsAsyncImpls = {
- xAccess: async function({filename, exists, readWrite}){
- warn("xAccess(",arguments[0],") is TODO");
- const rc = state.sq3Codes.SQLITE_IOERR;
+ xAccess: async function(filename){
+ log("xAccess(",arguments[0],")");
+ /* OPFS cannot support the full range of xAccess() queries sqlite3
+ calls for. We can essentially just tell if the file is
+ accessible, but if it is it's automatically writable (unless
+ it's locked, which we cannot(?) know without trying to open
+ it). OPFS does not have the notion of read-only.
+
+ The return semantics of this function differ from sqlite3's
+ xAccess semantics because we are limited in what we can
+ communicate back to our synchronous communication partner: 0 =
+ accessible, non-0 means not accessible.
+ */
+ let rc = 0;
+ try{
+ const [dh, fn] = await getDirForPath(filename);
+ await dh.getFileHandle(fn);
+ }catch(e){
+ rc = state.sq3Codes.SQLITE_IOERR;
+ }
storeAndNotify('xAccess', rc);
},
xClose: async function(fid){
}
},
xDelete: async function({filename, syncDir/*ignored*/}){
+ /* The syncDir flag is, for purposes of the VFS API's semantics,
+ ignored here. However, if it has the value 0x1234 then: after
+ deleting the given file, recursively try to delete any empty
+ directories left behind in its wake (ignoring any errors and
+ stopping at the first failure).
+
+ That said: we don't know for sure that removeEntry() fails if
+ the dir is not empty because the API is not documented. It has,
+ however, a "recursive" flag which defaults to false, so
+ presumably it will fail if the dir is not empty and that flag
+ is false.
+ */
log("xDelete(",arguments[0],")");
try {
- const [hDir, filenamePart] = await getDirForPath(filename, false);
- await hDir.removeEntry(filenamePart);
+ while(filename){
+ const [hDir, filenamePart] = await getDirForPath(filename, false);
+ //log("Removing:",hDir, filenamePart);
+ if(!filenamePart) break;
+ await hDir.removeEntry(filenamePart);
+ if(0x1234 !== syncDir) break;
+ filename = getResolvedPath(filename, true);
+ filename.pop();
+ filename = filename.join('/');
+ }
}catch(e){
- /* Ignoring: _presumably_ the file can't be found. */
+ /* Ignoring: _presumably_ the file can't be found or a dir is
+ not empty. */
+ //error("Delete failed",filename, e.message);
}
storeAndNotify('xDelete', 0);
},
xWrite: async function({fid,src,n,offset}){
log("xWrite(",arguments[0],")");
let rc;
+ const fh = __openFiles[fid];
try{
- const fh = __openFiles[fid];
affirmNotRO('xWrite', fh);
const nOut = fh.accessHandle.write(new UInt8Array(fh.sab, 0, n), {at: offset});
rc = (nOut===n) ? 0 : state.sq3Codes.SQLITE_IOERR_WRITE;
}
};
wMsg('loaded');
-});
+}).catch((e)=>error(e));
warn("This file is very much experimental and under construction.",self.location.pathname);
if(self.window===self ||
- !self.SharedBufferArray ||
+ !self.SharedArrayBuffer ||
!self.FileSystemHandle ||
!self.FileSystemDirectoryHandle ||
!self.FileSystemFileHandle ||
!self.FileSystemFileHandle.prototype.createSyncAccessHandle ||
!navigator.storage.getDirectory){
warn("This environment does not have OPFS support.");
+ return;
}
const capi = sqlite3.capi;
are in alphabetical order to simplify finding them.
*/
const vfsSyncWrappers = {
- // TODO: xAccess
+ xAccess: function(pVfs,zName,flags,pOut){
+ const rc = opRun('xAccess', wasm.cstringToJs(zName));
+ wasm.setMemValue(pOut, rc ? 0 : 1, 'i32');
+ return 0;
+ },
xCurrentTime: function(pVfs,pOut){
/* If it turns out that we need to adjust for timezone, see:
https://stackoverflow.com/a/11760121/1458521 */
const fid = sq3File.pointer;
const openFlags = capi.SQLITE_OPEN_CREATE
| capi.SQLITE_OPEN_READWRITE
- | capi.SQLITE_OPEN_DELETEONCLOSE
+ //| capi.SQLITE_OPEN_DELETEONCLOSE
| capi.SQLITE_OPEN_MAIN_DB;
const pOut = wasm.scopedAlloc(8);
const dbFile = "/sanity/check/file";
- let rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, dbFile,
- fid, openFlags, pOut);
+ const zDbFile = wasm.scopedAllocCString(dbFile);
+ let rc;
+ vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
+ rc = wasm.getMemValue(pOut,'i32');
+ log("xAccess(",dbFile,") exists ?=",rc);
+ rc = vfsSyncWrappers.xOpen(opfsVfs.pointer, zDbFile,
+ fid, openFlags, pOut);
log("open rc =",rc,"state.opSABView[xOpen] =",state.opSABView[state.opIds.xOpen]);
if(isWorkerErrCode(rc)){
error("open failed with code",rc);
return;
}
+ vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
+ rc = wasm.getMemValue(pOut,'i32');
+ if(!rc) toss("xAccess() failed to detect file.");
rc = ioSyncWrappers.xSync(sq3File.pointer, 0);
if(rc) toss('sync failed w/ rc',rc);
rc = ioSyncWrappers.xTruncate(sq3File.pointer, 1024);
if(rc) toss('xFileSize failed w/ rc',rc);
log("xFileSize says:",wasm.getMemValue(pOut, 'i64'));
log("xSleep()ing before close()ing...");
- opRun('xSleep',1500);
+ opRun('xSleep',1000);
rc = ioSyncWrappers.xClose(fid);
log("xClose rc =",rc,"opSABView =",state.opSABView);
log("Deleting file:",dbFile);
- opRun('xDelete', dbFile);
+ vfsSyncWrappers.xDelete(opfsVfs.pointer, zDbFile, 0x1234);
+ vfsSyncWrappers.xAccess(opfsVfs.pointer, zDbFile, 0, pOut);
+ rc = wasm.getMemValue(pOut,'i32');
+ if(rc) toss("Expecting 0 from xAccess(",dbFile,") after xDelete().");
}finally{
sq3File.dispose();
wasm.scopedAllocPop(scope);
-C Generic\scleanups\sin\sthe\sOPFS\ssync/async\sproxy.
-D 2022-09-17T21:13:26.228
+C Implement\sOPFS\sxAccess(),\salbeit\swith\smore\slimited\ssemantics\sthan\sthe\sVFS\sAPI\scalls\sfor.\sAdd\sa\sway\sfor\sOPFS\sxDelete()\sto\soptionally\srecursively\sremove\sempty\sdirs\sleft\sover\safter\sdeleting\sa\sfile.
+D 2022-09-17T23:29:27.832
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x
F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d82675bd63d9c2d97a15f0
F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5
-F ext/wasm/sqlite3-opfs-async-proxy.js ceb5e3a190bfd42d25fc137b3aaf09adf6469b5f1e33528a64ce7ff74e5ef5b1
+F ext/wasm/sqlite3-opfs-async-proxy.js c4be9e614abea5f237564757141e3e128f0cff4b4cd8350f68e17347230fd34e
F ext/wasm/sqlite3-worker1-promiser.js 92b8da5f38439ffec459a8215775d30fa498bc0f1ab929ff341fc3dd479660b9
F ext/wasm/sqlite3-worker1.js 0c1e7626304543969c3846573e080c082bf43bcaa47e87d416458af84f340a9e
F ext/wasm/testing-worker1-promiser.html 6eaec6e04a56cf24cf4fa8ef49d78ce8905dde1354235c9125dca6885f7ce893
F ext/wasm/testing2.js 25584bcc30f19673ce13a6f301f89f8820a59dfe044e0c4f2913941f4097fe3c
F ext/wasm/wasmfs.make 21a5cf297954a689e0dc2a95299ae158f681cae5e90c10b99d986097815fd42d
F ext/wasm/x-sync-async.html 717b0d3bee96e49cbd36731bead497ab27a8bf3a3b23dd11e40e61d4ac9e8b80
-F ext/wasm/x-sync-async.js c99a6f98a8b0ccb43b2883a1bc1da42d0d84b26b5d4fd99d2f053ffe209a0a1f
+F ext/wasm/x-sync-async.js a95e8acb04fd69526317ee63bf25fa478a20e223d5dc16f7526baf5d81e07d1e
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 44db9132145b3072488ea91db53f6c06be74544beccad5fd07efd22c0f03dc04
-R 75c9aa6717389505aa78f157abdf6d44
+P f36bddbe54c3acbfaa958042e4d24724f130bdca551401033f9bc63f3da73492
+R 5aad77d6a3a0d677e9c84e63e20c4294
U stephan
-Z 6a0a0bcb470dbb44e9d0c87c09f1fedd
+Z 31fefe04f3f471aad99d738829acf9a8
# Remove this line to create a well-formed Fossil manifest.
-f36bddbe54c3acbfaa958042e4d24724f130bdca551401033f9bc63f3da73492
\ No newline at end of file
+c342b5d745f301104c59851c753287ebbbe95a69a56cb522d376d0f3e352c30f
\ No newline at end of file