1
- use nix;
2
1
use nix:: mount:: { mount, MsFlags } ;
3
2
use nix:: sched:: { unshare, CloneFlags } ;
4
3
use nix:: sys:: signal:: { kill, Signal } ;
@@ -46,28 +45,52 @@ impl<'a> RunChroot<'a> {
46
45
47
46
fn bind_mount_directory ( & self , entry : & fs:: DirEntry ) {
48
47
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
+ }
52
55
}
53
- }
54
56
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
+ }
56
73
}
57
74
58
75
fn bind_mount_file ( & self , entry : & fs:: DirEntry ) {
59
76
let mountpoint = self . rootdir . join ( entry. file_name ( ) ) ;
77
+ if mountpoint. exists ( ) {
78
+ return ;
79
+ }
60
80
fs:: File :: create ( & mountpoint)
61
81
. unwrap_or_else ( |err| panic ! ( "failed to create {}: {}" , & mountpoint. display( ) , err) ) ;
62
82
63
83
bind_mount ( & entry. path ( ) , & mountpoint)
64
84
}
65
85
66
86
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
+ }
67
91
let path = entry. path ( ) ;
68
92
let target = fs:: read_link ( & path)
69
93
. unwrap_or_else ( |err| panic ! ( "failed to resolve symlink {}: {}" , & path. display( ) , err) ) ;
70
- let link_path = self . rootdir . join ( entry. file_name ( ) ) ;
71
94
symlink ( & target, & link_path) . unwrap_or_else ( |_| {
72
95
panic ! (
73
96
"failed to create symlink {} -> {}" ,
@@ -77,16 +100,12 @@ impl<'a> RunChroot<'a> {
77
100
} ) ;
78
101
}
79
102
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 ) {
86
104
let path = entry. path ( ) ;
87
105
let stat = entry
88
106
. metadata ( )
89
107
. unwrap_or_else ( |err| panic ! ( "cannot get stat of {}: {}" , path. display( ) , err) ) ;
108
+
90
109
if stat. is_dir ( ) {
91
110
self . bind_mount_directory ( & entry) ;
92
111
} else if stat. is_file ( ) {
@@ -104,11 +123,26 @@ impl<'a> RunChroot<'a> {
104
123
105
124
unshare ( CloneFlags :: CLONE_NEWNS | CloneFlags :: CLONE_NEWUSER ) . expect ( "unshare failed" ) ;
106
125
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
108
137
let nix_root = PathBuf :: from ( "/" ) ;
109
138
let dir = fs:: read_dir ( & nix_root) . expect ( "failed to list /nix directory" ) ;
110
139
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) ;
112
146
}
113
147
114
148
// mount the store
0 commit comments