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,17 @@ 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+ let mut db_file_path = data_dir. clone ( ) ;
243+ db_file_path. push ( & db_file_name) ;
244+ if db_file_name == ":memory:"
245+ || db_file_name. starts_with ( "file:" )
246+ || db_file_path. to_string_lossy ( ) . starts_with ( "file:" )
247+ {
248+ return Err ( io:: Error :: new (
249+ io:: ErrorKind :: InvalidInput ,
250+ "SQLite :memory: and file: database names are not supported" ,
251+ ) ) ;
252+ }
236253 let kv_table_name = kv_table_name. unwrap_or ( DEFAULT_KV_TABLE_NAME . to_string ( ) ) ;
237254
238255 create_dir_all_private ( & data_dir) . map_err ( |e| {
@@ -243,8 +260,16 @@ impl SqliteStoreInner {
243260 ) ;
244261 io:: Error :: new ( io:: ErrorKind :: Other , msg)
245262 } ) ?;
246- let mut db_file_path = data_dir. clone ( ) ;
247- db_file_path. push ( db_file_name) ;
263+ #[ cfg( unix) ]
264+ match OpenOptions :: new ( ) . create_new ( true ) . write ( true ) . mode ( 0o600 ) . open ( & db_file_path) {
265+ Ok ( _) => { } ,
266+ Err ( e) if e. kind ( ) == std:: io:: ErrorKind :: AlreadyExists => { } ,
267+ Err ( e) => {
268+ let msg =
269+ format ! ( "Failed to create database file {}: {}" , db_file_path. display( ) , e) ;
270+ return Err ( io:: Error :: new ( io:: ErrorKind :: Other , msg) ) ;
271+ } ,
272+ }
248273
249274 let mut connection = Connection :: open ( db_file_path. clone ( ) ) . map_err ( |e| {
250275 let msg =
@@ -701,6 +726,50 @@ mod tests {
701726 }
702727 }
703728
729+ #[ cfg( unix) ]
730+ #[ test]
731+ fn creates_private_database_storage ( ) {
732+ use std:: os:: unix:: fs:: PermissionsExt ;
733+
734+ let mut data_dir = random_storage_path ( ) ;
735+ data_dir. push ( "creates_private_database_storage" ) ;
736+ let db_file_name = "test_db" ;
737+ let db_file_path = data_dir. join ( db_file_name) ;
738+ let _store = SqliteStore :: new (
739+ data_dir. clone ( ) ,
740+ Some ( db_file_name. to_string ( ) ) ,
741+ Some ( "test_table" . to_string ( ) ) ,
742+ )
743+ . unwrap ( ) ;
744+
745+ let dir_mode = data_dir. metadata ( ) . unwrap ( ) . permissions ( ) . mode ( ) ;
746+ let file_mode = db_file_path. metadata ( ) . unwrap ( ) . permissions ( ) . mode ( ) ;
747+ assert_eq ! ( dir_mode & 0o077 , 0 ) ;
748+ assert_eq ! ( file_mode & 0o077 , 0 ) ;
749+ }
750+
751+ #[ test]
752+ fn rejects_sqlite_pseudo_filenames ( ) {
753+ for ( data_dir, db_file_name) in [
754+ ( random_storage_path ( ) , ":memory:" ) ,
755+ ( random_storage_path ( ) , "file:/tmp/node.db?mode=rwc" ) ,
756+ ( PathBuf :: from ( "file:." ) , "test_db" ) ,
757+ ] {
758+ let result = SqliteStore :: new (
759+ data_dir. clone ( ) ,
760+ Some ( db_file_name. to_string ( ) ) ,
761+ Some ( "test_table" . to_string ( ) ) ,
762+ ) ;
763+ let error = match result {
764+ Ok ( _) => panic ! ( "SQLite pseudo-filename was accepted" ) ,
765+ Err ( e) => e,
766+ } ;
767+
768+ assert_eq ! ( error. kind( ) , io:: ErrorKind :: InvalidInput ) ;
769+ assert ! ( !data_dir. exists( ) ) ;
770+ }
771+ }
772+
704773 #[ tokio:: test]
705774 async fn read_write_remove_list_persist ( ) {
706775 let mut temp_path = random_storage_path ( ) ;
0 commit comments