Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dd: open stdin from file descriptor when possible #4189

Merged
merged 2 commits into from
Mar 18, 2023

Conversation

jfinkels
Copy link
Collaborator

@jfinkels jfinkels commented Nov 27, 2022

This is a draft pull request because it includes the changes from pull request #4164. Review and merge that one first.

This pull request causes dd to open stdin using its file descriptor so that a dd skip=N command in a subshell does not consume all bytes from stdin.

For example, before this commit, multiple instances of dd reading from stdin and appearing in a single command line would incorrectly result in an empty stdin for each instance of dd after the first:

$ printf "abcdef\n" | (dd bs=1 skip=3 count=0 && dd)  2> /dev/null
# incorrectly results in no output

After this commit, the dd skip=3 process reads three bytes from the file descriptor referring to stdin without draining the remaining three bytes when it terminates:

$ printf "abcdef\n" | (dd bs=1 skip=3 count=0 && dd)  2> /dev/null
def

This implementation is inspired by #3662. I don't think this quite resolves #3008 but it is in the right direction. It does seem to get GNU test tests/dd/skip-seek2.sh to pass.

@github-actions
Copy link

GNU testsuite comparison:

Congrats! The gnu test tests/dd/skip-seek2 is no longer failing!
GNU test failed: tests/dd/no-allocate. tests/dd/no-allocate is passing on 'main'. Maybe you have to rebase?
GNU test failed: tests/misc/tee. tests/misc/tee is passing on 'main'. Maybe you have to rebase?
GNU test failed: tests/tail-2/inotify-dir-recreate. tests/tail-2/inotify-dir-recreate is passing on 'main'. Maybe you have to rebase?
Congrats! The gnu test tests/dd/no-allocate is no longer ERROR!

@jfinkels jfinkels force-pushed the dd-stdin-from-file-descriptor branch from 5a5ca37 to 11c7f40 Compare December 2, 2022 02:58
@github-actions
Copy link

github-actions bot commented Dec 2, 2022

GNU testsuite comparison:

Congrats! The gnu test tests/dd/skip-seek2 is no longer failing!
Congrats! The gnu test tests/tail-2/inotify-dir-recreate is no longer failing!
GNU test failed: tests/dd/no-allocate. tests/dd/no-allocate is passing on 'main'. Maybe you have to rebase?
GNU test failed: tests/misc/timeout. tests/misc/timeout is passing on 'main'. Maybe you have to rebase?
Congrats! The gnu test tests/dd/no-allocate is no longer ERROR!

@sylvestre sylvestre force-pushed the dd-stdin-from-file-descriptor branch from 11c7f40 to d0d72cd Compare December 2, 2022 09:39
@github-actions
Copy link

github-actions bot commented Dec 2, 2022

GNU testsuite comparison:

Congrats! The gnu test tests/dd/skip-seek2 is no longer failing!
GNU test failed: tests/dd/no-allocate. tests/dd/no-allocate is passing on 'main'. Maybe you have to rebase?
GNU test failed: tests/tail-2/inotify-dir-recreate. tests/tail-2/inotify-dir-recreate is passing on 'main'. Maybe you have to rebase?
Congrats! The gnu test tests/dd/no-allocate is no longer ERROR!

@github-actions
Copy link

GNU testsuite comparison:

Congrats! The gnu test tests/dd/skip-seek2 is no longer failing!
GNU test failed: tests/dd/no-allocate. tests/dd/no-allocate is passing on 'main'. Maybe you have to rebase?
GNU test failed: tests/rm/rm2. tests/rm/rm2 is passing on 'main'. Maybe you have to rebase?
Congrats! The gnu test tests/dd/no-allocate is no longer ERROR!

@jfinkels jfinkels force-pushed the dd-stdin-from-file-descriptor branch from 0ef5112 to 6af72d0 Compare February 23, 2023 01:29
@jfinkels jfinkels marked this pull request as ready for review February 23, 2023 02:18
@jfinkels
Copy link
Collaborator Author

I rebased this on main now that #4164 has been merged.

@github-actions
Copy link

GNU testsuite comparison:

Congrats! The gnu test tests/dd/skip-seek2 is no longer failing!
Congrats! The gnu test tests/tail-2/inotify-dir-recreate is no longer failing!

