99// except according to those terms.
1010
1111use env:: { split_paths} ;
12- use ffi:: OsStr ;
13- use os:: unix:: ffi:: OsStrExt ;
12+ use ffi:: { CStr , OsStr } ;
1413use fmt;
15- use io :: { self , Error , ErrorKind } ;
16- use iter ;
14+ use fs :: File ;
15+ use io :: { self , prelude :: * , BufReader , Error , ErrorKind , SeekFrom } ;
1716use libc:: { EXIT_SUCCESS , EXIT_FAILURE } ;
17+ use os:: unix:: ffi:: OsStrExt ;
1818use path:: { Path , PathBuf } ;
19+ use ptr;
20+ use sys:: ext:: fs:: MetadataExt ;
21+ use sys:: ext:: io:: AsRawFd ;
1922use sys:: fd:: FileDesc ;
20- use sys:: fs:: { File , OpenOptions } ;
23+ use sys:: fs:: { File as SysFile , OpenOptions } ;
24+ use sys:: os:: { ENV_LOCK , environ} ;
2125use sys:: pipe:: { self , AnonPipe } ;
2226use sys:: { cvt, syscall} ;
2327use sys_common:: process:: { CommandEnv , DefaultEnvKey } ;
@@ -297,12 +301,6 @@ impl Command {
297301 t ! ( callback( ) ) ;
298302 }
299303
300- let args: Vec < [ usize ; 2 ] > = iter:: once (
301- [ self . program . as_ptr ( ) as usize , self . program . len ( ) ]
302- ) . chain (
303- self . args . iter ( ) . map ( |arg| [ arg. as_ptr ( ) as usize , arg. len ( ) ] )
304- ) . collect ( ) ;
305-
306304 self . env . apply ( ) ;
307305
308306 let program = if self . program . contains ( ':' ) || self . program . contains ( '/' ) {
@@ -321,14 +319,93 @@ impl Command {
321319 None
322320 } ;
323321
324- if let Some ( program) = program {
325- if let Err ( err) = syscall:: execve ( program. as_os_str ( ) . as_bytes ( ) , & args) {
326- io:: Error :: from_raw_os_error ( err. errno as i32 )
322+ let mut file = if let Some ( program) = program {
323+ t ! ( File :: open( program. as_os_str( ) ) )
324+ } else {
325+ return io:: Error :: from_raw_os_error ( syscall:: ENOENT ) ;
326+ } ;
327+
328+ // Push all the arguments
329+ let mut args: Vec < [ usize ; 2 ] > = Vec :: with_capacity ( 1 + self . args . len ( ) ) ;
330+
331+ let interpreter = {
332+ let mut reader = BufReader :: new ( & file) ;
333+
334+ let mut shebang = [ 0 ; 2 ] ;
335+ let mut read = 0 ;
336+ loop {
337+ match t ! ( reader. read( & mut shebang[ read..] ) ) {
338+ 0 => break ,
339+ n => read += n,
340+ }
341+ }
342+
343+ if & shebang == b"#!" {
344+ // This is an interpreted script.
345+ // First of all, since we'll be passing another file to
346+ // fexec(), we need to manually check that we have permission
347+ // to execute this file:
348+ let uid = t ! ( cvt( syscall:: getuid( ) ) ) ;
349+ let gid = t ! ( cvt( syscall:: getgid( ) ) ) ;
350+ let meta = t ! ( file. metadata( ) ) ;
351+
352+ let mode = if uid == meta. uid ( ) as usize {
353+ meta. mode ( ) >> 3 * 2 & 0o7
354+ } else if gid == meta. gid ( ) as usize {
355+ meta. mode ( ) >> 3 * 1 & 0o7
356+ } else {
357+ meta. mode ( ) & 0o7
358+ } ;
359+ if mode & 1 == 0 {
360+ return io:: Error :: from_raw_os_error ( syscall:: EPERM ) ;
361+ }
362+
363+ // Second of all, we need to actually read which interpreter it wants
364+ let mut interpreter = Vec :: new ( ) ;
365+ t ! ( reader. read_until( b'\n' , & mut interpreter) ) ;
366+ // Pop one trailing newline, if any
367+ if interpreter. ends_with ( & [ b'\n' ] ) {
368+ interpreter. pop ( ) . unwrap ( ) ;
369+ }
370+
371+ // FIXME: Here we could just reassign `file` directly, if it
372+ // wasn't for lexical lifetimes. Remove the whole `let
373+ // interpreter = { ... };` hack once NLL lands.
374+ // NOTE: Although DO REMEMBER to make sure the interpreter path
375+ // still lives long enough to reach fexec.
376+ Some ( interpreter)
327377 } else {
328- panic ! ( "return from exec without err" ) ;
378+ None
379+ }
380+ } ;
381+ if let Some ( ref interpreter) = interpreter {
382+ let path: & OsStr = OsStr :: from_bytes ( & interpreter) ;
383+ file = t ! ( File :: open( path) ) ;
384+
385+ args. push ( [ interpreter. as_ptr ( ) as usize , interpreter. len ( ) ] ) ;
386+ } else {
387+ t ! ( file. seek( SeekFrom :: Start ( 0 ) ) ) ;
388+ }
389+
390+ args. push ( [ self . program . as_ptr ( ) as usize , self . program . len ( ) ] ) ;
391+ args. extend ( self . args . iter ( ) . map ( |arg| [ arg. as_ptr ( ) as usize , arg. len ( ) ] ) ) ;
392+
393+ // Push all the variables
394+ let mut vars: Vec < [ usize ; 2 ] > = Vec :: new ( ) ;
395+ {
396+ let _guard = ENV_LOCK . lock ( ) ;
397+ let mut environ = * environ ( ) ;
398+ while * environ != ptr:: null ( ) {
399+ let var = CStr :: from_ptr ( * environ) . to_bytes ( ) ;
400+ vars. push ( [ var. as_ptr ( ) as usize , var. len ( ) ] ) ;
401+ environ = environ. offset ( 1 ) ;
329402 }
403+ }
404+
405+ if let Err ( err) = syscall:: fexec ( file. as_raw_fd ( ) , & args, & vars) {
406+ io:: Error :: from_raw_os_error ( err. errno as i32 )
330407 } else {
331- io :: Error :: from_raw_os_error ( syscall :: ENOENT )
408+ panic ! ( "return from exec without err" ) ;
332409 }
333410 }
334411
@@ -392,7 +469,7 @@ impl Stdio {
392469 let mut opts = OpenOptions :: new ( ) ;
393470 opts. read ( readable) ;
394471 opts. write ( !readable) ;
395- let fd = File :: open ( Path :: new ( "null:" ) , & opts) ?;
472+ let fd = SysFile :: open ( Path :: new ( "null:" ) , & opts) ?;
396473 Ok ( ( ChildStdio :: Owned ( fd. into_fd ( ) ) , None ) )
397474 }
398475 }
@@ -405,8 +482,8 @@ impl From<AnonPipe> for Stdio {
405482 }
406483}
407484
408- impl From < File > for Stdio {
409- fn from ( file : File ) -> Stdio {
485+ impl From < SysFile > for Stdio {
486+ fn from ( file : SysFile ) -> Stdio {
410487 Stdio :: Fd ( file. into_fd ( ) )
411488 }
412489}
0 commit comments