diff options
Diffstat (limited to 'src/basic/stat-util.c')
-rw-r--r-- | src/basic/stat-util.c | 40 |
1 files changed, 36 insertions, 4 deletions
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 4e8651f428..3bef0dfe44 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -213,15 +213,47 @@ int fd_is_network_fs(int fd) { } int fd_is_network_ns(int fd) { + struct statfs s; int r; - r = fd_is_fs_type(fd, NSFS_MAGIC); - if (r <= 0) - return r; + /* Checks whether the specified file descriptor refers to a network namespace. On old kernels there's no nice + * way to detect that, hence on those we'll return a recognizable error (EUCLEAN), so that callers can handle + * this somewhat nicely. + * + * This function returns > 0 if the fd definitely refers to a network namespace, 0 if it definitely does not + * refer to a network namespace, -EUCLEAN if we can't determine, and other negative error codes on error. */ + + if (fstatfs(fd, &s) < 0) + return -errno; + + if (!is_fs_type(&s, NSFS_MAGIC)) { + /* On really old kernels, there was no "nsfs", and network namespace sockets belonged to procfs + * instead. Handle that in a somewhat smart way. */ + + if (is_fs_type(&s, PROC_SUPER_MAGIC)) { + struct statfs t; + + /* OK, so it is procfs. Let's see if our own network namespace is procfs, too. If so, then the + * passed fd might refer to a network namespace, but we can't know for sure. In that case, + * return a recognizable error. */ + + if (statfs("/proc/self/ns/net", &t) < 0) + return -errno; + + if (s.f_type == t.f_type) + return -EUCLEAN; /* It's possible, we simply don't know */ + } + + return 0; /* No! */ + } r = ioctl(fd, NS_GET_NSTYPE); - if (r < 0) + if (r < 0) { + if (errno == ENOTTY) /* Old kernels didn't know this ioctl, let's also return a recognizable error in that case */ + return -EUCLEAN; + return -errno; + } return r == CLONE_NEWNET; } |