@@ -19,6 +19,7 @@ use std::{
1919use std:: { fmt, io, mem, ptr, sync} ;
2020
2121use once_cell:: sync:: Lazy ;
22+ use same_file:: Handle ;
2223use synchronoise:: event:: SignalEvent ;
2324
2425use crate :: mdb:: error:: mdb_result;
@@ -32,7 +33,10 @@ use crate::{
3233/// noone tries to open the same environment between these two phases.
3334///
3435/// 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) ;
3640
3741struct EnvEntry {
3842 env : Option < Env > ,
@@ -180,22 +184,29 @@ impl EnvOpenOptions {
180184 pub fn open < P : AsRef < Path > > ( & self , path : P ) -> Result < Env > {
181185 let mut lock = OPENED_ENV . write ( ) . unwrap ( ) ;
182186
183- let path = match canonicalize_path ( path. as_ref ( ) ) {
187+ let ( path, handle ) = match canonicalize_path ( path. as_ref ( ) ) {
184188 Err ( err) => {
185189 if err. kind ( ) == NotFound && self . flags & ( Flag :: NoSubDir as u32 ) != 0 {
186190 let path = path. as_ref ( ) ;
187191 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+ }
189197 None => return Err ( err. into ( ) ) ,
190198 }
191199 } else {
192200 return Err ( err. into ( ) ) ;
193201 }
194202 }
195- Ok ( path) => path,
203+ Ok ( path) => {
204+ let handle = Handle :: from_path ( & path) ?;
205+ ( path, handle)
206+ }
196207 } ;
197208
198- match lock. entry ( path ) {
209+ match lock. entry ( handle ) {
199210 Entry :: Occupied ( entry) => {
200211 let env = entry. get ( ) . env . clone ( ) . ok_or ( Error :: DatabaseClosing ) ?;
201212 let options = entry. get ( ) . options . clone ( ) ;
@@ -206,7 +217,6 @@ impl EnvOpenOptions {
206217 }
207218 }
208219 Entry :: Vacant ( entry) => {
209- let path = entry. key ( ) ;
210220 let path_str = CString :: new ( path. as_os_str ( ) . as_bytes ( ) ) . unwrap ( ) ;
211221
212222 unsafe {
@@ -255,6 +265,7 @@ impl EnvOpenOptions {
255265 env,
256266 dbi_open_mutex : sync:: Mutex :: default ( ) ,
257267 path : path. clone ( ) ,
268+ handle : Handle :: from_path ( path) ?,
258269 } ;
259270 let env = Env ( Arc :: new ( inner) ) ;
260271 let cache_entry = EnvEntry {
@@ -277,9 +288,11 @@ impl EnvOpenOptions {
277288}
278289
279290/// 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 > > {
281292 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 ( ) ) ) )
283296}
284297
285298/// An environment handle constructed by using [`EnvOpenOptions`].
@@ -288,7 +301,7 @@ pub struct Env(Arc<EnvInner>);
288301
289302impl fmt:: Debug for Env {
290303 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 ( ) ;
292305 f. debug_struct ( "Env" ) . field ( "path" , & path. display ( ) ) . finish_non_exhaustive ( )
293306 }
294307}
@@ -297,6 +310,7 @@ struct EnvInner {
297310 env : * mut ffi:: MDB_env ,
298311 dbi_open_mutex : sync:: Mutex < HashMap < u32 , Option < ( TypeId , TypeId ) > > > ,
299312 path : PathBuf ,
313+ handle : Handle ,
300314}
301315
302316unsafe impl Send for EnvInner { }
@@ -307,7 +321,7 @@ impl Drop for EnvInner {
307321 fn drop ( & mut self ) {
308322 let mut lock = OPENED_ENV . write ( ) . unwrap ( ) ;
309323
310- match lock. remove ( & self . path ) {
324+ match lock. remove ( & self . handle ) {
311325 None => panic ! ( "It seems another env closed this env before" ) ,
312326 Some ( EnvEntry { signal_event, .. } ) => {
313327 unsafe {
@@ -653,7 +667,7 @@ impl Env {
653667 /// when all references are dropped, the last one will eventually close the environment.
654668 pub fn prepare_for_closing ( self ) -> EnvClosingEvent {
655669 let mut lock = OPENED_ENV . write ( ) . unwrap ( ) ;
656- match lock. get_mut ( self . path ( ) ) {
670+ match lock. get_mut ( & self . 0 . handle ) {
657671 None => panic ! ( "cannot find the env that we are trying to close" ) ,
658672 Some ( EnvEntry { env, signal_event, .. } ) => {
659673 // We remove the env from the global list and replace it with a None.
@@ -797,7 +811,7 @@ mod tests {
797811 eprintln ! ( "env closed successfully" ) ;
798812
799813 // 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( ) ) ;
801815 }
802816
803817 #[ test]
@@ -830,6 +844,75 @@ mod tests {
830844 . unwrap ( ) ;
831845 }
832846
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+
833916 #[ test]
834917 #[ cfg( not( windows) ) ]
835918 fn open_database_with_writemap_flag ( ) {
@@ -853,6 +936,82 @@ mod tests {
853936 let _env = envbuilder. open ( & dir. path ( ) . join ( "data.mdb" ) ) . unwrap ( ) ;
854937 }
855938
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+
8561015 #[ test]
8571016 fn create_database_without_commit ( ) {
8581017 let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
0 commit comments