@@ -19,6 +19,7 @@ use std::{
19
19
use std:: { fmt, io, mem, ptr, sync} ;
20
20
21
21
use once_cell:: sync:: Lazy ;
22
+ use same_file:: Handle ;
22
23
use synchronoise:: event:: SignalEvent ;
23
24
24
25
use crate :: mdb:: error:: mdb_result;
@@ -32,7 +33,10 @@ use crate::{
32
33
/// noone tries to open the same environment between these two phases.
33
34
///
34
35
/// Trying to open a None marked environment returns an error to the user trying to open it.
35
- static OPENED_ENV : Lazy < RwLock < HashMap < PathBuf , EnvEntry > > > = Lazy :: new ( RwLock :: default) ;
36
+ ///
37
+ /// [samefile::Handle] abstract the platform details of checking if the given paths points to
38
+ /// the same file on the filesystem.
39
+ static OPENED_ENV : Lazy < RwLock < HashMap < Handle , EnvEntry > > > = Lazy :: new ( RwLock :: default) ;
36
40
37
41
struct EnvEntry {
38
42
env : Option < Env > ,
@@ -180,22 +184,29 @@ impl EnvOpenOptions {
180
184
pub fn open < P : AsRef < Path > > ( & self , path : P ) -> Result < Env > {
181
185
let mut lock = OPENED_ENV . write ( ) . unwrap ( ) ;
182
186
183
- let path = match canonicalize_path ( path. as_ref ( ) ) {
187
+ let ( path, handle ) = match canonicalize_path ( path. as_ref ( ) ) {
184
188
Err ( err) => {
185
189
if err. kind ( ) == NotFound && self . flags & ( Flag :: NoSubDir as u32 ) != 0 {
186
190
let path = path. as_ref ( ) ;
187
191
match path. parent ( ) . zip ( path. file_name ( ) ) {
188
- Some ( ( dir, file_name) ) => canonicalize_path ( dir) ?. join ( file_name) ,
192
+ Some ( ( dir, file_name) ) => {
193
+ let handle = Handle :: from_path ( dir) ?;
194
+ let path = canonicalize_path ( dir) ?. join ( file_name) ;
195
+ ( path, handle)
196
+ }
189
197
None => return Err ( err. into ( ) ) ,
190
198
}
191
199
} else {
192
200
return Err ( err. into ( ) ) ;
193
201
}
194
202
}
195
- Ok ( path) => path,
203
+ Ok ( path) => {
204
+ let handle = Handle :: from_path ( & path) ?;
205
+ ( path, handle)
206
+ }
196
207
} ;
197
208
198
- match lock. entry ( path ) {
209
+ match lock. entry ( handle ) {
199
210
Entry :: Occupied ( entry) => {
200
211
let env = entry. get ( ) . env . clone ( ) . ok_or ( Error :: DatabaseClosing ) ?;
201
212
let options = entry. get ( ) . options . clone ( ) ;
@@ -206,7 +217,6 @@ impl EnvOpenOptions {
206
217
}
207
218
}
208
219
Entry :: Vacant ( entry) => {
209
- let path = entry. key ( ) ;
210
220
let path_str = CString :: new ( path. as_os_str ( ) . as_bytes ( ) ) . unwrap ( ) ;
211
221
212
222
unsafe {
@@ -255,6 +265,7 @@ impl EnvOpenOptions {
255
265
env,
256
266
dbi_open_mutex : sync:: Mutex :: default ( ) ,
257
267
path : path. clone ( ) ,
268
+ handle : Handle :: from_path ( path) ?,
258
269
} ;
259
270
let env = Env ( Arc :: new ( inner) ) ;
260
271
let cache_entry = EnvEntry {
@@ -277,9 +288,11 @@ impl EnvOpenOptions {
277
288
}
278
289
279
290
/// Returns a struct that allows to wait for the effective closing of an environment.
280
- pub fn env_closing_event < P : AsRef < Path > > ( path : P ) -> Option < EnvClosingEvent > {
291
+ pub fn env_closing_event < P : AsRef < Path > > ( path : P ) -> Result < Option < EnvClosingEvent > > {
281
292
let lock = OPENED_ENV . read ( ) . unwrap ( ) ;
282
- lock. get ( path. as_ref ( ) ) . map ( |e| EnvClosingEvent ( e. signal_event . clone ( ) ) )
293
+ let handle = Handle :: from_path ( path) ?;
294
+
295
+ Ok ( lock. get ( & handle) . map ( |e| EnvClosingEvent ( e. signal_event . clone ( ) ) ) )
283
296
}
284
297
285
298
/// An environment handle constructed by using [`EnvOpenOptions`].
@@ -288,7 +301,7 @@ pub struct Env(Arc<EnvInner>);
288
301
289
302
impl fmt:: Debug for Env {
290
303
fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
291
- let EnvInner { env : _, dbi_open_mutex : _, path } = self . 0 . as_ref ( ) ;
304
+ let EnvInner { env : _, dbi_open_mutex : _, path, .. } = self . 0 . as_ref ( ) ;
292
305
f. debug_struct ( "Env" ) . field ( "path" , & path. display ( ) ) . finish_non_exhaustive ( )
293
306
}
294
307
}
@@ -297,6 +310,7 @@ struct EnvInner {
297
310
env : * mut ffi:: MDB_env ,
298
311
dbi_open_mutex : sync:: Mutex < HashMap < u32 , Option < ( TypeId , TypeId ) > > > ,
299
312
path : PathBuf ,
313
+ handle : Handle ,
300
314
}
301
315
302
316
unsafe impl Send for EnvInner { }
@@ -307,7 +321,7 @@ impl Drop for EnvInner {
307
321
fn drop ( & mut self ) {
308
322
let mut lock = OPENED_ENV . write ( ) . unwrap ( ) ;
309
323
310
- match lock. remove ( & self . path ) {
324
+ match lock. remove ( & self . handle ) {
311
325
None => panic ! ( "It seems another env closed this env before" ) ,
312
326
Some ( EnvEntry { signal_event, .. } ) => {
313
327
unsafe {
@@ -653,7 +667,7 @@ impl Env {
653
667
/// when all references are dropped, the last one will eventually close the environment.
654
668
pub fn prepare_for_closing ( self ) -> EnvClosingEvent {
655
669
let mut lock = OPENED_ENV . write ( ) . unwrap ( ) ;
656
- match lock. get_mut ( self . path ( ) ) {
670
+ match lock. get_mut ( & self . 0 . handle ) {
657
671
None => panic ! ( "cannot find the env that we are trying to close" ) ,
658
672
Some ( EnvEntry { env, signal_event, .. } ) => {
659
673
// We remove the env from the global list and replace it with a None.
@@ -797,7 +811,7 @@ mod tests {
797
811
eprintln ! ( "env closed successfully" ) ;
798
812
799
813
// Make sure we don't have a reference to the env
800
- assert ! ( env_closing_event( & dir. path( ) ) . is_none( ) ) ;
814
+ assert ! ( env_closing_event( & dir. path( ) ) . unwrap ( ) . is_none( ) ) ;
801
815
}
802
816
803
817
#[ test]
@@ -830,6 +844,75 @@ mod tests {
830
844
. unwrap ( ) ;
831
845
}
832
846
847
+ #[ test]
848
+ fn open_env_with_named_path_hardlink_and_no_subdir ( ) {
849
+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
850
+ let dir_symlink = tempfile:: tempdir ( ) . unwrap ( ) ;
851
+ let env_name = dir. path ( ) . join ( "babar.mdb" ) ;
852
+ let hardlink_name = dir_symlink. path ( ) . join ( "babar.mdb.link" ) ;
853
+
854
+ let mut envbuilder = EnvOpenOptions :: new ( ) ;
855
+ unsafe { envbuilder. flag ( crate :: Flag :: NoSubDir ) } ;
856
+ let _env = envbuilder
857
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
858
+ . open ( & env_name)
859
+ . unwrap ( ) ;
860
+
861
+ std:: os:: unix:: fs:: symlink ( & dir. path ( ) , & hardlink_name) . unwrap ( ) ;
862
+ let _env = envbuilder
863
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
864
+ . open ( & hardlink_name)
865
+ . unwrap ( ) ;
866
+
867
+ let _env = envbuilder
868
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
869
+ . open ( & env_name)
870
+ . unwrap ( ) ;
871
+ }
872
+
873
+ #[ test]
874
+ #[ cfg( unix) ]
875
+ fn open_env_with_named_path_symlink ( ) {
876
+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
877
+ let dir_symlink = tempfile:: tempdir ( ) . unwrap ( ) ;
878
+
879
+ let env_name = dir. path ( ) . join ( "babar.mdb" ) ;
880
+ let symlink_name = dir_symlink. path ( ) . join ( "babar.mdb.link" ) ;
881
+ fs:: create_dir_all ( & env_name) . unwrap ( ) ;
882
+
883
+ std:: os:: unix:: fs:: symlink ( & env_name, & symlink_name) . unwrap ( ) ;
884
+ let _env = EnvOpenOptions :: new ( )
885
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
886
+ . open ( & symlink_name)
887
+ . unwrap ( ) ;
888
+
889
+ let _env = EnvOpenOptions :: new ( )
890
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
891
+ . open ( & env_name)
892
+ . unwrap ( ) ;
893
+ }
894
+
895
+ #[ test]
896
+ fn open_env_with_named_path_rename ( ) {
897
+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
898
+
899
+ let env_name = dir. path ( ) . join ( "babar.mdb" ) ;
900
+ fs:: create_dir_all ( & env_name) . unwrap ( ) ;
901
+
902
+ let _env = EnvOpenOptions :: new ( )
903
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
904
+ . open ( & env_name)
905
+ . unwrap ( ) ;
906
+
907
+ let env_renamed = dir. path ( ) . join ( "serafina.mdb" ) ;
908
+ std:: fs:: rename ( & env_name, & env_renamed) . unwrap ( ) ;
909
+
910
+ let _env = EnvOpenOptions :: new ( )
911
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
912
+ . open ( & env_renamed)
913
+ . unwrap ( ) ;
914
+ }
915
+
833
916
#[ test]
834
917
#[ cfg( not( windows) ) ]
835
918
fn open_database_with_writemap_flag ( ) {
@@ -853,6 +936,82 @@ mod tests {
853
936
let _env = envbuilder. open ( & dir. path ( ) . join ( "data.mdb" ) ) . unwrap ( ) ;
854
937
}
855
938
939
+ #[ test]
940
+ #[ cfg( unix) ]
941
+ fn open_env_with_named_path_symlink_and_no_subdir ( ) {
942
+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
943
+ let dir_symlink = tempfile:: tempdir ( ) . unwrap ( ) ;
944
+ let env_name = dir. path ( ) . join ( "babar.mdb" ) ;
945
+ let symlink_name = dir_symlink. path ( ) . join ( "babar.mdb.link" ) ;
946
+
947
+ let mut envbuilder = EnvOpenOptions :: new ( ) ;
948
+ unsafe { envbuilder. flag ( crate :: Flag :: NoSubDir ) } ;
949
+ let _env = envbuilder
950
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
951
+ . open ( & env_name)
952
+ . unwrap ( ) ;
953
+
954
+ std:: os:: unix:: fs:: symlink ( & dir. path ( ) , & symlink_name) . unwrap ( ) ;
955
+ let _env = envbuilder
956
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
957
+ . open ( & symlink_name)
958
+ . unwrap ( ) ;
959
+
960
+ let _env = envbuilder
961
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
962
+ . open ( & env_name)
963
+ . unwrap ( ) ;
964
+ }
965
+
966
+ #[ test]
967
+ #[ cfg( windows) ]
968
+ fn open_env_with_named_path_symlinkfile_and_no_subdir ( ) {
969
+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
970
+ let dir_symlink = tempfile:: tempdir ( ) . unwrap ( ) ;
971
+ let env_name = dir. path ( ) . join ( "babar.mdb" ) ;
972
+ let symlink_name = dir_symlink. path ( ) . join ( "babar.mdb.link" ) ;
973
+
974
+ let mut envbuilder = EnvOpenOptions :: new ( ) ;
975
+ unsafe { envbuilder. flag ( crate :: Flag :: NoSubDir ) } ;
976
+ let _env = envbuilder
977
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
978
+ . open ( & env_name)
979
+ . unwrap ( ) ;
980
+
981
+ std:: os:: windows:: fs:: symlink_file ( & dir. path ( ) , & symlink_name) . unwrap ( ) ;
982
+ let _env = envbuilder
983
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
984
+ . open ( & symlink_name)
985
+ . unwrap ( ) ;
986
+
987
+ let _env = envbuilder
988
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
989
+ . open ( & env_name)
990
+ . unwrap ( ) ;
991
+ }
992
+
993
+ #[ test]
994
+ #[ cfg( windows) ]
995
+ fn open_env_with_named_path_symlink ( ) {
996
+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
997
+ let dir_symlink = tempfile:: tempdir ( ) . unwrap ( ) ;
998
+
999
+ let env_name = dir. path ( ) . join ( "babar.mdb" ) ;
1000
+ let symlink_name = dir_symlink. path ( ) . join ( "babar.mdb.link" ) ;
1001
+ fs:: create_dir_all ( & env_name) . unwrap ( ) ;
1002
+
1003
+ std:: os:: windows:: fs:: symlink_dir ( & env_name, & symlink_name) . unwrap ( ) ;
1004
+ let _env = EnvOpenOptions :: new ( )
1005
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
1006
+ . open ( & symlink_name)
1007
+ . unwrap ( ) ;
1008
+
1009
+ let _env = EnvOpenOptions :: new ( )
1010
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
1011
+ . open ( & env_name)
1012
+ . unwrap ( ) ;
1013
+ }
1014
+
856
1015
#[ test]
857
1016
fn create_database_without_commit ( ) {
858
1017
let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
0 commit comments