diff --git a/bin/dd/dd.c b/bin/dd/dd.c --- a/bin/dd/dd.c +++ b/bin/dd/dd.c @@ -99,8 +99,7 @@ { struct itimerval itv = { { 1, 0 }, { 1, 0 } }; /* SIGALARM every second, if needed */ - (void)siginterrupt(SIGINT, 1); - (void)signal(SIGINT, terminate); + prepare_io(); (void)setlocale(LC_CTYPE, ""); jcl(argv); @@ -158,9 +157,9 @@ iflags = 0; if (ddflags & C_IDIRECT) iflags |= O_DIRECT; - check_terminate(); + before_io(); in.fd = open(in.name, O_RDONLY | iflags, 0); - check_terminate(); + after_io(); if (in.fd == -1) err(1, "%s", in.name); } @@ -197,17 +196,18 @@ oflags |= O_FSYNC; if (ddflags & C_ODIRECT) oflags |= O_DIRECT; - check_terminate(); + before_io(); out.fd = open(out.name, O_RDWR | oflags, DEFFILEMODE); - check_terminate(); + after_io(); /* * May not have read access, so try again with write only. * Without read we may have a problem if output also does * not support seeks. */ if (out.fd == -1) { + before_io(); out.fd = open(out.name, O_WRONLY | oflags, DEFFILEMODE); - check_terminate(); + after_io(); out.flags |= NOREAD; cap_rights_clear(&rights, CAP_READ); } @@ -424,9 +424,9 @@ in.dbrcnt = 0; fill: - check_terminate(); + before_io(); n = read(in.fd, in.dbp + in.dbrcnt, in.dbsz - in.dbrcnt); - check_terminate(); + after_io(); /* EOF */ if (n == 0 && in.dbrcnt == 0) @@ -607,9 +607,9 @@ pending = 0; } if (cnt) { - check_terminate(); + before_io(); nw = write(out.fd, outp, cnt); - check_terminate(); + after_io(); out.seek_offset = 0; } else { return; diff --git a/bin/dd/extern.h b/bin/dd/extern.h --- a/bin/dd/extern.h +++ b/bin/dd/extern.h @@ -49,8 +49,9 @@ void summary(void); void sigalarm_handler(int); void siginfo_handler(int); -void terminate(int); -void check_terminate(void); +void prepare_io(void); +void before_io(void); +void after_io(void); void unblock(void); void unblock_close(void); diff --git a/bin/dd/misc.c b/bin/dd/misc.c --- a/bin/dd/misc.c +++ b/bin/dd/misc.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -147,23 +148,58 @@ need_progress = 1; } -void +static void terminate(int signo) __dead2; +static void terminate(int signo) { - kill_signal = signo; + summary(); + (void)fflush(stderr); + raise(kill_signal); + /* NOT REACHED */ + _exit(1); +} + +static sig_atomic_t in_io = 0; +static sig_atomic_t sigint_seen = 0; + +static void +sigint_handler(int signo __unused) +{ + atomic_signal_fence(memory_order_acquire); + if (in_io) + terminate(SIGINT); + sigint_seen = 1; +} + +void +prepare_io(void) +{ + struct sigaction sa; + int error; + + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_NODEFER | SA_RESETHAND; + sa.sa_handler = sigint_handler; + error = sigaction(SIGINT, &sa, 0); + if (error != 0) + err(1, "sigaction"); } void -check_terminate(void) +before_io(void) { + in_io = 1; + atomic_signal_fence(memory_order_seq_cst); + if (sigint_seen) + terminate(SIGINT); +} - if (kill_signal) { - summary(); - (void)fflush(stderr); - signal(kill_signal, SIG_DFL); - raise(kill_signal); - /* NOT REACHED */ - _exit(128 + kill_signal); - } +void +after_io(void) +{ + in_io = 0; + atomic_signal_fence(memory_order_seq_cst); + if (sigint_seen) + terminate(SIGINT); } diff --git a/bin/dd/position.c b/bin/dd/position.c --- a/bin/dd/position.c +++ b/bin/dd/position.c @@ -191,10 +191,11 @@ /* Read it. */ for (cnt = 0; cnt < out.offset; ++cnt) { - check_terminate(); - if ((n = read(out.fd, out.db, out.dbsz)) > 0) + before_io(); + n = read(out.fd, out.db, out.dbsz); + after_io(); + if (n > 0) continue; - check_terminate(); if (n == -1) err(1, "%s", out.name); @@ -209,9 +210,9 @@ err(1, "%s", out.name); while (cnt++ < out.offset) { - check_terminate(); + before_io(); n = write(out.fd, out.db, out.dbsz); - check_terminate(); + after_io(); if (n == -1) err(1, "%s", out.name); if (n != out.dbsz)