Difference between dup(0) and open("/dev/fd/0",...);

I believe APUE (2nd ed.; Sec. 3.16) is not correct.

APUE says fd = open("/dev/fd/0", mode); is equivalent to fd = dup (0);, and mode is completely ignored. It seems this is the case in Solaris, but wrong in Linux. (I don’t have access to other Unices at this moment.)

A test program:
01 #include <unistd.h>
02 #include <fcntl.h>
04 int main ()
05 {
06     close (0);
07     printf ("%d\n", open ("a.txt", O_RDONLY)); // Should be 0
08     //int f2 = open ("/dev/fd/0", O_WRONLY);
09     int f2 = dup(0);
10     printf ("%d\n", f2);
11     write (f2, "Hello world\n", 12);
12     return 0;
13 }

Let’s run the program with an empty a.txt. Certainly the write function in Line 11 is going to fail.

Now, let’s comment out Line 9 and uncomment line 8 and try it again.

First I ran it in Solaris, the write call still failed. The behavior is like what APUE tells us.

Try it again in Linux - It was successful!

It seems that in Linux, /dev/fd/0 is considered by open as nothing but a normal symlink to a.txt. So it returns a completely new descriptor instead of a duplicate of the old.

Let’s try it again with a shell script:
rm -f a.txt
touch a.txt
exec 0<a.txt
exec 3>/dev/fd/0
echo 'Hello world' >&3
cat a.txt

Run it in Linux (with DASH or BASH): Both outputed ‘Hello world’.

Run it in Solaris (with Bourne shell and BASH): Both failed, outputting nothing (Bourne shell) or failing with ‘Bad file number’ (BASH).


(1) Solaris handles /dev/fd/.. specially, as APUE tells us;

(2) Linux simply consider /dev/fd/0 a symlink to the actual file.

(I’ll try later how Linux handles open("/dev/fd/0",mode) if the descriptor is an anonymous pipe or socket or something else that a normal symlink is unable to link to.

Kernels used in the above tests:

Linux: Linux desktop 2.6.28-gentoo #4 SMP Mon Jan 12 17:39:23 CST 2009 x86_64 Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz GenuineIntel GNU/Linux

Solaris: SunOS caesar 5.8 Generic_117350-51 sun4u sparc SUNW,Ultra-80 Solaris

1 comment: