-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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 stdout from file descriptor when possible #4542
Conversation
src/uu/dd/src/dd.rs
Outdated
None if is_stdout_redirected_to_seekable_file() => { | ||
Output::new_file(Path::new(&stdout_canonicalized()), &settings)? | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this workaround is now handled more appropriately in the new_stdout()
case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the seeking is different now, isn't it? Because StdoutFile
copies 0
's instead of seeking. Also does it matter that the StdoutFile
is only available on unix, while this match expression works on all platforms?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jfinkels ping ? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the seeking is different now, isn't it? Because
StdoutFile
copies0
's instead of seeking.
Hmm, yes I guess this removal was premature. It seems like there should be a way to unify these two code paths in a cleaner way, but maybe I'll just postpone that until a later pull request. For example, maybe the decision about whether the destination is seekable could move inside the new_stdout()
constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh actually, I forgot---if I leave this branch in, then the new test case fails:
$ printf "abcde\n" | (dd bs=1 skip=1 count=1 && dd bs=1 skip=1) 2> /dev/null
bde
(this comes from the GNU test suite). For reference, we added the if is_stdout_redirected_to_seekable_file()
in pull request #3880.
Let me see if there's a way to reconcile the two.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just updated this branch to keep the None if is_stdout_redirected_to_seekable_file() => {
branch. There seems to be an issue with the new test case I added, but I'm not sure what the problem is.
When I run the test case from the command line I get the expected behavior:
$ printf "abcde\n" | (./target/debug/dd bs=1 skip=1 count=1; ./target/debug/dd bs=1 skip=1) 2> /dev/null
bde
but when I run cargo test
I get a failure:
$ cargo test test_dd::test_multiple_processes_writing_stdout
[...]
---- test_dd::test_multiple_processes_writing_stdout stdout ----
run: /bin/sh -c /home/jeffrey/src/coreutils/target/debug/coreutils printf 'abcde
' | ( /home/jeffrey/src/coreutils/target/debug/coreutils dd bs=1 skip=1 count=1; /home/jeffrey/src/coreutils/target/debug/coreutils dd bs=1 skip=1 ) 2> /dev/null
thread 'test_dd::test_multiple_processes_writing_stdout' panicked at 'assertion failed: `(left == right)`
Diff < left / right > :
<de
>bde
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All good apart from the comment above, which is more a question than a concern
I plan to look at this again soon, thanks for the ping
…------- Original Message -------
On Friday, April 28th, 2023 at 3:34 AM, Sylvestre Ledru ***@***.***> wrote:
@sylvestre commented on this pull request.
---------------------------------------------------------------
In [src/uu/dd/src/dd.rs](#4542 (comment)):
> - None if is_stdout_redirected_to_seekable_file() => {
- Output::new_file(Path::new(&stdout_canonicalized()), &settings)?
- }
***@***.***(https://github.com/jfinkels) ping ? :)
—
Reply to this email directly, [view it on GitHub](#4542 (comment)), or [unsubscribe](https://github.com/notifications/unsubscribe-auth/AAA5XG652MODQFJOASC66P3XDNXJ7ANCNFSM6AAAAAAV7SP2XM).
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
fc000ea
to
b613dce
Compare
GNU testsuite comparison:
|
GNU testsuite comparison:
|
Failed with:
|
454e6da
to
fad41c2
Compare
Yeah I'm stuck here unfortunately, I don't know enough to diagnose the issue. It works as expected when I run the command manually on the command line:
but the same fails when running |
GNU testsuite comparison:
|
I've narrowed this down to Here's a theory: we pipe the stdout to a file, what if that file is not opened in append mode, but truncated on every new command that writes to stdout? Update: it's a bit simpler. This gives
So possibly we're being too eager with trying to seek to the start of the file? Update 2: I think treating the implementation of treating stdout as a file directly is wrong, because the seek, truncate and append defaults are different for piping to stdout. This diff fixes the behaviour for stdout (but obviously break the implementation for normal files). Does that give you enough info to fix it? @@ -693,7 +693,7 @@ impl<'a> Output<'a> {
opts.write(true)
.create(!cflags.nocreat)
.create_new(cflags.excl)
- .append(oflags.append);
+ .append(true);
#[cfg(any(target_os = "linux", target_os = "android"))]
if let Some(libc_flags) = make_linux_oflags(oflags) {
@@ -714,9 +714,6 @@ impl<'a> Output<'a> {
// suppress the error by calling `Result::ok()`. This matches
// the behavior of GNU `dd` when given the command-line
// argument `of=/dev/null`.
- if !settings.oconv.notrunc {
- dst.set_len(settings.seek).ok();
- }
let density = if settings.oconv.sparse {
Density::Sparse
} else { |
Open stdout using its file descriptor so that a `dd skip=N` command in a subprocess does not close stdout. For example, before this commit, multiple instances of `dd` writing to stdout and appearing in a single command line could incorrectly result in an empty stdout for each instance of `dd` after the first: $ printf "abcde\n" | (dd bs=1 skip=1 count=1 && dd bs=1 skip=1) 2> /dev/null b After this commit, each `dd` process writes to the file descriptor referring to stdout: $ printf "abcde\n" | (dd bs=1 skip=1 count=1 && dd bs=1 skip=1) 2> /dev/null bde
fad41c2
to
c1ba7c9
Compare
@jfinkels still interested in finishing this? |
I'll update the test to use the standard library
Yes, this is helpful. I'll try to adapt that idea to support all the necessary cases. |
seems that it is stuck - closing to remove it from the list |
(Complements pull request #4189.)
Open stdout using its file descriptor so that a
dd skip=N
command in a subprocess does not close stdout.For example, before this commit, multiple instances of
dd
writing to stdout and appearing in a single command line could incorrectly result in an empty stdout for each instance ofdd
after the first:After this commit, each
dd
process writes to the file descriptor referring to stdout: