summaryrefslogtreecommitdiff
path: root/src/dd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dd.c')
-rw-r--r--src/dd.c36
1 files changed, 34 insertions, 2 deletions
diff --git a/src/dd.c b/src/dd.c
index b2bdf118c..b8512fbb5 100644
--- a/src/dd.c
+++ b/src/dd.c
@@ -1325,12 +1325,44 @@ copy_with_unblock (char const *buf, size_t nread)
static void
set_fd_flags (int fd, int add_flags, char const *name)
{
+ /* Ignore file creation flags that are no-ops on file descriptors. */
+ add_flags &= ~ (O_NOCTTY | O_NOFOLLOW);
+
if (add_flags)
{
int old_flags = fcntl (fd, F_GETFL);
int new_flags = old_flags | add_flags;
- if (old_flags < 0
- || (new_flags != old_flags && fcntl (fd, F_SETFL, new_flags) == -1))
+ bool ok = true;
+ if (old_flags < 0)
+ ok = false;
+ else if (old_flags != new_flags)
+ {
+ if (new_flags & (O_DIRECTORY | O_NOLINKS))
+ {
+ /* NEW_FLAGS contains at least one file creation flag that
+ requires some checking of the open file descriptor. */
+ struct stat st;
+ if (fstat (fd, &st) != 0)
+ ok = false;
+ else if ((new_flags & O_DIRECTORY) && ! S_ISDIR (st.st_mode))
+ {
+ errno = ENOTDIR;
+ ok = false;
+ }
+ else if ((new_flags & O_NOLINKS) && 1 < st.st_nlink)
+ {
+ errno = EMLINK;
+ ok = false;
+ }
+ new_flags &= ~ (O_DIRECTORY | O_NOLINKS);
+ }
+
+ if (ok && old_flags != new_flags
+ && fcntl (fd, F_SETFL, new_flags) == -1)
+ ok = false;
+ }
+
+ if (!ok)
error (EXIT_FAILURE, errno, _("setting flags for %s"), quote (name));
}
}