@@ -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,49 @@ mod tests {
830
844
. unwrap ( ) ;
831
845
}
832
846
847
+ #[ test]
848
+ #[ cfg( unix) ]
849
+ fn open_env_with_named_path_symlink ( ) {
850
+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
851
+ let dir_symlink = tempfile:: tempdir ( ) . unwrap ( ) ;
852
+
853
+ let env_name = dir. path ( ) . join ( "babar.mdb" ) ;
854
+ let symlink_name = dir_symlink. path ( ) . join ( "babar.mdb.link" ) ;
855
+ fs:: create_dir_all ( & env_name) . unwrap ( ) ;
856
+
857
+ std:: os:: unix:: fs:: symlink ( & env_name, & symlink_name) . unwrap ( ) ;
858
+ let _env = EnvOpenOptions :: new ( )
859
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
860
+ . open ( & symlink_name)
861
+ . unwrap ( ) ;
862
+
863
+ let _env = EnvOpenOptions :: new ( )
864
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
865
+ . open ( & symlink_name)
866
+ . unwrap ( ) ;
867
+ }
868
+
869
+ #[ test]
870
+ fn open_env_with_named_path_rename ( ) {
871
+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
872
+
873
+ let env_name = dir. path ( ) . join ( "babar.mdb" ) ;
874
+ fs:: create_dir_all ( & env_name) . unwrap ( ) ;
875
+
876
+ let _env = EnvOpenOptions :: new ( )
877
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
878
+ . open ( & env_name)
879
+ . unwrap ( ) ;
880
+
881
+ let env_renamed = dir. path ( ) . join ( "serafina.mdb" ) ;
882
+ std:: fs:: rename ( & env_name, & env_renamed) . unwrap ( ) ;
883
+
884
+ let _env = EnvOpenOptions :: new ( )
885
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
886
+ . open ( & env_renamed)
887
+ . unwrap ( ) ;
888
+ }
889
+
833
890
#[ test]
834
891
#[ cfg( not( windows) ) ]
835
892
fn open_database_with_writemap_flag ( ) {
@@ -853,6 +910,33 @@ mod tests {
853
910
let _env = envbuilder. open ( & dir. path ( ) . join ( "data.mdb" ) ) . unwrap ( ) ;
854
911
}
855
912
913
+ #[ test]
914
+ #[ cfg( unix) ]
915
+ fn open_env_with_named_path_symlink_and_no_subdir ( ) {
916
+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
917
+ let dir_symlink = tempfile:: tempdir ( ) . unwrap ( ) ;
918
+ let env_name = dir. path ( ) . join ( "babar.mdb" ) ;
919
+ let symlink_name = dir_symlink. path ( ) . join ( "babar.mdb.link" ) ;
920
+
921
+ let mut envbuilder = EnvOpenOptions :: new ( ) ;
922
+ unsafe { envbuilder. flag ( crate :: Flag :: NoSubDir ) } ;
923
+ let _env = envbuilder
924
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
925
+ . open ( & env_name)
926
+ . unwrap ( ) ;
927
+
928
+ std:: os:: unix:: fs:: symlink ( & dir. path ( ) , & symlink_name) . unwrap ( ) ;
929
+ let _env = envbuilder
930
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
931
+ . open ( & symlink_name)
932
+ . unwrap ( ) ;
933
+
934
+ let _env = envbuilder
935
+ . map_size ( 10 * 1024 * 1024 ) // 10MB
936
+ . open ( & env_name)
937
+ . unwrap ( ) ;
938
+ }
939
+
856
940
#[ test]
857
941
fn create_database_without_commit ( ) {
858
942
let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
0 commit comments