From 6bc2a14987afae23a63bc514b1e1755c0874ddb8 Mon Sep 17 00:00:00 2001 From: wangmengc Date: Fri, 7 Jun 2024 14:13:10 +0800 Subject: [PATCH 1/4] add copy_buffered_stream function --- utshell-0.5/r_input/src/lib.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/utshell-0.5/r_input/src/lib.rs b/utshell-0.5/r_input/src/lib.rs index e28266c7..4d943536 100644 --- a/utshell-0.5/r_input/src/lib.rs +++ b/utshell-0.5/r_input/src/lib.rs @@ -368,4 +368,23 @@ unsafe extern "C" fn make_buffered_stream( (*bp).b_flag |= B_TEXT; } return bp; +} + + +/* Allocate a new BUFFERED_STREAM, copy BP to it, and return the new copy. */ +unsafe extern "C" fn copy_buffered_stream(mut bp: *mut BUFFERED_STREAM) -> *mut BUFFERED_STREAM { + let mut nbp: *mut BUFFERED_STREAM = 0 as *mut BUFFERED_STREAM; + + if bp.is_null() { + return 0 as *mut libc::c_void as *mut BUFFERED_STREAM; + } + + nbp = libc::malloc(::core::mem::size_of::() as libc::c_ulong as usize) + as *mut BUFFERED_STREAM; + xbcopy( + bp as *mut libc::c_char, + nbp as *mut libc::c_char, + ::core::mem::size_of::() as libc::c_ulong as libc::c_int, + ); + return nbp; } \ No newline at end of file -- Gitee From b0c970fbe16fdcad9c169d54293e201d361f735f Mon Sep 17 00:00:00 2001 From: wangmengc Date: Fri, 7 Jun 2024 14:20:07 +0800 Subject: [PATCH 2/4] add set_bash_input_fd fd_is_bash_input function --- utshell-0.5/r_input/src/lib.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/utshell-0.5/r_input/src/lib.rs b/utshell-0.5/r_input/src/lib.rs index 4d943536..4f83889c 100644 --- a/utshell-0.5/r_input/src/lib.rs +++ b/utshell-0.5/r_input/src/lib.rs @@ -387,4 +387,27 @@ unsafe extern "C" fn copy_buffered_stream(mut bp: *mut BUFFERED_STREAM) -> *mut ::core::mem::size_of::() as libc::c_ulong as libc::c_int, ); return nbp; +} + + +#[no_mangle] +pub unsafe extern "C" fn set_bash_input_fd(mut fd: libc::c_int) -> libc::c_int { + if bash_input.type_0 as libc::c_uint == st_bstream as libc::c_int as libc::c_uint { + bash_input.location.buffered_fd = fd; + } else if interactive_shell == 0 as libc::c_int { + default_buffered_input = fd; + } + return 0 as libc::c_int; +} + +#[no_mangle] +pub unsafe extern "C" fn fd_is_bash_input(mut fd: libc::c_int) -> libc::c_int { + if bash_input.type_0 as libc::c_uint == st_bstream as libc::c_int as libc::c_uint + && bash_input.location.buffered_fd == fd + { + return 1 as libc::c_int; + } else if interactive_shell == 0 as libc::c_int && default_buffered_input == fd { + return 1 as libc::c_int; + } + return 0 as libc::c_int; } \ No newline at end of file -- Gitee From f064d8fe1bf8315ce4ff1ad347733a83ad3ced59 Mon Sep 17 00:00:00 2001 From: wangmengc Date: Fri, 7 Jun 2024 14:37:45 +0800 Subject: [PATCH 3/4] add save_bash_input function --- utshell-0.5/r_input/src/lib.rs | 83 ++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/utshell-0.5/r_input/src/lib.rs b/utshell-0.5/r_input/src/lib.rs index 4f83889c..94c7f66f 100644 --- a/utshell-0.5/r_input/src/lib.rs +++ b/utshell-0.5/r_input/src/lib.rs @@ -410,4 +410,87 @@ pub unsafe extern "C" fn fd_is_bash_input(mut fd: libc::c_int) -> libc::c_int { return 1 as libc::c_int; } return 0 as libc::c_int; +} + + +/* Save the buffered stream corresponding to file descriptor FD (which bash +is using to read input) to a buffered stream associated with NEW_FD. If +NEW_FD is -1, a new file descriptor is allocated with fcntl. The new +file descriptor is returned on success, -1 on error. */ +#[no_mangle] +pub unsafe extern "C" fn save_bash_input( + mut fd: libc::c_int, + mut new_fd: libc::c_int, +) -> libc::c_int { + let mut nfd: libc::c_int = 0; + + /* Sync the stream so we can re-read from the new file descriptor. We + might be able to avoid this by copying the buffered stream verbatim + to the new file descriptor. */ + if !(*buffers.offset(fd as isize)).is_null() { + sync_buffered_stream(fd); + } + + /* Now take care of duplicating the file descriptor that bash is + using for input, so we can reinitialize it later. */ + nfd = if new_fd == -(1 as libc::c_int) { + fcntl(fd, 0 as libc::c_int, 10 as libc::c_int) + } else { + new_fd + }; + if nfd == -(1 as libc::c_int) { + if fcntl(fd, 1 as libc::c_int, 0 as libc::c_int) == 0 as libc::c_int { + sys_error( + dcgettext( + 0 as *const libc::c_char, + b"cannot allocate new file descriptor for bash input from fd %d\0" as *const u8 + as *const libc::c_char, + 5 as libc::c_int, + ), + fd, + ); + } + return -(1 as libc::c_int); + } + + if nfd < nbuffers && !(*buffers.offset(nfd as isize)).is_null() { + /* What's this? A stray buffer without an associated open file + descriptor? Free up the buffer and report the error. */ + internal_error( + dcgettext( + 0 as *const libc::c_char, + b"save_bash_input: buffer already exists for new fd %d\0" as *const u8 + as *const libc::c_char, + 5 as libc::c_int, + ), + nfd, + ); + if (**buffers.offset(nfd as isize)).b_flag & B_SHAREDBUF as libc::c_int != 0 { + let ref mut fresh3 = (**buffers.offset(nfd as isize)).b_buffer; + *fresh3 = 0 as *mut libc::c_void as *mut libc::c_char; + } + free_buffered_stream(*buffers.offset(nfd as isize)); + } + + /* Reinitialize bash_input.location. */ + if bash_input.type_0 as libc::c_uint == st_bstream as libc::c_int as libc::c_uint { + bash_input.location.buffered_fd = nfd; + fd_to_buffered_stream(nfd); + close_buffered_fd(fd); /* XXX */ + } else { + /* If the current input type is not a buffered stream, but the shell + is not interactive and therefore using a buffered stream to read + input (e.g. with an `eval exec 3>output' inside a script), note + that the input fd has been changed. pop_stream() looks at this + value and adjusts the input fd to the new value of + default_buffered_input accordingly. */ + bash_input_fd_changed += 1; + bash_input_fd_changed; + } + if default_buffered_input == fd { + default_buffered_input = nfd; + } + + SET_CLOSE_ON_EXEC!(nfd); + return nfd; } \ No newline at end of file -- Gitee From ac5a05b0071226d05545fe528f0d6455e120d077 Mon Sep 17 00:00:00 2001 From: wangmengc Date: Tue, 11 Jun 2024 09:25:32 +0800 Subject: [PATCH 4/4] add check_bash_input and duplicate_buffered_stream function --- utshell-0.5/r_input/src/lib.rs | 96 ++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/utshell-0.5/r_input/src/lib.rs b/utshell-0.5/r_input/src/lib.rs index 94c7f66f..d974c2b2 100644 --- a/utshell-0.5/r_input/src/lib.rs +++ b/utshell-0.5/r_input/src/lib.rs @@ -493,4 +493,100 @@ pub unsafe extern "C" fn save_bash_input( SET_CLOSE_ON_EXEC!(nfd); return nfd; +} + +/* Check that file descriptor FD is not the one that bash is currently +using to read input from a script. FD is about to be duplicated onto, +which means that the kernel will close it for us. If FD is the bash +input file descriptor, we need to seek backwards in the script (if +possible and necessary -- scripts read from stdin are still unbuffered), +allocate a new file descriptor to use for bash input, and re-initialize +the buffered stream. Make sure the file descriptor used to save bash +input is set close-on-exec. Returns 0 on success, -1 on failure. This +works only if fd is > 0 -- if fd == 0 and bash is reading input from +fd 0, sync_buffered_stream is used instead, to cooperate with input +redirection (look at redir.c:add_undo_redirect()). */ +#[no_mangle] +pub unsafe extern "C" fn check_bash_input(mut fd: libc::c_int) -> libc::c_int { + if fd_is_bash_input(fd) != 0 { + if fd > 0 as libc::c_int { + return if save_bash_input(fd, -(1 as libc::c_int)) == -(1 as libc::c_int) { + -(1 as libc::c_int) + } else { + 0 as libc::c_int + }; + } else if fd == 0 as libc::c_int { + return if sync_buffered_stream(fd) == -(1 as libc::c_int) { + -(1 as libc::c_int) + } else { + 0 as libc::c_int + }; + } + } + return 0 as libc::c_int; +} + +/* This is the buffered stream analogue of dup2(fd1, fd2). The +BUFFERED_STREAM corresponding to fd2 is deallocated, if one exists. +BUFFERS[fd1] is copied to BUFFERS[fd2]. This is called by the +redirect code for constructs like 4<&0 and 3 libc::c_int { + let mut is_bash_input: libc::c_int = 0; + let mut m: libc::c_int = 0; + + if fd1 == fd2 { + return 0 as libc::c_int; + } + + m = max!(fd1, fd2); + ALLOCATE_BUFFERS!(m); + + /* If FD2 is the file descriptor bash is currently using for shell input, + we need to do some extra work to make sure that the buffered stream + actually exists (it might not if fd1 was not active, and the copy + didn't actually do anything). */ + is_bash_input = (bash_input.type_0 as libc::c_uint == st_bstream as libc::c_int as libc::c_uint + && bash_input.location.buffered_fd == fd2) as libc::c_int; + + if !(*buffers.offset(fd2 as isize)).is_null() { + /* If the two objects share the same b_buffer, don't free it. */ + if !(*buffers.offset(fd1 as isize)).is_null() + && !((**buffers.offset(fd1 as isize)).b_buffer).is_null() + && (**buffers.offset(fd1 as isize)).b_buffer + == (**buffers.offset(fd2 as isize)).b_buffer + { + let ref mut fresh4 = *buffers.offset(fd2 as isize); + *fresh4 = 0 as *mut libc::c_void as *mut BUFFERED_STREAM; + /* If this buffer is shared with another fd, don't free the buffer */ + } else if (**buffers.offset(fd2 as isize)).b_flag & B_SHAREDBUF as libc::c_int != 0 { + let ref mut fresh5 = (**buffers.offset(fd2 as isize)).b_buffer; + *fresh5 = 0 as *mut libc::c_void as *mut libc::c_char; + free_buffered_stream(*buffers.offset(fd2 as isize)); + } else { + free_buffered_stream(*buffers.offset(fd2 as isize)); + } + } + let ref mut fresh6 = *buffers.offset(fd2 as isize); + *fresh6 = copy_buffered_stream(*buffers.offset(fd1 as isize)); + if !(*buffers.offset(fd2 as isize)).is_null() { + (**buffers.offset(fd2 as isize)).b_fd = fd2; + } + if is_bash_input != 0 { + if (*buffers.offset(fd2 as isize)).is_null() { + fd_to_buffered_stream(fd2); + } + (**buffers.offset(fd2 as isize)).b_flag |= B_WASBASHINPUT as libc::c_int; + } + if fd_is_bash_input(fd1) != 0 + || !(*buffers.offset(fd1 as isize)).is_null() + && (**buffers.offset(fd1 as isize)).b_flag & B_SHAREDBUF as libc::c_int != 0 + { + (**buffers.offset(fd2 as isize)).b_flag |= B_SHAREDBUF as libc::c_int; + } + + return fd2; } \ No newline at end of file -- Gitee