bug-coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: dd new iflag= oflag= flags directory, nolinks


From: Paul Eggert
Subject: Re: dd new iflag= oflag= flags directory, nolinks
Date: Wed, 08 Mar 2006 19:02:34 -0000
User-agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux)

Phillip Susi <address@hidden> writes:

> Ok, so this allows you to atomically test if the named object is both a
> directory and is readable, but why bother?

You might want to bother if you want to test some filesystem code
using a shell script.  That's what I was doing.  I wanted to make sure
that O_DIRECTORY worked, even with a symbolic link to a directory.  I
couldn't do that with the shell and standard utilites, until I added
iflag=directory.

> The purpose of dd is to transfer data.

No, dd has other purposes.  For example, you can use dd to reposition
standard input without reading it.  There's an example of this in the
coreutils manual.

The original purpose of the shell was to provide a thin layer over
operating system services.  The idea was that there was a simple
scriptable interface to pretty much anything you could do with a
system call.  This change fits within that tradition.  (Obviously the
tradition is not always upheld, but that's a different matter.)

(One could also argue that the original purpose of 'dd' was to mimic
the DD directive of OS/360 JCL
<http://www.okstate.edu/cis_info/cis_manual/jcl_data.html>, and the
'directory' flag is also well within this tradition.  :-)


Anyway, your email caused me to look again at the 'dd' source code
and test cases and NEWS, and I installed these further improvements.
Thanks.

2006-03-08  Paul Eggert  <address@hidden>

        * NEWS: Document dd's new 'directory' and 'nolinks' flags.
        * src/dd.c (set_fd_flags): Handle file-creation flags on file
        descriptors, rather than ignoring them.
        * tests/dd/misc: Add test cases for append, nofollow, directory,
        and nolinks flags.  Simplify redirection to /dev/null in some cases.

Index: NEWS
===================================================================
RCS file: /fetish/cu/NEWS,v
retrieving revision 1.361
diff -p -u -r1.361 NEWS
--- NEWS        27 Feb 2006 10:47:56 -0000      1.361
+++ NEWS        8 Mar 2006 18:55:23 -0000
@@ -71,9 +71,18 @@ GNU coreutils NEWS                      
 
 ** New features
 
-  dd's new iflag=noatime option causes it to read a file without
-  updating its access time, on hosts that support this (currently only
-  Linux kernels, version 2.6.8 and later).
+  New dd iflag= and oflag= flags:
+
+    'directory' causes dd to fail unless the file is a directory, on
+    hosts that support this (e.g., Linux kernels, version 2.1.126 and
+    later).  This has limited utility but is present for completeness.
+
+    'noatime' causes dd to read a file without updating its access
+    time, on hosts that support this (e.g., Linux kernels, version
+    2.6.8 and later).
+
+    'nolinks' causes dd to fail if the file has multiple hard links,
+    on hosts that support this (e.g., Solaris 10 and later).
 
   rm now accepts the -I (--interactive=once) option.  This new option
   prompts once if rm is invoked recursively or if more than three
Index: src/dd.c
===================================================================
RCS file: /fetish/cu/src/dd.c,v
retrieving revision 1.191
diff -p -u -r1.191 dd.c
--- src/dd.c    6 Mar 2006 07:20:19 -0000       1.191
+++ src/dd.c    8 Mar 2006 18:55:24 -0000
@@ -1325,12 +1325,44 @@ copy_with_unblock (char const *buf, size
 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));
     }
 }
Index: tests/dd/misc
===================================================================
RCS file: /fetish/cu/tests/dd/misc,v
retrieving revision 1.5
diff -p -u -r1.5 misc
--- tests/dd/misc       8 Mar 2006 18:21:54 -0000       1.5
+++ tests/dd/misc       8 Mar 2006 18:55:24 -0000
@@ -1,8 +1,10 @@
 #!/bin/sh
 # Ensure dd treats `--' properly.
-# Also ensure that iflag=noatime works.
+# Also test some flag values.
 
 tmp_in=dd-in.$$
+tmp_in2=dd-in2.$$
+tmp_sym=dd-sym.$$
 tmp_out=dd-out.$$
 
 if test "$VERBOSE" = yes; then
@@ -12,21 +14,36 @@ fi
 
 test_failure=0
 echo data > $tmp_in || test_failure=1
+ln $tmp_in $tmp_in2 || test_failure=1
+ln -s $tmp_in $tmp_sym || test_failure=1
 if test $test_failure = 1; then
   echo 'failure in testing framework'
   exit 1
 fi
 
-dd if=$tmp_in of=$tmp_out > /dev/null 2>&1 || fail=1
+dd if=$tmp_in of=$tmp_out 2> /dev/null || fail=1
 cmp $tmp_in $tmp_out || fail=1
 
 rm $tmp_out
-dd -- if=$tmp_in of=$tmp_out > /dev/null 2>&1 || fail=1
+dd -- if=$tmp_in of=$tmp_out 2> /dev/null || fail=1
 cmp $tmp_in $tmp_out || fail=1
 
+if dd oflag=append if=$tmp_in of=$tmp_out 2> /dev/null; then
+  cmp $tmp_in $tmp_out || fail=1
+fi
+
+if dd iflag=nofollow if=$tmp_in count=0 2> /dev/null; then
+  dd iflag=nofollow if=$tmp_sym count=0 2> /dev/null && fail=1
+fi
+
+if dd iflag=directory if=. count=0 2> /dev/null; then
+  dd iflag=directory count=0 <. 2> /dev/null || fail=1
+  dd iflag=directory count=0 <$tmp_in 2> /dev/null && fail=1
+fi
+
 old_ls=`ls -u --full-time $tmp_in`
 sleep 1
-if dd iflag=noatime if=$tmp_in of=$tmp_out > /dev/null 2>&1; then
+if dd iflag=noatime if=$tmp_in of=$tmp_out 2> /dev/null; then
   new_ls=`ls -u --full-time $tmp_in`
   if test "x$old_ls" != "x$new_ls"; then
     echo "dd iflag=noatime updated atime; O_NOATIME bug in your kernel?" >&2
@@ -34,6 +51,12 @@ if dd iflag=noatime if=$tmp_in of=$tmp_o
   fi
 fi
 
-rm -f $tmp_in $tmp_out
+if dd oflag=nolinks if=$tmp_in of=$tmp_out 2> /dev/null; then
+  dd iflag=nolinks if=$tmp_in > /dev/null 2>&1 && fail=1
+  dd iflag=nolinks < $tmp_in > /dev/null 2>&1 && fail=1
+  dd oflag=nolinks < $tmp_in > $tmp_out 2>&1 || fail=1
+fi
+
+rm -f $tmp_in $tmp_in2 $tmp_sym $tmp_out
 
 exit $fail




reply via email to

[Prev in Thread] Current Thread [Next in Thread]