@tertsdiepraam
Copy link
Member

Could you add a test for this somehow? Maybe by testing the script from the description?

@jfinkels
Copy link
Collaborator Author

I'll give it a shot and report back.

@tertsdiepraam
Copy link
Member

Not sure, but this might help: #4293

@jfinkels
Copy link
Collaborator Author

jfinkels commented Mar 5, 2023

@Joining7943 since you worked on the UCommand implementation recently in #4293, do you know if it provides an obvious way for us to create a test like the following, where there are two invocations of the dd util joined by an &&:

$ printf "abcdef\n" | (dd bs=1 skip=3 count=0 && dd)  2> /dev/null
def

@Joining7943
Copy link
Contributor

I agree with @tertsdiepraam. The most obvious way I can think of, is to use the shell, although I don't know if the windows cmd shell supports the parentheses the same way the sh shell does. Maybe somethink like this?

use crate::common::util::; // includes the constant TESTS_BINARY

#[cfg(feature = "printf")
#[test]
fn test() {
UCommand::new().arg(format!("{TESTS_BINARY} printf 'abcdef\n' | ( {TESTS_BINARY} dd bs=1 skip=3 count=0 && {TESTS_BINARY} dd ) 2> /dev/null")).run();

// or if you need to pass different arguments to the shell for unix and windows

UCommand::new().arg(
  #[cfg(unix)]
  // unix arg
  #[cfg(windows)]
  // windows arg
).run();
}

@jfinkels
Copy link
Collaborator Author

jfinkels commented Mar 5, 2023

I'll try that. Thank you!

@Joining7943
Copy link
Contributor

Sure :)

@jfinkels jfinkels force-pushed the dd-stdin-from-file-descriptor branch from 6af72d0 to f4620f6 Compare March 10, 2023 15:33
@jfinkels
Copy link
Collaborator Author

Rebased on main and added a test.

@github-actions
Copy link

GNU testsuite comparison:

Congrats! The gnu test tests/dd/skip-seek2 is no longer failing!

Open stdin using its file descriptor so that a `dd skip=N` command in
a subshell does not consume all bytes from stdin.

For example, before this commit, multiple instances of `dd` reading
from stdin and appearing in a single command line would incorrectly
result in an empty stdin for each instance of `dd` after the first:

    $ printf "abcdef\n" | (dd bs=1 skip=3 count=0 && dd)  2> /dev/null
    # incorrectly results in no output

After this commit, the `dd skip=3` process reads three bytes from the
file descriptor referring to stdin without draining the remaining
three bytes when it terminates:

    $ printf "abcdef\n" | (dd bs=1 skip=3 count=0 && dd)  2> /dev/null
    def
@jfinkels jfinkels force-pushed the dd-stdin-from-file-descriptor branch from f4620f6 to 9cb6b4a Compare March 11, 2023 20:56
@github-actions
Copy link

GNU testsuite comparison:

Congrats! The gnu test tests/dd/skip-seek2 is no longer failing!
GNU test failed: tests/misc/tee. tests/misc/tee is passing on 'main'. Maybe you have to rebase?

@jfinkels
Copy link
Collaborator Author

The remaining CI error is due to #4487. Other than that, this is ready for review again

src/uu/dd/src/dd.rs Show resolved Hide resolved
@@ -116,6 +142,15 @@ impl Source {
Ok(m) => Ok(m),
Err(e) => Err(e),
},
#[cfg(unix)]
Self::StdinFile(f) => match io::copy(&mut f.take(n), &mut io::sink()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose we can't use file seeking operations here, because stdin doesn't support that even when you open as file?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't try, but that was my assumption.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth trying? I'm fine with the current implementation too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can take a look now and remind myself what happens when using seek(). I'll report back

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to cause a bunch of Broken pipe (os error 32) and Illegal seek errors throughout the tests if I use f.seek() here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, thanks!

@github-actions
Copy link

GNU testsuite comparison:

Congrats! The gnu test tests/dd/skip-seek2 is no longer failing!
GNU test failed: tests/tail-2/inotify-dir-recreate. tests/tail-2/inotify-dir-recreate is passing on 'main'. Maybe you have to rebase?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

dd: unexpectedly consumes all bytes from stdin
3 participants