99use std:: collections:: HashMap ;
1010#[ cfg( test) ]
1111use std:: fs;
12+ #[ cfg( unix) ]
13+ use std:: fs:: OpenOptions ;
1214use std:: future:: Future ;
15+ #[ cfg( unix) ]
16+ use std:: os:: unix:: fs:: OpenOptionsExt ;
1317use std:: path:: PathBuf ;
1418use std:: sync:: atomic:: { AtomicI64 , AtomicU64 , Ordering } ;
1519use std:: sync:: { Arc , Mutex } ;
@@ -59,6 +63,8 @@ impl SqliteStore {
5963 /// If not already existing, a new SQLite database will be created in the given `data_dir` under the
6064 /// given `db_file_name` (or the default to [`DEFAULT_SQLITE_DB_FILE_NAME`] if set to `None`).
6165 ///
66+ /// SQLite's `:memory:` database name and `file:` URI filenames are not supported.
67+ ///
6268 /// Similarly, the given `kv_table_name` will be used or default to [`DEFAULT_KV_TABLE_NAME`].
6369 pub fn new (
6470 data_dir : PathBuf , db_file_name : Option < String > , kv_table_name : Option < String > ,
@@ -233,6 +239,12 @@ impl SqliteStoreInner {
233239 data_dir : PathBuf , db_file_name : Option < String > , kv_table_name : Option < String > ,
234240 ) -> io:: Result < Self > {
235241 let db_file_name = db_file_name. unwrap_or ( DEFAULT_SQLITE_DB_FILE_NAME . to_string ( ) ) ;
242+ if db_file_name == ":memory:" || db_file_name. starts_with ( "file:" ) {
243+ return Err ( io:: Error :: new (
244+ io:: ErrorKind :: InvalidInput ,
245+ "SQLite :memory: and file: database names are not supported" ,
246+ ) ) ;
247+ }
236248 let kv_table_name = kv_table_name. unwrap_or ( DEFAULT_KV_TABLE_NAME . to_string ( ) ) ;
237249
238250 create_dir_all_private ( & data_dir) . map_err ( |e| {
@@ -245,6 +257,16 @@ impl SqliteStoreInner {
245257 } ) ?;
246258 let mut db_file_path = data_dir. clone ( ) ;
247259 db_file_path. push ( db_file_name) ;
260+ #[ cfg( unix) ]
261+ match OpenOptions :: new ( ) . create_new ( true ) . write ( true ) . mode ( 0o600 ) . open ( & db_file_path) {
262+ Ok ( _) => { } ,
263+ Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: AlreadyExists => { } ,
264+ Err ( e) => {
265+ let msg =
266+ format ! ( "Failed to create database file {}: {}" , db_file_path. display( ) , e) ;
267+ return Err ( io:: Error :: new ( io:: ErrorKind :: Other , msg) ) ;
268+ } ,
269+ }
248270
249271 let mut connection = Connection :: open ( db_file_path. clone ( ) ) . map_err ( |e| {
250272 let msg =
@@ -701,6 +723,47 @@ mod tests {
701723 }
702724 }
703725
726+ #[ cfg( unix) ]
727+ #[ test]
728+ fn creates_private_database_storage ( ) {
729+ use std:: os:: unix:: fs:: PermissionsExt ;
730+
731+ let mut data_dir = random_storage_path ( ) ;
732+ data_dir. push ( "creates_private_database_storage" ) ;
733+ let db_file_name = "test_db" ;
734+ let db_file_path = data_dir. join ( db_file_name) ;
735+ let _store = SqliteStore :: new (
736+ data_dir. clone ( ) ,
737+ Some ( db_file_name. to_string ( ) ) ,
738+ Some ( "test_table" . to_string ( ) ) ,
739+ )
740+ . unwrap ( ) ;
741+
742+ let dir_mode = data_dir. metadata ( ) . unwrap ( ) . permissions ( ) . mode ( ) ;
743+ let file_mode = db_file_path. metadata ( ) . unwrap ( ) . permissions ( ) . mode ( ) ;
744+ assert_eq ! ( dir_mode & 0o077 , 0 ) ;
745+ assert_eq ! ( file_mode & 0o077 , 0 ) ;
746+ }
747+
748+ #[ test]
749+ fn rejects_sqlite_pseudo_filenames ( ) {
750+ for db_file_name in [ ":memory:" , "file:/tmp/node.db?mode=rwc" ] {
751+ let data_dir = random_storage_path ( ) ;
752+ let result = SqliteStore :: new (
753+ data_dir. clone ( ) ,
754+ Some ( db_file_name. to_string ( ) ) ,
755+ Some ( "test_table" . to_string ( ) ) ,
756+ ) ;
757+ let error = match result {
758+ Ok ( _) => panic ! ( "SQLite pseudo-filename was accepted" ) ,
759+ Err ( e) => e,
760+ } ;
761+
762+ assert_eq ! ( error. kind( ) , io:: ErrorKind :: InvalidInput ) ;
763+ assert ! ( !data_dir. exists( ) ) ;
764+ }
765+ }
766+
704767 #[ tokio:: test]
705768 async fn read_write_remove_list_persist ( ) {
706769 let mut temp_path = random_storage_path ( ) ;
0 commit comments