1- use nix;
21use nix:: mount:: { mount, MsFlags } ;
32use nix:: sched:: { unshare, CloneFlags } ;
43use nix:: sys:: signal:: { kill, Signal } ;
@@ -46,28 +45,52 @@ impl<'a> RunChroot<'a> {
4645
4746 fn bind_mount_directory ( & self , entry : & fs:: DirEntry ) {
4847 let mountpoint = self . rootdir . join ( entry. file_name ( ) ) ;
49- if let Err ( e) = fs:: create_dir ( & mountpoint) {
50- if e. kind ( ) != io:: ErrorKind :: AlreadyExists {
51- panic ! ( "failed to create {}: {}" , & mountpoint. display( ) , e) ;
48+
49+ // if the destination doesn't exist we can proceed as normal
50+ if !mountpoint. exists ( ) {
51+ if let Err ( e) = fs:: create_dir ( & mountpoint) {
52+ if e. kind ( ) != io:: ErrorKind :: AlreadyExists {
53+ panic ! ( "failed to create {}: {}" , & mountpoint. display( ) , e) ;
54+ }
5255 }
53- }
5456
55- bind_mount ( & entry. path ( ) , & mountpoint)
57+ bind_mount ( & entry. path ( ) , & mountpoint)
58+ } else {
59+ // otherwise, if the dest is also a dir, we can recurse into it
60+ // and mount subdirectory siblings of existing paths
61+ if mountpoint. is_dir ( ) {
62+ let dir = fs:: read_dir ( entry. path ( ) ) . unwrap_or_else ( |err| {
63+ panic ! ( "failed to list dir {}: {}" , entry. path( ) . display( ) , err)
64+ } ) ;
65+
66+ let child = RunChroot :: new ( & mountpoint) ;
67+ for entry in dir {
68+ let entry = entry. expect ( "error while listing subdir" ) ;
69+ child. bind_mount_direntry ( & entry) ;
70+ }
71+ }
72+ }
5673 }
5774
5875 fn bind_mount_file ( & self , entry : & fs:: DirEntry ) {
5976 let mountpoint = self . rootdir . join ( entry. file_name ( ) ) ;
77+ if mountpoint. exists ( ) {
78+ return ;
79+ }
6080 fs:: File :: create ( & mountpoint)
6181 . unwrap_or_else ( |err| panic ! ( "failed to create {}: {}" , & mountpoint. display( ) , err) ) ;
6282
6383 bind_mount ( & entry. path ( ) , & mountpoint)
6484 }
6585
6686 fn mirror_symlink ( & self , entry : & fs:: DirEntry ) {
87+ let link_path = self . rootdir . join ( entry. file_name ( ) ) ;
88+ if link_path. exists ( ) {
89+ return ;
90+ }
6791 let path = entry. path ( ) ;
6892 let target = fs:: read_link ( & path)
6993 . unwrap_or_else ( |err| panic ! ( "failed to resolve symlink {}: {}" , & path. display( ) , err) ) ;
70- let link_path = self . rootdir . join ( entry. file_name ( ) ) ;
7194 symlink ( & target, & link_path) . unwrap_or_else ( |_| {
7295 panic ! (
7396 "failed to create symlink {} -> {}" ,
@@ -77,16 +100,12 @@ impl<'a> RunChroot<'a> {
77100 } ) ;
78101 }
79102
80- fn bind_mount_direntry ( & self , entry : io:: Result < fs:: DirEntry > ) {
81- let entry = entry. expect ( "error while listing from /nix directory" ) ;
82- // do not bind mount an existing nix installation
83- if entry. file_name ( ) == PathBuf :: from ( "nix" ) {
84- return ;
85- }
103+ fn bind_mount_direntry ( & self , entry : & fs:: DirEntry ) {
86104 let path = entry. path ( ) ;
87105 let stat = entry
88106 . metadata ( )
89107 . unwrap_or_else ( |err| panic ! ( "cannot get stat of {}: {}" , path. display( ) , err) ) ;
108+
90109 if stat. is_dir ( ) {
91110 self . bind_mount_directory ( & entry) ;
92111 } else if stat. is_file ( ) {
@@ -104,11 +123,26 @@ impl<'a> RunChroot<'a> {
104123
105124 unshare ( CloneFlags :: CLONE_NEWNS | CloneFlags :: CLONE_NEWUSER ) . expect ( "unshare failed" ) ;
106125
107- // bind mount all / stuff into rootdir
126+ // create /run/opengl-driver/lib in chroot, to behave like NixOS
127+ // (needed for nix pkgs with OpenGL or CUDA support to work)
128+ let ogldir = nixdir. join ( "var/nix/opengl-driver/lib" ) ;
129+ if ogldir. is_dir ( ) {
130+ let ogl_mount = self . rootdir . join ( "run/opengl-driver/lib" ) ;
131+ fs:: create_dir_all ( & ogl_mount)
132+ . unwrap_or_else ( |err| panic ! ( "failed to create {}: {}" , & ogl_mount. display( ) , err) ) ;
133+ bind_mount ( & ogldir, & ogl_mount) ;
134+ }
135+
136+ // bind the rest of / stuff into rootdir
108137 let nix_root = PathBuf :: from ( "/" ) ;
109138 let dir = fs:: read_dir ( & nix_root) . expect ( "failed to list /nix directory" ) ;
110139 for entry in dir {
111- self . bind_mount_direntry ( entry) ;
140+ let entry = entry. expect ( "error while listing from /nix directory" ) ;
141+ // do not bind mount an existing nix installation
142+ if entry. file_name ( ) == PathBuf :: from ( "nix" ) {
143+ continue ;
144+ }
145+ self . bind_mount_direntry ( & entry) ;
112146 }
113147
114148 // mount the store
0 commit comments