1010
1111use env:: { split_paths} ;
1212use ffi:: { CStr , OsStr } ;
13+ use fs:: File ;
1314use os:: unix:: ffi:: OsStrExt ;
1415use fmt;
16+ <<<<<<< HEAD
1517use io:: { self , Error , ErrorKind } ;
1618use iter;
19+ =======
20+ use io:: { self , prelude:: * , BufReader , Error , ErrorKind , SeekFrom } ;
21+ >>>>>>> b7b1d416a1... Interpret shebangs on redox
1722use libc:: { EXIT_SUCCESS , EXIT_FAILURE } ;
1823use path:: { Path , PathBuf } ;
1924use ptr;
25+ use sys:: ext:: fs:: MetadataExt ;
26+ use sys:: ext:: io:: AsRawFd ;
2027use sys:: fd:: FileDesc ;
21- use sys:: fs:: { File , OpenOptions } ;
28+ use sys:: fs:: { File as SysFile , OpenOptions } ;
2229use sys:: os:: { ENV_LOCK , environ } ;
2330use sys:: pipe:: { self , AnonPipe } ;
2431use sys:: { cvt , syscall } ;
@@ -317,20 +324,80 @@ impl Command {
317324 None
318325 } ;
319326
320- let fd = if let Some ( program) = program {
321- t ! ( cvt ( syscall :: open( program. as_os_str( ) . as_bytes ( ) , syscall :: O_RDONLY | syscall :: O_CLOEXEC ) ) )
327+ let mut file = if let Some ( program) = program {
328+ t ! ( File :: open( program. as_os_str( ) ) )
322329 } else {
323330 return io:: Error :: from_raw_os_error ( syscall:: ENOENT ) ;
324331 } ;
325332
326- let mut args: Vec < [ usize ; 2 ] > = iter:: once (
327- [ self . program . as_ptr ( ) as usize , self . program . len ( ) ]
328- ) . chain (
329- self . args . iter ( ) . map ( |arg| [ arg. as_ptr ( ) as usize , arg. len ( ) ] )
330- ) . collect ( ) ;
333+ // Push all the arguments
334+ let mut args: Vec < [ usize ; 2 ] > = Vec :: with_capacity ( 1 + self . args . len ( ) ) ;
331335
336+ let interpreter = {
337+ let mut reader = BufReader :: new ( & file) ;
338+
339+ let mut shebang = [ 0 ; 2 ] ;
340+ let mut read = 0 ;
341+ loop {
342+ match t ! ( reader. read( & mut shebang[ read..] ) ) {
343+ 0 => break ,
344+ n => read += n,
345+ }
346+ }
347+
348+ if & shebang == b"#!" {
349+ // This is an interpreted script.
350+ // First of all, since we'll be passing another file to
351+ // fexec(), we need to manually check that we have permission
352+ // to execute this file:
353+ let uid = t ! ( cvt( syscall:: getuid( ) ) ) ;
354+ let gid = t ! ( cvt( syscall:: getgid( ) ) ) ;
355+ let meta = t ! ( file. metadata( ) ) ;
356+
357+ let mode = if uid == meta. uid ( ) as usize {
358+ meta. mode ( ) >> 3 * 2 & 0o7
359+ } else if gid == meta. gid ( ) as usize {
360+ meta. mode ( ) >> 3 * 1 & 0o7
361+ } else {
362+ meta. mode ( ) & 0o7
363+ } ;
364+ if mode & 1 == 0 {
365+ return io:: Error :: from_raw_os_error ( syscall:: EPERM ) ;
366+ }
367+
368+ // Second of all, we need to actually read which interpreter it wants
369+ let mut interpreter = Vec :: new ( ) ;
370+ t ! ( reader. read_until( b'\n' , & mut interpreter) ) ;
371+ // Pop one trailing newline, if any
372+ if interpreter. ends_with ( & [ b'\n' ] ) {
373+ interpreter. pop ( ) . unwrap ( ) ;
374+ }
375+
376+ // TODO: Here we could just reassign `file` directly, if it
377+ // wasn't for lexical lifetimes. Remove the whole `let
378+ // interpreter = { ... };` hack once NLL lands.
379+ // NOTE: Although DO REMEMBER to make sure the interpreter path
380+ // still lives long enough to reach fexec.
381+ Some ( interpreter)
382+ } else {
383+ None
384+ }
385+ } ;
386+ if let Some ( ref interpreter) = interpreter {
387+ let path: & OsStr = OsStr :: from_bytes ( & interpreter) ;
388+ file = t ! ( File :: open( path) ) ;
389+
390+ args. push ( [ interpreter. as_ptr ( ) as usize , interpreter. len ( ) ] ) ;
391+ } else {
392+ t ! ( file. seek( SeekFrom :: Start ( 0 ) ) ) ;
393+ }
394+
395+ args. push ( [ self . program . as_ptr ( ) as usize , self . program . len ( ) ] ) ;
396+ args. extend ( self . args . iter ( ) . map ( |arg| [ arg. as_ptr ( ) as usize , arg. len ( ) ] ) ) ;
397+
398+ // Push all the variables
332399 let mut vars: Vec < [ usize ; 2 ] > = Vec :: new ( ) ;
333- unsafe {
400+ {
334401 let _guard = ENV_LOCK . lock ( ) ;
335402 let mut environ = * environ ( ) ;
336403 while * environ != ptr:: null ( ) {
@@ -340,8 +407,7 @@ impl Command {
340407 }
341408 }
342409
343- if let Err ( err) = syscall:: fexec ( fd, & args, & vars) {
344- let _ = syscall:: close ( fd) ;
410+ if let Err ( err) = syscall:: fexec ( file. as_raw_fd ( ) , & args, & vars) {
345411 io:: Error :: from_raw_os_error ( err. errno as i32 )
346412 } else {
347413 panic ! ( "return from exec without err" ) ;
@@ -408,7 +474,7 @@ impl Stdio {
408474 let mut opts = OpenOptions :: new ( ) ;
409475 opts. read ( readable) ;
410476 opts. write ( !readable) ;
411- let fd = File :: open ( Path :: new ( "null:" ) , & opts) ?;
477+ let fd = SysFile :: open ( Path :: new ( "null:" ) , & opts) ?;
412478 Ok ( ( ChildStdio :: Owned ( fd. into_fd ( ) ) , None ) )
413479 }
414480 }
@@ -421,8 +487,8 @@ impl From<AnonPipe> for Stdio {
421487 }
422488}
423489
424- impl From < File > for Stdio {
425- fn from ( file : File ) -> Stdio {
490+ impl From < SysFile > for Stdio {
491+ fn from ( file : SysFile ) -> Stdio {
426492 Stdio :: Fd ( file. into_fd ( ) )
427493 }
428494}
0 commit comments