* buildsys: Fix DEJAGNU work-around Debian #1015089
* killall: Use kill if pidfd_send_signal fails Debian #1015228
* fuser: Do not mention nonexistent - reset option #42
+ * fuser: Use modern statn where possible
* pstree: Better AppArmor support !30
Changes in 23.5
src/fuser.h \
src/lists.h
-if WANT_TIMEOUT_STAT
-src_fuser_SOURCES += src/timeout.c src/timeout.h
+if HAVE_SYSCALL_STATX
+src_fuser_SOURCES += src/statx.c src/statx.h
endif
src_fuser_LDADD = @LIBINTL@
src_killall_SOURCES = src/killall.c src/comm.h src/signals.c src/signals.h src/i18n.h
fi
AM_CONDITIONAL([WANT_TIMEOUT_STAT], [test "$enable_timeout_stat" = "static"])
-# Use string search for network based file systems but only if the system
-# has /proc/self/mountinfo
-AC_SUBST([WITH_MOUNTINFO_LIST])
-AC_ARG_ENABLE([mountinfo_list],
- [AS_HELP_STRING([--enable-mountinfo-list], [Use the list in /proc/self/mountinfo to replace stat(2) syscall on network file systems shares])],
- [enable_mountinfo_list="yes"],
- [enable_mountinfo_list="no"])
-if test "$enable_mountinfo_list" = "yes" -a -e /proc/self/mountinfo ; then
- AC_DEFINE([WITH_MOUNTINFO_LIST], [1], [Use list in /proc/self/mountinfo to replace stat calls])
-fi
-
+AC_CHECK_HEADERS([sys/syscall.h])
+AC_CHECK_DECLS([SYS_statx],
+ [has_syscall_statx="yes"],
+ [has_syscall_statx="no"],
+ [[#include <sys/syscall.h>]]
+)
+AC_CHECK_FUNCS([statx])
+# Check for linux specific statx(2) system call
+AC_SUBST([HAS_SYSCALL_STATX])
+AC_ARG_ENABLE([disable_statx],
+ [AS_HELP_STRING([--disable-statx], [Do not use linux specific statx(2) system call as replacement for stat(2), lstat(2), and fstat(2)])],
+ [enable_syscall_statx="no"],
+ [enable_syscall_statx=$has_syscall_statx])
+AM_CONDITIONAL([HAVE_SYSCALL_STATX], [test "$enable_syscall_statx" = "yes"])
+
# Enable hardened compile and link flags
AC_ARG_ENABLE([harden_flags],
[AS_HELP_STRING([--disable-harden-flags], [disable hardened compilier and linker flags])],
#include "fuser.h"
#include "signals.h"
#include "i18n.h"
-#include "timeout.h"
+#include "statx.h"
#include "comm.h"
//#define DEBUG 1
struct device_list *dev_head);
#endif
-#if defined(WITH_MOUNTINFO_LIST)
-static void clear_mntinfo(void) __attribute__ ((__destructor__));
-static void init_mntinfo(void) __attribute__ ((__constructor__));
-static int mntstat(const char *path, struct stat *buf);
-#endif
-static stat_t thestat = stat;
static char *expandpath(const char *path);
static struct unixsocket_list *unixsockets = NULL;
static struct names *names_head = NULL, *names_tail = NULL;
free(this_name->filename);
this_name->filename = strdup(new);
}
- if (timeout(thestat, this_name->filename, &(this_name->st), 5) != 0)
+ if (statn(this_name->filename, STATX_INO|STATX_TYPE, &(this_name->st)) != 0 )
{
if (errno == ENOENT)
fprintf(stderr,
opts |= OPT_INTERACTIVE;
break;
case 'I':
-#if defined(WITH_MOUNTINFO_LIST)
opts |= OPT_ALWAYSSTAT;
-#endif
break;
case 'k':
opts |= OPT_KILL;
continue;
}
-#if defined(WITH_MOUNTINFO_LIST)
- if ((opts & OPT_ALWAYSSTAT) == 0)
- thestat = mntstat;
+#if defined(HAVE_DECL_SYS_STATX) && HAVE_DECL_SYS_STATX == 1
+ if ((opts & OPT_ALWAYSSTAT))
+ stat_flags = 0; /* Triggers sync with e.g. remote NFS server even on autofs */
#endif
/* an option */
/* Not an option, must be a file specification */
if ((st = (struct stat *)malloc(sizeof(struct stat))) == NULL)
return NULL;
snprintf(pathname, PATH_MAX-1, "/proc/%d/%s", pid, filename);
- if (timeout(thestat, pathname, st, 5) != 0)
+ if (statn(pathname, STATX_UID|STATX_INO|STATX_TYPE, st) != 0)
{
free(st);
return NULL;
snprintf(filepath, sizeof filepath - 1, "/proc/%d/%s/%s",
pid, dirname, direntry->d_name);
- if (timeout(thestat, filepath, &st, 5) != 0)
+ if (statn(filepath, STATX_INO, &st) != 0)
{
if (errno != ENOENT && errno != ENOTDIR)
{
{
if (thedev != ino_tmp->device)
continue;
- if (!st.st_ino && timeout(thestat, filepath, &st, 5) != 0)
+ if (!st.st_ino && statn(filepath, STATX_INO, &st) != 0)
{
fprintf(stderr, _("Cannot stat file %s: %s\n"),
filepath, strerror(errno));
if (asprintf(&pathname, "/proc/%d", pid) < 0)
return 0;
- if (timeout(thestat, pathname, &st, 5) != 0)
+ if (statn(pathname, STATX_UID, &st) != 0)
{
free(pathname);
return 0;
path = scanned_path;
if (*scanned_path == '@')
scanned_path++;
- if (timeout(thestat, scanned_path, &st, 5) < 0)
+ if (statn(scanned_path, STATX_INO, &st) < 0)
{
free(path);
continue;
fprintf(stderr, _("Cannot open a network socket.\n"));
return -1;
}
- if (fstat(skt, &st) != 0)
+ if (fstatn(skt, STATX_INO, &st) != 0)
{
fprintf(stderr, _("Cannot find socket's device number.\n"));
close(skt);
continue;
*find_space = '\0';
- if (timeout(thestat, line, &st, 5) != 0)
+ if (statn(line, STATX_INO, &st) != 0)
continue;
/* Scan the devices */
if ((find_space = strchr(find_mountp, ' ')) == NULL)
continue;
*find_space = '\0';
-
- if (timeout(thestat, find_mountp, &st, 5) != 0)
+ if (statn(find_mountp, STATX_INO, &st) != 0)
continue;
/* Scan the devices */
for (dev_tmp = dev_head; dev_tmp != NULL;
if (*find_space == '\0')
continue;
}
- if (timeout(thestat, line, &st, 5) != 0)
+ if (statn(line, STATX_INO, &st) != 0)
continue;
/* Scan the devices */
fclose(fp);
}
-#if defined(WITH_MOUNTINFO_LIST)
-/*
- * Use /proc/self/mountinfo of modern linux system to determine
- * the device numbers of the mount points. Use this to avoid the
- * stat(2) system call wherever possible.
- */
-
-static list_t mntinfo = { &mntinfo, &mntinfo };
-
-static void clear_mntinfo(void)
-{
- list_t *ptr, *tmp;
-
- list_for_each_safe(ptr, tmp, &mntinfo)
- {
- mntinfo_t *mnt = list_entry(ptr, mntinfo_t);
- delete(ptr);
- free(mnt);
- }
-}
-
-static void init_mntinfo(void)
-{
- char mpoint[PATH_MAX *4 + 1]; // octal escaping takes 4 chars per 1 char
- int mid, parid, max = 0;
- uint maj, min;
- list_t sort;
- FILE *mnt;
-
- if (!list_empty(&mntinfo))
- return;
- if ((mnt = fopen("/proc/self/mountinfo", "r")) == (FILE *) 0)
- return;
- while (fscanf
- (mnt, "%i %i %u:%u %*s %s %*[^\n]",
- &mid, &parid, &maj, &min, &mpoint[0]) == 5)
- {
- const size_t nlen = strlen(mpoint);
- mntinfo_t *restrict mnt;
- if (posix_memalign ((void *)&mnt, sizeof(void *),
- alignof(mntinfo_t) + (nlen + 1)) != 0)
- {
- fprintf(stderr,
- _("Cannot allocate memory for matched proc: %s\n"),
- strerror(errno));
- exit(1);
- }
- append(mnt, mntinfo);
- mnt->mpoint = ((char *)mnt) + alignof(mntinfo_t);
- strcpy(mnt->mpoint, mpoint);
- mnt->nlen = nlen;
- mnt->parid = parid;
- mnt->dev = makedev(maj, min);
- mnt->id = mid;
- if (mid > max)
- max = mid;
- }
- fclose(mnt);
-
- /* Sort mount points accordingly to the reverse mount order */
- initial(&sort);
- for (mid = 1; mid <= max; mid++)
- {
- list_t *ptr, *tmp;
- list_for_each_safe(ptr, tmp, &mntinfo)
- {
- mntinfo_t *mnt = list_entry(ptr, mntinfo_t);
- if (mid != mnt->id)
- continue;
- move_head(ptr, &sort);
- break;
- }
- list_for_each_safe(ptr, tmp, &mntinfo)
- {
- mntinfo_t *mnt = list_entry(ptr, mntinfo_t);
- if (mid != mnt->parid)
- continue;
- move_head(ptr, &sort);
- }
- }
- if (!list_empty(&mntinfo))
- {
-#ifdef EBADE
- errno = EBADE;
-#else
- errno = ENOENT;
-#endif /* EBADE */
- }
- join(&sort, &mntinfo);
-}
-
-/*
- * Determine device of links below /proc/
- */
-static int mntstat(
- const char *path,
- struct stat *buf)
-{
- char name[PATH_MAX + 1];
- const char *use;
- ssize_t nlen;
- list_t *ptr;
-
- if ((use = realpath(path, name)) == NULL || *use != '/')
- {
- if (errno == ENOENT)
- return -1;
- /*
- * Could be a special file (socket, pipe, inotify)
- */
- errno = 0;
- return stat(path, buf);
- }
-
- nlen = strlen(use);
- list_for_each(ptr, &mntinfo)
- {
- mntinfo_t *mnt = list_entry(ptr, mntinfo_t);
- if (nlen < mnt->nlen)
- continue;
- if (mnt->nlen == 1) /* root fs is the last entry */
- {
- buf->st_dev = mnt->dev;
- buf->st_ino = 0;
- return 0;
- }
- if (use[mnt->nlen] != '\0' && use[mnt->nlen] != '/')
- continue;
- if (strncmp(use, mnt->mpoint, mnt->nlen) == 0)
- {
- buf->st_dev = mnt->dev;
- buf->st_ino = 0;
- return 0;
- }
- }
- errno = ENOENT;
- return -1;
-}
-#endif /* WITH_MOUNTINFO_LIST */
-
/*
* Somehow the realpath(3) glibc function call, nevertheless
* it avoids lstat(2) system calls.
--- /dev/null
+/*
+ * statx.c - Map modern statx(2) system call to older stat(2), lstat(2),
+ * and fstat(2) replacements named {,l,f}statn()
+ *
+ * Copyright (C) 2018 Werner Fink
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifndef HAVE_STATX
+# define _ASM_GENERIC_FCNTL_H /* Avoid collisions between asm/fcntl.h and bits/fcntl.h ! */
+# include <linux/fcntl.h> /* Definition of AT_* and AT_STATX_* constants ! */
+#endif
+#include <fcntl.h> /* Definition of AT_* constants */
+#include <sys/stat.h>
+#ifndef HAVE_STATX
+# ifndef STATX_TYPE
+# include <linux/stat.h> /* Provides 'struct statx' and STATX_* ! */
+# endif
+#endif
+#include <sys/sysmacros.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+int stat_flags = AT_NO_AUTOMOUNT|AT_STATX_DONT_SYNC;
+
+int statn(const char *pathname, unsigned int mask, struct stat *st)
+{
+ int flags = stat_flags;
+ int dirfd = pathname && *pathname == '/' ? 0 : AT_FDCWD;
+ int ret;
+ struct statx stx;
+
+#ifndef HAVE_STATX
+ ret = syscall(SYS_statx, dirfd, pathname, flags, mask, &stx);
+#else
+ ret = statx(dirfd, pathname, flags, mask, &stx);
+#endif
+ if (ret >= 0) {
+ st->st_dev = makedev(stx.stx_dev_major, stx.stx_dev_minor);
+ st->st_rdev = makedev(stx.stx_rdev_major, stx.stx_rdev_minor);
+
+ st->st_ino = stx.stx_ino;
+ st->st_mode = stx.stx_mode;
+ st->st_nlink = stx.stx_nlink;
+ st->st_uid = stx.stx_uid;
+ st->st_gid = stx.stx_gid;
+ st->st_size = stx.stx_size;
+ st->st_blksize = stx.stx_blksize;
+ st->st_blocks = stx.stx_blocks;
+
+ st->st_atim.tv_sec = stx.stx_atime.tv_sec;
+ st->st_atim.tv_nsec = stx.stx_atime.tv_nsec;
+ st->st_mtim.tv_sec = stx.stx_mtime.tv_sec;
+ st->st_mtim.tv_nsec = stx.stx_mtime.tv_nsec;
+ st->st_ctim.tv_sec = stx.stx_ctime.tv_sec;
+ st->st_ctim.tv_nsec = stx.stx_ctime.tv_nsec;
+ }
+ return ret;
+}
+
+int fstatn(int fd, unsigned int mask, struct stat *st)
+{
+ int flags = AT_EMPTY_PATH|stat_flags;
+ int ret;
+ struct statx stx;
+
+#ifndef HAVE_STATX
+ ret = syscall(SYS_statx, fd, "", flags, mask, &stx);
+#else
+ ret = statx(fd, "", flags, mask, &stx);
+#endif
+ if (ret >= 0) {
+ st->st_dev = makedev(stx.stx_dev_major, stx.stx_dev_minor);
+ st->st_rdev = makedev(stx.stx_rdev_major, stx.stx_rdev_minor);
+
+ st->st_ino = stx.stx_ino;
+ st->st_mode = stx.stx_mode;
+ st->st_nlink = stx.stx_nlink;
+ st->st_uid = stx.stx_uid;
+ st->st_gid = stx.stx_gid;
+ st->st_size = stx.stx_size;
+ st->st_blksize = stx.stx_blksize;
+ st->st_blocks = stx.stx_blocks;
+
+ st->st_atim.tv_sec = stx.stx_atime.tv_sec;
+ st->st_atim.tv_nsec = stx.stx_atime.tv_nsec;
+ st->st_mtim.tv_sec = stx.stx_mtime.tv_sec;
+ st->st_mtim.tv_nsec = stx.stx_mtime.tv_nsec;
+ st->st_ctim.tv_sec = stx.stx_ctime.tv_sec;
+ st->st_ctim.tv_nsec = stx.stx_ctime.tv_nsec;
+ }
+ return ret;
+}
+
+int lstatn(const char *pathname, unsigned int mask, struct stat *st)
+{
+ int flags = AT_SYMLINK_NOFOLLOW|stat_flags;
+ int dirfd = pathname && *pathname == '/' ? 0 : AT_FDCWD;
+ int ret;
+ struct statx stx;
+
+#ifndef HAVE_STATX
+ ret = syscall(SYS_statx, dirfd, pathname, flags, mask, &stx);
+#else
+ ret = statx(dirfd, pathname, flags, mask, &stx);
+#endif
+ if (ret >= 0) {
+ st->st_dev = makedev(stx.stx_dev_major, stx.stx_dev_minor);
+ st->st_rdev = makedev(stx.stx_rdev_major, stx.stx_rdev_minor);
+
+ st->st_ino = stx.stx_ino;
+ st->st_mode = stx.stx_mode;
+ st->st_nlink = stx.stx_nlink;
+ st->st_uid = stx.stx_uid;
+ st->st_gid = stx.stx_gid;
+ st->st_size = stx.stx_size;
+ st->st_blksize = stx.stx_blksize;
+ st->st_blocks = stx.stx_blocks;
+
+ st->st_atim.tv_sec = stx.stx_atime.tv_sec;
+ st->st_atim.tv_nsec = stx.stx_atime.tv_nsec;
+ st->st_mtim.tv_sec = stx.stx_mtime.tv_sec;
+ st->st_mtim.tv_nsec = stx.stx_mtime.tv_nsec;
+ st->st_ctim.tv_sec = stx.stx_ctime.tv_sec;
+ st->st_ctim.tv_nsec = stx.stx_ctime.tv_nsec;
+ }
+ return ret;
+}
--- /dev/null
+/*
+ * statx.h - Map modern statx(2) system call to older stat(2), lstat(2),
+ * and fstat(2) replacements named {,l,f}statn()
+ *
+ * Copyright (C) 2018 Werner Fink
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _STATX_H
+#define _STATX_H
+
+extern int stat_flags;
+#if defined(HAVE_DECL_SYS_STATX) && HAVE_DECL_SYS_STATX == 1
+# ifndef HAVE_STATX
+# define _ASM_GENERIC_FCNTL_H /* Avoid collisions between asm/fcntl.h and bits/fcntl.h ! */
+# include <linux/fcntl.h> /* Definition of AT_* and AT_STATX_* constants ! */
+# ifndef STATX_TYPE
+# include <linux/stat.h> /* Provides 'struct statx' and STATX_* ! */
+# endif
+# endif
+extern int statn(const char*, unsigned int, struct stat*);
+extern int fstatn(int, unsigned int, struct stat*);
+extern int lstatn(const char*, unsigned int, struct stat*);
+#else
+extern inline int
+statn(const char *path, unsigned int mask __attribute__((unused)), struct stat *st)
+{
+ return stat(path, st);
+}
+extern inline int
+fstatn(int fd, unsigned int mask __attribute__((unused)), struct stat *st)
+{
+ return fstat(fd, st);
+}
+extern inline int
+lstatn(const char *path, unsigned int mask __attribute__((unused)), struct stat *st)
+{
+ return lstat(path, st);
+}
+#define STATX_TYPE 0
+#define STATX_MODE 0
+#define STATX_NLINK 0
+#define STATX_UID 0
+#define STATX_GID 0
+#define STATX_ATIME 0
+#define STATX_MTIME 0
+#define STATX_CTIME 0
+#define STATX_INO 0
+#define STATX_SIZE 0
+#define STATX_BLOCKS 0
+#define STATX_BASIC_STATS 0
+#define STATX_BTIME 0
+#define STATX_ALL 0
+#endif
+#endif