diff --git a/src/lib/libidbfs.js b/src/lib/libidbfs.js index b33a19e9ce8c2..036e52fd9126c 100644 --- a/src/lib/libidbfs.js +++ b/src/lib/libidbfs.js @@ -180,7 +180,7 @@ addToLibrary({ var stat; try { - stat = FS.stat(path); + stat = FS.lstat(path); } catch (e) { return callback(e); } @@ -232,13 +232,15 @@ addToLibrary({ try { var lookup = FS.lookupPath(path); node = lookup.node; - stat = FS.stat(path); + stat = FS.lstat(path); } catch (e) { return callback(e); } if (FS.isDir(stat.mode)) { return callback(null, { 'timestamp': stat.mtime, 'mode': stat.mode }); + } else if (FS.isLink(stat.mode)) { + return callback(null, { 'timestamp': stat.mtime, 'mode': stat.mode, 'link': node.link, }); } else if (FS.isFile(stat.mode)) { // Performance consideration: storing a normal JavaScript array to a IndexedDB is much slower than storing a typed array. // Therefore always convert the file contents to a typed array first before writing the data to IndexedDB. @@ -252,6 +254,8 @@ addToLibrary({ try { if (FS.isDir(entry['mode'])) { FS.mkdirTree(path, entry['mode']); + } else if (FS.isLink(entry['mode'])) { + FS.symlink(entry['link'], path); } else if (FS.isFile(entry['mode'])) { FS.writeFile(path, entry['contents'], { canOwn: true }); } else { @@ -268,11 +272,11 @@ addToLibrary({ }, removeLocalEntry: (path, callback) => { try { - var stat = FS.stat(path); + var stat = FS.lstat(path); if (FS.isDir(stat.mode)) { FS.rmdir(path); - } else if (FS.isFile(stat.mode)) { + } else { FS.unlink(path); } } catch (e) { diff --git a/test/fs/test_idbfs_autopersist.c b/test/fs/test_idbfs_autopersist.c index 997f9d3fc7614..fdb330c033271 100644 --- a/test/fs/test_idbfs_autopersist.c +++ b/test/fs/test_idbfs_autopersist.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -87,8 +88,26 @@ static void test_case_symlink(void) { } case 3: { struct stat st; + + // lstat should report S_IFLNK int res = lstat("/working1/symlink", &st); assert(res == 0); + int fmt = st.st_mode & S_IFMT; + printf("lstat => %d\n", fmt); + assert(fmt == S_IFLNK); + + // stat should report S_IFREG + res = stat("/working1/symlink", &st); + assert(res == 0); + fmt = st.st_mode & S_IFMT; + printf("stat => %d\n", fmt); + assert(fmt == S_IFREG); + + // Verify value returned by readlink + char buf[256]; + readlink("/working1/symlink", buf, 256); + printf("readlink => %s\n", buf); + assert(strcmp("/working1/file", buf) == 0); break; } default: @@ -190,6 +209,7 @@ void test(void) { } int main(void) { + printf("Running test case=%d phase=%d\n", TEST_CASE, TEST_PHASE); EM_ASM({ globalThis.runOnceIDBFSIdle = (callback) => { const { mount } = FS.lookupPath('/working1').node; @@ -214,7 +234,7 @@ int main(void) { // Erase persisted state by overwriting the contents of IndexedDB // with our empty in-memory filesystem. FS.syncfs(false, (err) => { - assert(!err); + assert(!err, 'syncfs failed'); callUserCallback(_test); }); }); @@ -223,7 +243,7 @@ int main(void) { // All subsequent phases rely on the effects of phases before them. // Load the persisted filesystem from IndexedDB into memory. FS.syncfs(true, (err) => { - assert(!err); + assert(!err, 'syncfs failed'); // FS.syncfs() may run operations on the in-memory filesystem which // might trigger IDBFS.queuePersist() calls. These queued calls will