66#include < algorithm>
77#include < atomic>
88#include < cctype>
9+ #include < cstdlib>
910#include < cstring>
1011#include < string>
1112#include < string_view>
13+ #include < vector>
1214
1315namespace node {
1416namespace sqlite {
@@ -45,6 +47,7 @@ void DecodeUriComponent(std::string_view component, std::string* decoded) {
4547
4648bool GetSQLiteUriParameter (std::string_view path,
4749 std::string_view parameter,
50+ bool use_last,
4851 std::string* value) {
4952 if (!path.starts_with (" file:" )) return false ;
5053
@@ -62,6 +65,7 @@ bool GetSQLiteUriParameter(std::string_view path,
6265 size_t parameter_start = query_start + 1 ;
6366 std::string decoded_key;
6467 std::string decoded_value;
68+ bool found = false ;
6569 while (parameter_start <= query_end) {
6670 size_t parameter_end = path.find (' &' , parameter_start);
6771 if (parameter_end == std::string_view::npos || parameter_end > query_end) {
@@ -83,13 +87,25 @@ bool GetSQLiteUriParameter(std::string_view path,
8387 &decoded_value);
8488 *value = decoded_value;
8589 }
86- return true ;
90+ found = true ;
91+ if (!use_last) return true ;
8792 }
8893
8994 if (parameter_end == query_end) break ;
9095 parameter_start = parameter_end + 1 ;
9196 }
92- return false ;
97+ return found;
98+ }
99+
100+ bool EqualsIgnoreCase (std::string_view left, std::string_view right) {
101+ if (left.size () != right.size ()) return false ;
102+ for (size_t i = 0 ; i < left.size (); ++i) {
103+ if (std::tolower (static_cast <unsigned char >(left[i])) !=
104+ std::tolower (static_cast <unsigned char >(right[i]))) {
105+ return false ;
106+ }
107+ }
108+ return true ;
93109}
94110
95111bool IsSidecarPath (std::string_view path, std::string* database_path) {
@@ -113,6 +129,25 @@ bool IsSidecarPath(std::string_view path, std::string* database_path) {
113129 return false ;
114130}
115131
132+ sqlite3_filename CreateReadOnlyShmFilename (sqlite3_filename name) {
133+ std::vector<const char *> parameters = {" readonly_shm" , " 1" };
134+ for (int index = 0 ;; ++index) {
135+ const char * key = sqlite3_uri_key (name, index);
136+ if (key == nullptr ) break ;
137+ if (strcmp (key, " readonly_shm" ) == 0 ) continue ;
138+
139+ const char * value = sqlite3_uri_parameter (name, key);
140+ parameters.push_back (key);
141+ parameters.push_back (value == nullptr ? " " : value);
142+ }
143+
144+ return sqlite3_create_filename (sqlite3_filename_database (name),
145+ sqlite3_filename_journal (name),
146+ sqlite3_filename_wal (name),
147+ static_cast <int >(parameters.size () / 2 ),
148+ parameters.data ());
149+ }
150+
116151std::string CanonicalPath (SQLitePermissionVFS* vfs, const char * path) {
117152 if (path == nullptr ) return {};
118153
@@ -133,9 +168,11 @@ std::string CanonicalPath(SQLitePermissionVFS* vfs, const char* path) {
133168struct PermissionFile {
134169 sqlite3_file base;
135170 SQLitePermissionVFS* vfs;
171+ sqlite3_filename parent_name;
136172 bool can_read;
137173 bool can_write;
138174 bool can_write_sidecar;
175+ bool read_only_shm;
139176 sqlite3_io_methods methods;
140177};
141178
@@ -203,6 +240,8 @@ int PermissionClose(sqlite3_file* file) {
203240 const int result = real->pMethods == nullptr
204241 ? SQLITE_OK
205242 : real->pMethods ->xClose (real);
243+ sqlite3_free_filename (permission_file->parent_name );
244+ permission_file->parent_name = nullptr ;
206245 file->pMethods = nullptr ;
207246 return result;
208247}
@@ -321,8 +360,12 @@ int PermissionShmMap(sqlite3_file* file,
321360 if (!HasReadPermission (permission_file)) return SQLITE_PERM ;
322361
323362 // OS VFS implementations can open or create the SHM file read-write even
324- // when extend is false.
325- if (!permission_file->can_write_sidecar ) return SQLITE_PERM ;
363+ // when extend is false. A read-only clone of the SQLite filename is used
364+ // when the caller has no permission to write sidecars.
365+ if (!permission_file->can_write_sidecar &&
366+ !permission_file->read_only_shm ) {
367+ return SQLITE_PERM ;
368+ }
326369
327370 sqlite3_file* real = RealFile (permission_file);
328371 if (real->pMethods ->xShmMap == nullptr ) return SQLITE_NOTFOUND ;
@@ -468,14 +511,27 @@ const char* PermissionVfsNextSystemCall(sqlite3_vfs* vfs, const char* name) {
468511
469512bool HasSQLiteVfsUriParameter (std::string_view path) {
470513 std::string value;
471- return GetSQLiteUriParameter (path, " vfs" , &value);
514+ return GetSQLiteUriParameter (path, " vfs" , false , &value);
472515}
473516
474517bool SQLiteUriParameterEquals (std::string_view path,
475518 std::string_view parameter,
476519 std::string_view value) {
477520 std::string actual;
478- return GetSQLiteUriParameter (path, parameter, &actual) && actual == value;
521+ return GetSQLiteUriParameter (path, parameter, true , &actual) &&
522+ actual == value;
523+ }
524+
525+ bool SQLiteUriBooleanParameter (std::string_view path,
526+ std::string_view parameter) {
527+ std::string value;
528+ if (!GetSQLiteUriParameter (path, parameter, false , &value)) return false ;
529+
530+ if (!value.empty () && std::isdigit (static_cast <unsigned char >(value[0 ]))) {
531+ return std::strtoll (value.c_str (), nullptr , 10 ) != 0 ;
532+ }
533+ return EqualsIgnoreCase (value, " on" ) || EqualsIgnoreCase (value, " yes" ) ||
534+ EqualsIgnoreCase (value, " true" );
479535}
480536
481537std::string SQLitePathForPermission (std::string_view path) {
@@ -516,12 +572,15 @@ std::string SQLitePathForPermission(std::string_view path) {
516572
517573SQLitePermissionVFS::SQLitePermissionVFS (Environment* env)
518574 : parent_(sqlite3_vfs_find(nullptr )),
575+ memory_vfs_ (sqlite3_vfs_find(" memdb" )),
519576 permission_(env->permission ()),
520577 name_(" node-permission-" + std::to_string(++next_vfs_id)) {
521578 CHECK_NOT_NULL (parent_);
579+ CHECK_NOT_NULL (memory_vfs_);
522580
523581 vfs_.iVersion = parent_->iVersion ;
524- vfs_.szOsFile = sizeof (PermissionFile) + parent_->szOsFile ;
582+ vfs_.szOsFile = sizeof (PermissionFile) +
583+ std::max (parent_->szOsFile , memory_vfs_->szOsFile );
525584 vfs_.mxPathname = parent_->mxPathname ;
526585 vfs_.zName = name_.c_str ();
527586 vfs_.pAppData = this ;
@@ -585,18 +644,29 @@ bool SQLitePermissionVFS::AllowsPath(std::string_view path,
585644 candidate));
586645 };
587646
588- if (allowed (canonical)) return true ;
647+ return allowed (canonical);
648+ }
649+
650+ bool SQLitePermissionVFS::AllowsSidecarPath (std::string_view path,
651+ bool read,
652+ bool write) const {
653+ if (AllowsPath (path, read, write)) return true ;
654+
655+ const std::string canonical = CanonicalPath (
656+ const_cast <SQLitePermissionVFS*>(this ), std::string (path).c_str ());
657+ if (canonical.empty ()) return false ;
589658
590659 std::string database_path;
591- return IsSidecarPath (canonical, &database_path) && allowed (database_path);
660+ return IsSidecarPath (canonical, &database_path) &&
661+ AllowsPath (database_path, read, write);
592662}
593663
594664bool SQLitePermissionVFS::AllowsTemporaryFile (bool read, bool write) const {
595665 if (permission_->warning_only ()) return true ;
596666
597- // SQLite does not provide a pathname for anonymous temporary files. Only
598- // allow them when the corresponding global permission is granted; a
599- // path-specific grant cannot safely authorize an unknown temporary path .
667+ // SQLite does not provide a pathname for anonymous temporary files. Disk
668+ // storage is allowed only with global permissions. Otherwise xOpen uses
669+ // SQLite's memory VFS .
600670 return (!read || permission_->is_granted_no_side_effects (
601671 permission::PermissionScope::kFileSystemRead , " " )) &&
602672 (!write || permission_->is_granted_no_side_effects (
@@ -613,32 +683,66 @@ int SQLitePermissionVFS::Open(sqlite3_filename name,
613683
614684 const bool read =
615685 (flags & (SQLITE_OPEN_READONLY | SQLITE_OPEN_READWRITE )) != 0 ;
616- const bool write = (flags & SQLITE_OPEN_READWRITE ) != 0 ;
686+ const bool immutable = name != nullptr && (flags & SQLITE_OPEN_MAIN_DB ) &&
687+ sqlite3_uri_boolean (name, " immutable" , 0 ) != 0 ;
688+ bool write = (flags & SQLITE_OPEN_READWRITE ) != 0 && !immutable;
689+ int parent_flags = flags;
690+ sqlite3_vfs* selected_vfs = parent_;
617691 bool can_write_sidecar = write;
692+ bool read_only_shm = false ;
618693 bool allowed;
619694
620695 if (flags & SQLITE_OPEN_MEMORY ) {
696+ selected_vfs = memory_vfs_;
621697 allowed = true ;
622698 } else if (name == nullptr ) {
623699 allowed = AllowsTemporaryFile (read, write);
700+ if (!allowed) {
701+ selected_vfs = memory_vfs_;
702+ allowed = true ;
703+ }
624704 } else {
625705 const char * permission_name = name;
626- if (flags & (SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_WAL )) {
706+ const bool is_journal =
707+ flags & (SQLITE_OPEN_MAIN_JOURNAL | SQLITE_OPEN_WAL );
708+ if (is_journal) {
627709 permission_name = sqlite3_filename_database (name);
710+ sqlite3_file* database_file = sqlite3_database_file_object (name);
711+ if (database_file != nullptr &&
712+ !PermissionFileFromBase (database_file)->can_write_sidecar ) {
713+ write = false ;
714+ parent_flags &= ~(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE );
715+ parent_flags |= SQLITE_OPEN_READONLY ;
716+ }
628717 }
718+
629719 allowed = permission_name != nullptr &&
630720 !HasSQLiteVfsUriParameter (permission_name) &&
631- AllowsPath (permission_name, read, write);
632- if (permission_name != nullptr && (flags & SQLITE_OPEN_MAIN_DB )) {
721+ ((flags & SQLITE_OPEN_SUPER_JOURNAL )
722+ ? AllowsSidecarPath (permission_name, read, write)
723+ : AllowsPath (permission_name, read, write));
724+ if (allowed && permission_name != nullptr &&
725+ (flags & SQLITE_OPEN_MAIN_DB )) {
633726 can_write_sidecar = AllowsPath (permission_name, false , true );
727+ if (!can_write_sidecar) {
728+ permission_file->parent_name = CreateReadOnlyShmFilename (name);
729+ if (permission_file->parent_name == nullptr ) return SQLITE_NOMEM ;
730+ read_only_shm = true ;
731+ }
634732 }
635733 }
636734
637735 if (!allowed) return SQLITE_PERM ;
638736
737+ sqlite3_filename parent_name = permission_file->parent_name == nullptr
738+ ? name
739+ : permission_file->parent_name ;
639740 sqlite3_file* real = RealFile (permission_file);
640- const int result = parent_->xOpen (parent_, name, real, flags, out_flags);
741+ const int result = selected_vfs->xOpen (
742+ selected_vfs, parent_name, real, parent_flags, out_flags);
641743 if (real->pMethods == nullptr ) {
744+ sqlite3_free_filename (permission_file->parent_name );
745+ permission_file->parent_name = nullptr ;
642746 file->pMethods = nullptr ;
643747 return result;
644748 }
@@ -648,6 +752,7 @@ int SQLitePermissionVFS::Open(sqlite3_filename name,
648752 (out_flags == nullptr ||
649753 (*out_flags & SQLITE_OPEN_READONLY ) == 0 );
650754 permission_file->can_write_sidecar = can_write_sidecar;
755+ permission_file->read_only_shm = read_only_shm;
651756 permission_file->methods = kPermissionIoMethods ;
652757 permission_file->methods .iVersion = std::min (
653758 permission_file->methods .iVersion , real->pMethods ->iVersion );
@@ -666,7 +771,9 @@ int SQLitePermissionVFS::Open(sqlite3_filename name,
666771}
667772
668773int SQLitePermissionVFS::Delete (const char * name, int sync_dir) {
669- if (name == nullptr || !AllowsPath (name, false , true )) return SQLITE_PERM ;
774+ if (name == nullptr || !AllowsSidecarPath (name, false , true )) {
775+ return SQLITE_PERM ;
776+ }
670777 return parent_->xDelete (parent_, name, sync_dir);
671778}
672779
@@ -678,7 +785,7 @@ int SQLitePermissionVFS::Access(const char* name, int flags, int* result) {
678785
679786 const bool read = true ;
680787 const bool write = flags == SQLITE_ACCESS_READWRITE ;
681- if (!AllowsPath (name, read, write)) {
788+ if (!AllowsSidecarPath (name, read, write)) {
682789 *result = 0 ;
683790 return SQLITE_OK ;
684791 }
0 commit comments