fix failure of fchmod, fstat, fchdir, and fchown to produce EBADF
authorRich Felker <dalias@aerifal.cx>
Thu, 19 Dec 2013 19:24:55 +0000 (14:24 -0500)
committerRich Felker <dalias@aerifal.cx>
Thu, 19 Dec 2013 19:24:55 +0000 (14:24 -0500)
the workaround/fallback code for supporting O_PATH file descriptors
when the kernel lacks support for performing these operations on them
caused EBADF to get replaced by ENOENT (due to missing entry in
/proc/self/fd). this is unlikely to affect real-world code (calls that
might yield EBADF are generally unsafe, especially in library code)
but it was breaking some test cases.

the fix I've applied is something of a tradeoff: it adds one syscall
to these operations on kernels where the workaround is needed. the
alternative would be to catch ENOENT from the /proc lookup and
translate it to EBADF, but I want to avoid doing that in the interest
of not touching/depending on /proc at all in these functions as long
as the kernel correctly supports the operations. this is following the
general principle of isolating hacks to code paths that are taken on
broken systems, and keeping the code for correct systems completely
hack-free.

src/stat/fchmod.c
src/stat/fstat.c
src/unistd/fchdir.c
src/unistd/fchown.c

index 1b943d440c547dfd81e7269e9d70710bdefc2770..6d2814162ebc296d99d100afcccaf64f74ea080c 100644 (file)
@@ -1,5 +1,6 @@
 #include <sys/stat.h>
 #include <errno.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 void __procfdname(char *, unsigned);
@@ -7,7 +8,8 @@ void __procfdname(char *, unsigned);
 int fchmod(int fd, mode_t mode)
 {
        int ret = __syscall(SYS_fchmod, fd, mode);
-       if (ret != -EBADF || fd < 0) return __syscall_ret(ret);
+       if (ret != -EBADF || __syscall(SYS_fcntl, fd, F_GETFD) < 0)
+               return __syscall_ret(ret);
 
        char buf[15+3*sizeof(int)];
        __procfdname(buf, fd);
index 29b4243db37307c27c65601ef84fa16177ef669f..b5611767b6b8499ba7b03f9e033bb8421bc23ec2 100644 (file)
@@ -1,5 +1,6 @@
 #include <sys/stat.h>
 #include <errno.h>
+#include <fcntl.h>
 #include "syscall.h"
 #include "libc.h"
 
@@ -8,7 +9,8 @@ void __procfdname(char *, unsigned);
 int fstat(int fd, struct stat *st)
 {
        int ret = __syscall(SYS_fstat, fd, st);
-       if (ret != -EBADF || fd < 0) return __syscall_ret(ret);
+       if (ret != -EBADF || __syscall(SYS_fcntl, fd, F_GETFD) < 0)
+               return __syscall_ret(ret);
 
        char buf[15+3*sizeof(int)];
        __procfdname(buf, fd);
index 9fbc815429e43973311b3cab56ab298224ec1b0a..72c3915eee095964e0f161183ccfcff28862a47a 100644 (file)
@@ -1,5 +1,6 @@
 #include <unistd.h>
 #include <errno.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 void __procfdname(char *, unsigned);
@@ -7,7 +8,8 @@ void __procfdname(char *, unsigned);
 int fchdir(int fd)
 {
        int ret = __syscall(SYS_fchdir, fd);
-       if (ret != -EBADF || fd < 0) return __syscall_ret(ret);
+       if (ret != -EBADF || __syscall(SYS_fcntl, fd, F_GETFD) < 0)
+               return __syscall_ret(ret);
 
        char buf[15+3*sizeof(int)];
        __procfdname(buf, fd);
index e1c3198a738cd2cb46f52f13f78892b81b6ab483..36633b0e95f3145a3cacf33958dd3862ced78b68 100644 (file)
@@ -1,5 +1,6 @@
 #include <unistd.h>
 #include <errno.h>
+#include <fcntl.h>
 #include "syscall.h"
 
 void __procfdname(char *, unsigned);
@@ -7,7 +8,8 @@ void __procfdname(char *, unsigned);
 int fchown(int fd, uid_t uid, gid_t gid)
 {
        int ret = __syscall(SYS_fchown, fd, uid, gid);
-       if (ret != -EBADF || fd < 0) return __syscall_ret(ret);
+       if (ret != -EBADF || __syscall(SYS_fcntl, fd, F_GETFD) < 0)
+               return __syscall_ret(ret);
 
        char buf[15+3*sizeof(int)];
        __procfdname(buf, fd);