psmisc/0001-Use-mountinfo-to-be-able-to-use-the-mount-identity.patch

1194 lines
33 KiB
Diff

From bfa17afa43fc1b98c2bbc9c54416db9ccb505028 Mon Sep 17 00:00:00 2001
From: Werner Fink <werner@suse.de>
Date: Fri, 7 May 2021 16:25:13 +0200
Subject: [PATCH] Use mountinfo to be able to use the mount identity
which allows to distinguish different mounts with the
same device number as it happens with NFS shares.
Smaller cleanup as support of chroot environments
and older systems.
Add support for name_to_handle_at() system call to
get the real mount ID for each file
Support also BtrFS with its various subvolumes
On BtrFS stat(2) on binary does not see subvol dev
Allow not unique mounts as well as not unique mountpoint
Fuser does not show open kvm storage image files
such as qcow2 files. Patch from Ali Abdallah <ali.abdallah@suse.com>
Correct last change by using our own namespace to
determine the appropriate mount ID for e.g. qcow2 files.
Signed-off-by: Werner Fink <werner@suse.de>
---
configure.ac | 15 +
src/fuser.c | 681 +++++++++++++++++++++++++++++++++++++++++++++-------------
src/fuser.h | 27 +-
src/timeout.c | 5
4 files changed, 567 insertions(+), 161 deletions(-)
--- configure.ac
+++ configure.ac 2021-05-07 15:01:31.910757814 +0000
@@ -44,6 +44,19 @@ if test "$enable_timeout_stat" = "static
fi
AM_CONDITIONAL([WANT_TIMEOUT_STAT], [test "$enable_timeout_stat" = "static"])
+# Use /proc/self/mountinfo if available
+if test -e /proc/self/mountinfo ; then
+ AC_DEFINE([HAS_MOUNTINFO], [1], [System has /proc/self/mountinfo which can used instead /proc(/self)/mounts])
+fi
+# Use /proc/self/fdinfo if available
+if test -e /proc/self/fdinfo ; then
+ AC_DEFINE([HAS_FDINFO], [1], [System has /proc/self/fdinfo for informations on file descriptors])
+fi
+
+# Check for Linux specific name_to_handle_at(2) system call for getting mount IDs
+AC_CHECK_FUNC([name_to_handle_at],[
+ AC_DEFINE([HAS_NAME_TO_HANDLE_AT], [1], [System has name_to_handle_at(2) system call])])
+
# Use string search for network based file systems but only if the system
# has /proc/self/mountinfo
AC_SUBST([WITH_MOUNTINFO_LIST])
@@ -85,7 +98,7 @@ dnl Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS([arpa/inet.h fcntl.h langinfo.h libintl.h limits.h locale.h mntent.h netdb.h netinet/in.h stdlib.h string.h sys/ioctl.h sys/socket.h termios.h unistd.h])
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h langinfo.h libintl.h limits.h locale.h mntent.h netdb.h netinet/in.h stdlib.h string.h sys/ioctl.h sys/mount.h sys/param.h sys/stat.h sys/socket.h sys/types.h termios.h unistd.h])
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
--- src/fuser.c
+++ src/fuser.c 2021-05-07 15:03:42.232365037 +0000
@@ -32,6 +32,8 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <fcntl.h>
+#include <sys/mount.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -79,7 +81,7 @@ static void check_map(const pid_t pid, c
struct device_list *dev_head,
struct inode_list *ino_head, const uid_t uid,
const char access);
-static struct stat *get_pidstat(const pid_t pid, const char *filename);
+static struct stat *get_pidstat(const pid_t pid, const char *filename, int *id);
static uid_t getpiduid(const pid_t pid);
static int print_matches(struct names *names_head, const opt_type opts,
const int sig_number);
@@ -88,9 +90,9 @@ static int kill_matched_proc(struct proc
/*int parse_mount(struct names *this_name, struct device_list **dev_list);*/
static void add_device(struct device_list **dev_list,
- struct names *this_name, dev_t device);
-void fill_unix_cache(struct unixsocket_list **unixsocket_head);
-void clear_unix_cache(struct unixsocket_list **unixsocket_head);
+ struct names *this_name, dev_t device, dev_t subvol);
+static void fill_unix_cache(struct unixsocket_list **unixsocket_head);
+static void clear_unix_cache(struct unixsocket_list **unixsocket_head);
static void atexit_clear_unix_cache();
static dev_t find_net_dev(void);
static void scan_procs(struct names *names_head, struct inode_list *ino_head,
@@ -109,9 +111,15 @@ static void debug_match_lists(struct nam
struct device_list *dev_head);
#endif
-#if defined(WITH_MOUNTINFO_LIST)
+static list_t mntinfo = { &mntinfo, &mntinfo };
static void clear_mntinfo(void) __attribute__ ((__destructor__));
static void init_mntinfo(void) __attribute__ ((__constructor__));
+static int get_fdinfo(const pid_t pid, const char *fd, struct fdinfo *info);
+#if defined(HAS_NAME_TO_HANDLE_AT)
+static int get_mountid(const char *path);
+#endif
+static int find_mountpoint(const char *path, mntinfo_t **mountinfo);
+#if defined(WITH_MOUNTINFO_LIST)
static int mntstat(const char *path, struct stat *buf);
#endif
static stat_t thestat = stat;
@@ -202,6 +210,7 @@ scan_procs(struct names *names_head, str
struct stat *cwd_stat = NULL;
struct stat *exe_stat = NULL;
struct stat *root_stat = NULL;
+ int cwd_id, exe_id, root_id;
if (topproc_dent->d_name[0] < '0' || topproc_dent->d_name[0] > '9') /* Not a process */
continue;
@@ -211,9 +220,9 @@ scan_procs(struct names *names_head, str
continue;
uid = getpiduid(pid);
- cwd_stat = get_pidstat(pid, "cwd");
- exe_stat = get_pidstat(pid, "exe");
- root_stat = get_pidstat(pid, "root");
+ cwd_stat = get_pidstat(pid, "cwd", &cwd_id);
+ exe_stat = get_pidstat(pid, "exe", &exe_id);
+ root_stat = get_pidstat(pid, "root", &root_id);
cwd_dev = cwd_stat ? cwd_stat->st_dev : 0;
exe_dev = exe_stat ? exe_stat->st_dev : 0;
root_dev = root_stat ? root_stat->st_dev : 0;
@@ -221,39 +230,51 @@ scan_procs(struct names *names_head, str
/* Scan the devices */
for (dev_tmp = dev_head; dev_tmp != NULL;
dev_tmp = dev_tmp->next) {
- if (exe_dev == dev_tmp->device)
- add_matched_proc(dev_tmp->name, pid, uid,
- ACCESS_EXE);
- if (root_dev == dev_tmp->device)
- add_matched_proc(dev_tmp->name, pid, uid,
- ACCESS_ROOT);
- if (cwd_dev == dev_tmp->device)
- add_matched_proc(dev_tmp->name, pid, uid,
- ACCESS_CWD);
+ struct subvol *vol_tmp;
+ char access = 0;
+ if (exe_dev == dev_tmp->device && dev_tmp->mnt_id == exe_id)
+ access |= ACCESS_EXE;
+ if (root_dev == dev_tmp->device && dev_tmp->mnt_id == root_id)
+ access |= ACCESS_ROOT;
+ if (cwd_dev == dev_tmp->device && dev_tmp->mnt_id == cwd_id)
+ access |= ACCESS_CWD;
+ for (vol_tmp = dev_tmp->vol; vol_tmp != NULL;
+ vol_tmp = vol_tmp->next) {
+ /* For BtrFS sub volumes there are different
+ mount ids for the same device */
+ if (exe_dev == vol_tmp->device && vol_tmp->mnt_id == exe_id)
+ access |= ACCESS_EXE;
+ if (root_dev == vol_tmp->device && vol_tmp->mnt_id == root_id)
+ access |= ACCESS_ROOT;
+ if (cwd_dev == vol_tmp->device && vol_tmp->mnt_id == cwd_id)
+ access |= ACCESS_CWD;
+ }
+ if (access)
+ add_matched_proc(dev_tmp->name, pid, uid, access);
}
for (ino_tmp = ino_head; ino_tmp != NULL;
ino_tmp = ino_tmp->next) {
- if (exe_dev == ino_tmp->device) {
+ if (exe_dev == ino_tmp->device && ino_tmp->mnt_id == exe_id) {
if (!exe_stat)
- exe_stat = get_pidstat(pid, "exe");
+ exe_stat = get_pidstat(pid, "exe", NULL);
if (exe_stat
&& exe_stat->st_dev == ino_tmp->device
&& exe_stat->st_ino == ino_tmp->inode)
add_matched_proc(ino_tmp->name, pid,
uid, ACCESS_EXE);
}
- if (root_dev == ino_tmp->device) {
+ if (root_dev == ino_tmp->device && ino_tmp->mnt_id == root_id) {
if (!root_stat)
- root_stat = get_pidstat(pid, "root");
+ root_stat = get_pidstat(pid, "root", NULL);
if (root_stat
&& root_stat->st_dev == ino_tmp->device
&& root_stat->st_ino == ino_tmp->inode)
add_matched_proc(ino_tmp->name, pid,
uid, ACCESS_ROOT);
}
- if (cwd_dev == ino_tmp->device) {
+ if (cwd_dev == ino_tmp->device && ino_tmp->mnt_id == cwd_id) {
if (!cwd_stat)
- cwd_stat = get_pidstat(pid, "cwd");
+ cwd_stat = get_pidstat(pid, "cwd", NULL);
if (cwd_stat
&& cwd_stat->st_dev == ino_tmp->device
&& cwd_stat->st_ino == ino_tmp->inode)
@@ -294,18 +315,42 @@ add_inode(struct inode_list **ino_list,
ino_tmp->name = this_name;
ino_tmp->device = device;
ino_tmp->inode = inode;
+ ino_tmp->mnt_id = this_name->mnt_id;
ino_tmp->next = ino_head;
*ino_list = ino_tmp;
}
+
+static void
+add_subvol(struct subvol **head, dev_t device, int mnt_id)
+{
+ struct subvol *vol_tmp, *vol_head;
+
+ if ((vol_tmp =
+ (struct subvol *)malloc(sizeof(struct subvol))) == NULL)
+ return;
+ vol_head = *head;
+ vol_tmp->device = device;
+ vol_tmp->mnt_id = mnt_id;
+ vol_tmp->next = vol_head;
+ *head = vol_tmp;
+}
static void
-add_device(struct device_list **dev_list, struct names *this_name, dev_t device)
+add_device(struct device_list **dev_list, struct names *this_name, dev_t device, dev_t subvol)
{
struct device_list *dev_tmp, *dev_head;
#ifdef DEBUG
fprintf(stderr, "add_device(%s %u\n", this_name->filename,
(unsigned int)device);
#endif /* DEBUG */
+ /* For BtrFS there are many sub volumes all on the same block device */
+ for (dev_tmp = *dev_list; dev_tmp != NULL; dev_tmp = dev_tmp->next)
+ if (dev_tmp->device == device) {
+ if (dev_tmp->device != subvol)
+ add_subvol(&dev_tmp->vol, subvol, this_name->mnt_id);
+ *dev_list = dev_tmp;
+ return;
+ }
if ((dev_tmp =
(struct device_list *)malloc(sizeof(struct device_list))) == NULL)
@@ -313,6 +358,10 @@ add_device(struct device_list **dev_list
dev_head = *dev_list;
dev_tmp->name = this_name;
dev_tmp->device = device;
+ dev_tmp->mnt_id = this_name->mnt_id;
+ dev_tmp->vol = NULL;
+ if (dev_tmp->device != subvol)
+ add_subvol(&dev_tmp->vol, subvol, this_name->mnt_id);
dev_tmp->next = dev_head;
*dev_list = dev_tmp;
}
@@ -454,13 +503,15 @@ add_special_proc(struct names *name_list
int parse_file(struct names *this_name, struct inode_list **ino_list,
const opt_type opts)
{
+ mntinfo_t *mountinfo;
char *new = expandpath(this_name->filename);
if (new) {
if (this_name->filename)
free(this_name->filename);
this_name->filename = strdup(new);
}
- if (timeout(thestat, this_name->filename, &(this_name->st), 5) != 0) {
+ if (timeout(thestat, this_name->filename, &(this_name->st), 5) != 0 ||
+ find_mountpoint(this_name->filename, &mountinfo) != 0) {
if (errno == ENOENT)
fprintf(stderr,
_("Specified filename %s does not exist.\n"),
@@ -470,10 +521,12 @@ int parse_file(struct names *this_name,
this_name->filename, strerror(errno));
return -1;
}
+ this_name->mnt_id = mountinfo->id;
#ifdef DEBUG
- printf("adding file %s %lX %lX\n", this_name->filename,
+ printf("adding file %s %lX %lX %d nfs=%s\n", this_name->filename,
(unsigned long)this_name->st.st_dev,
- (unsigned long)this_name->st.st_ino);
+ (unsigned long)this_name->st.st_ino,
+ mountinfo->id, mountinfo->isremote ? "yes" : "no");
#endif /* DEBUG */
add_inode(ino_list, this_name, this_name->st.st_dev,
this_name->st.st_ino);
@@ -505,12 +558,43 @@ parse_mounts(struct names *this_name, st
const opt_type opts)
{
dev_t match_device;
+ list_t *ptr;
+ int count;
if (S_ISBLK(this_name->st.st_mode))
match_device = this_name->st.st_rdev;
else
match_device = this_name->st.st_dev;
- add_device(dev_list, this_name, match_device);
+
+ count = 0;
+ list_for_each(ptr, &mntinfo) {
+ mntinfo_t *mnt = list_entry(ptr, mntinfo_t);
+ if (match_device != mnt->dev)
+ continue;
+ if (S_ISBLK(this_name->st.st_mode)) {
+ /* Correct mount IDs check if a block device
+ * was specified */
+ this_name->mnt_id = mnt->id;
+ add_device(dev_list, this_name, match_device, mnt->dev);
+ if (mnt->dev == mnt->vol)
+ count++;
+ else count = 1;
+ continue;
+ }
+ if (this_name->mnt_id != mnt->id)
+ continue;
+ count++;
+ }
+ if (count == 0) {
+ errno = ENOENT;
+ fprintf(stderr,
+ _("Specified filename %s has no mountpoint.\n"),
+ this_name->filename);
+ return -1;
+ }
+ if (S_ISBLK(this_name->st.st_mode))
+ return 0;
+ add_device(dev_list, this_name, match_device, match_device);
return 0;
}
@@ -687,10 +771,12 @@ int parse_inet(struct names *this_name,
break;
#endif
}
- } /*while */
- freeaddrinfo(res);
+ } /* for (;;) */
+
+ freeaddrinfo(res);
return 0;
- }
+
+ } /* if getaddrinfo */
}
return 1;
}
@@ -957,6 +1043,21 @@ free_inodes(struct inode_list **match_in
/*
* Free up structures allocated in add_device
*/
+
+static void
+free_subvol(struct subvol **volumes)
+{
+ struct subvol *vol_tmp, *vol_next;
+
+ vol_tmp = *volumes;
+ while (vol_tmp != NULL) {
+ vol_next = vol_tmp->next;
+ free(vol_tmp);
+ vol_tmp = vol_next;
+ }
+ *volumes =NULL;
+}
+
static void
free_devices(struct device_list **match_devices)
{
@@ -964,6 +1065,8 @@ free_devices(struct device_list **match_
device_tmp = *match_devices;
while(device_tmp != NULL) {
+ if (device_tmp->vol)
+ free_subvol(&device_tmp->vol);
device_next = device_tmp->next;
free(device_tmp);
device_tmp = device_next;
@@ -1166,16 +1269,11 @@ int main(int argc, char *argv[])
skip_argv = 1;
//while(option != '\0') option++;
if (strcmp(argv[argc_cnt], "tcp") == 0)
- default_namespace =
- NAMESPACE_TCP;
- else if (strcmp(argv[argc_cnt], "udp")
- == 0)
- default_namespace =
- NAMESPACE_UDP;
- else if (strcmp(argv[argc_cnt], "file")
- == 0)
- default_namespace =
- NAMESPACE_FILE;
+ default_namespace = NAMESPACE_TCP;
+ else if (strcmp(argv[argc_cnt], "udp") == 0)
+ default_namespace = NAMESPACE_UDP;
+ else if (strcmp(argv[argc_cnt], "file") == 0)
+ default_namespace = NAMESPACE_FILE;
else
usage(_
("Invalid namespace name"));
@@ -1215,7 +1313,7 @@ int main(int argc, char *argv[])
}
#if defined(WITH_MOUNTINFO_LIST)
- if ((opts & OPT_ALWAYSSTAT) == 0)
+ if ((opts & (OPT_MOUNTS|OPT_ALWAYSSTAT)) == OPT_MOUNTS)
thestat = mntstat;
#endif
/* an option */
@@ -1529,7 +1627,7 @@ print_matches(struct names *names_head,
}
-static struct stat *get_pidstat(const pid_t pid, const char *filename)
+static struct stat *get_pidstat(const pid_t pid, const char *filename, int *id)
{
char pathname[256];
struct stat *st;
@@ -1541,6 +1639,15 @@ static struct stat *get_pidstat(const pi
free(st);
return NULL;
}
+
+ if (id) {
+ mntinfo_t *info;
+ char *new = expandpath(pathname);
+ if (new && find_mountpoint(new, &info) == 0)
+ *id = info->id;
+ else *id = -1;
+ }
+
return st;
}
@@ -1555,7 +1662,7 @@ check_dir(const pid_t pid, const char *d
struct inode_list *ino_tmp;
struct device_list *dev_tmp;
struct unixsocket_list *sock_tmp;
- struct stat st, lst;
+ struct stat st;
char *dirpath;
char filepath[PATH_MAX];
@@ -1580,6 +1687,9 @@ check_dir(const pid_t pid, const char *d
filepath, strerror(errno));
}
} else {
+ struct fdinfo fd;
+ int fdret;
+
thedev = st.st_dev;
if (thedev == netdev) {
for (sock_tmp = sockets; sock_tmp != NULL;
@@ -1592,13 +1702,37 @@ check_dir(const pid_t pid, const char *d
}
}
}
+
+ memset(&fd, 0, sizeof(struct fdinfo));
+ fdret = get_fdinfo(pid, direntry->d_name, &fd);
+
for (dev_tmp = dev_head; dev_tmp != NULL;
dev_tmp = dev_tmp->next) {
- if (thedev != dev_tmp->device)
+
+ if (thedev != dev_tmp->device) {
+ struct subvol *vol_tmp;
+ int subvol_found = 0;
+
+ for (vol_tmp = dev_tmp->vol; vol_tmp != NULL;
+ vol_tmp = vol_tmp->next) {
+ /* Check for BtrFS sub volumes as well */
+ if (thedev == vol_tmp->device) {
+ subvol_found++;
+ break;
+ }
+ }
+
+ if (!subvol_found)
+ continue;
+ }
+
+ if (fdret != 0)
+ continue;
+ if (fd.mnt_id != dev_tmp->mnt_id)
continue;
+
if (access == ACCESS_FILE
- && (lstat(filepath, &lst) == 0)
- && (lst.st_mode & S_IWUSR)) {
+ && (fd.flags & (O_WRONLY|O_RDWR))) {
add_matched_proc(dev_tmp->name,
pid, uid,
ACCESS_FILEWR |
@@ -1620,9 +1754,10 @@ check_dir(const pid_t pid, const char *d
continue;
}
if (st.st_ino == ino_tmp->inode) {
+ if (fdret != 0)
+ continue;
if (access == ACCESS_FILE
- && (lstat(filepath, &lst) == 0)
- && (lst.st_mode & S_IWUSR)) {
+ && (fd.flags & (O_WRONLY|O_RDWR))) {
add_matched_proc(ino_tmp->name,
pid, uid,
ACCESS_FILEWR |
@@ -1651,31 +1786,54 @@ check_map(const pid_t pid, const char *f
FILE *fp;
unsigned long long tmp_inode;
unsigned int tmp_maj, tmp_min;
- dev_t tmp_device;
if (asprintf(&pathname, "/proc/%d/%s", pid, filename) < 0)
return;
if ((fp = fopen(pathname, "r")) == NULL) {
- free(pathname);
+ free(pathname);
return;
- }
- free(pathname);
+ }
+ free(pathname);
while (fgets(line, BUFSIZ, fp)) {
- if (sscanf(line, "%*s %*s %*s %x:%x %lld",
- &tmp_maj, &tmp_min, &tmp_inode) == 3) {
- tmp_device = tmp_maj * 256 + tmp_min;
+ char *scanned_path = NULL;
+ if (sscanf(line, "%*s %*s %*s %x:%x %lld %ms",
+ &tmp_maj, &tmp_min, &tmp_inode, &scanned_path) == 4) {
+ dev_t tmp_device = makedev(tmp_maj, tmp_min);
+ int mnt_id = -1;
+
+ if (scanned_path) {
+ if (*scanned_path == '/') {
+ mntinfo_t *mountinfo;
+ if (find_mountpoint(scanned_path, &mountinfo) == 0)
+ mnt_id = mountinfo->id;
+ }
+ free(scanned_path);
+ }
+
for (dev_tmp = dev_head; dev_tmp != NULL;
- dev_tmp = dev_tmp->next)
- if (dev_tmp->device == tmp_device)
+ dev_tmp = dev_tmp->next) {
+ struct subvol *vol_tmp;
+ if (dev_tmp->device == tmp_device &&
+ mnt_id == dev_tmp->mnt_id)
add_matched_proc(dev_tmp->name, pid,
uid, access);
+ for (vol_tmp = dev_tmp->vol; vol_tmp != NULL;
+ vol_tmp = vol_tmp->next) {
+ /* For BtrFS sub volumes there are different
+ mount ids for the same device */
+ if (vol_tmp->device == tmp_device && mnt_id == vol_tmp->mnt_id)
+ add_matched_proc(dev_tmp->name, pid,
+ uid, access);
+ }
+ }
for (ino_tmp = ino_head; ino_tmp != NULL;
ino_tmp = ino_tmp->next)
if (ino_tmp->device == tmp_device
&& ino_tmp->inode == tmp_inode)
add_matched_proc(ino_tmp->name, pid,
uid, access);
- }
+ } else if (scanned_path)
+ free(scanned_path);
}
fclose(fp);
}
@@ -1699,6 +1857,7 @@ static uid_t getpiduid(const pid_t pid)
* fill_unix_cache : Create a list of Unix sockets
* This list is used later for matching purposes
*/
+static
void fill_unix_cache(struct unixsocket_list **unixsocket_head)
{
FILE *fp;
@@ -1715,6 +1874,8 @@ void fill_unix_cache(struct unixsocket_l
while (fgets(line, BUFSIZ, fp) != NULL) {
char *path;
char *scanned_path = NULL;
+ int mnt_id = -1;
+ mntinfo_t *mountinfo;
if (sscanf(line, "%*x: %*x %*x %*x %*x %*d %llu %ms",
&scanned_inode, &scanned_path) != 2) {
if (scanned_path)
@@ -1730,6 +1891,8 @@ void fill_unix_cache(struct unixsocket_l
free(path);
continue;
}
+ if (find_mountpoint(scanned_path, &mountinfo) == 0)
+ mnt_id = mountinfo->id;
if ((newsocket = (struct unixsocket_list *)
malloc(sizeof(struct unixsocket_list))) == NULL) {
free(path);
@@ -1738,6 +1901,7 @@ void fill_unix_cache(struct unixsocket_l
newsocket->sun_name = strdup(scanned_path);
newsocket->inode = st.st_ino;
newsocket->dev = st.st_dev;
+ newsocket->mnt_id = mnt_id;
newsocket->net_inode = scanned_inode;
newsocket->next = *unixsocket_head;
*unixsocket_head = newsocket;
@@ -1750,6 +1914,7 @@ void fill_unix_cache(struct unixsocket_l
/*
* Free up the list of Unix sockets
*/
+static
void clear_unix_cache(struct unixsocket_list **unixsocket_head)
{
while(*unixsocket_head != NULL) {
@@ -1921,34 +2086,21 @@ scan_mounts(struct names *names_head, st
{
struct device_list *dev_tmp;
struct inode_list *ino_tmp;
- FILE *fp;
- char line[BUFSIZ];
- char *find_mountp;
- char *find_space;
struct stat st;
+ list_t *ptr;
- if ( (ino_head == NULL) && (dev_head == NULL) )
- return;
-
+ list_for_each(ptr, &mntinfo) {
+ mntinfo_t *mnt = list_entry(ptr, mntinfo_t);
+ const char *find_mountp = mnt->mpoint;
- if ((fp = fopen(PROC_MOUNTS, "r")) == NULL) {
- fprintf(stderr, "Cannot open %s\n", PROC_MOUNTS);
- return;
- }
- while (fgets(line, BUFSIZ, fp) != NULL) {
- if ((find_mountp = strchr(line, ' ')) == NULL)
- continue;
- find_mountp++;
- if ((find_space = strchr(find_mountp, ' ')) == NULL)
- continue;
- *find_space = '\0';
- if (timeout(thestat, find_mountp, &st, 5) != 0) {
+ if (timeout(thestat, find_mountp, &st, 5) != 0)
continue;
- }
+
/* Scan the devices */
for (dev_tmp = dev_head; dev_tmp != NULL;
dev_tmp = dev_tmp->next) {
- if (st.st_dev == dev_tmp->device)
+ if (st.st_dev == dev_tmp->device &&
+ mnt->id == dev_tmp->mnt_id)
add_special_proc(dev_tmp->name, PTYPE_MOUNT, 0,
find_mountp);
}
@@ -1960,7 +2112,6 @@ scan_mounts(struct names *names_head, st
find_mountp);
}
}
- fclose(fp);
}
static void
@@ -2013,16 +2164,44 @@ scan_swaps(struct names *names_head, str
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.
+ * stat(2) system call for remote file system.
*/
-static list_t mntinfo = { &mntinfo, &mntinfo };
+static mntinfo_t*
+add_mntinfo(const char *mpoint, const char *type, int mid, int parid, dev_t dev)
+{
+ 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 = dev;
+ mnt->id = mid;
+ if (strncmp("nfs", type, 3) == 0 || strncmp("afs", type, 3) == 0 || strncmp("autofs", type, 6))
+ mnt->isremote = 1;
+ else mnt->isremote = 0;
+ /* E.g. sub volumes of BtrFS do not show main device number in
+ /proc/self/mountinfo, for now just map the device to this */
+ mnt->vol = dev;
+ return mnt;
+}
-static void clear_mntinfo(void)
+static void
+clear_mntinfo(void)
{
list_t *ptr, *tmp;
@@ -2033,72 +2212,276 @@ static void clear_mntinfo(void)
}
}
-static void init_mntinfo(void)
+static void
+init_mntinfo(void)
{
+ char type[256];
char mpoint[PATH_MAX *4 + 1]; // octal escaping takes 4 chars per 1 char
- int mid, parid, max = 0;
+ char devname[PATH_MAX];
+ int mid, parid;
+ mntinfo_t *mntinf;
+#if defined(HAS_MOUNTINFO)
uint maj, min;
- list_t sort;
+#endif
FILE *mnt;
if (!list_empty(&mntinfo))
return;
- if ((mnt = fopen("/proc/self/mountinfo", "r")) == (FILE *) 0)
+#if defined(HAS_MOUNTINFO)
+ /*
+ * We could also loop over all mount namespaces, that is not only
+ * over /proc/self/ns/mnt. But this expands this list as well as
+ * we can use name_to_handle_at(2) in our get_mountid() function.
+ */
+ if ((mnt = fopen(PROC_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);
+ (mnt, "%i %i %u:%u %*s %s %*[^-] - %s %s %*[^\n]",
+ &mid, &parid, &maj, &min, &mpoint[0], &type[0], &devname[0]) == 7) {
+ struct stat st;
+ mntinf = add_mntinfo(mpoint, type, mid, parid, makedev(maj, min));
+ if (mntinf && strncmp(devname, "/dev/", 5) == 0 && stat(devname, &st) == 0) {
+ if (st.st_rdev != 0 && mntinf->dev != st.st_rdev) {
+ mntinf->vol = st.st_rdev;
+ stat(mpoint, &st);
+ mntinf->dev = st.st_dev; /* stat(2) on binary does not see subvol dev */
+ }
}
- 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;
}
+#else
+ if ((mnt = fopen(PROC_MOUNTS, "r")) == (FILE *) 0)
+ return;
+ mid = 1;
+ parid = -1;
+ while (fscanf (mnt, "%s %s %s %*[^\n]", &devname[0], &mpoint[0], &type[0]) == 3) {
+ struct stat st;
+ if (stat(mpoint, &st) != 0) {
+ if (errno != EACCES) {
+ fprintf(stderr, _("Cannot stat %s: %s\n"),
+ mpoint, strerror(errno));
+ exit(1);
+ }
+ st.st_dev = (dev_t)-1;
+ }
+ mntinf = add_mntinfo(mpoint, type, mid++, parid, st.st_dev);
+ if (mntinf && strncmp(devname, "/dev/", 5) == 0 && stat(devname, &st) == 0) {
+ if (st.st_rdev != 0 && mntinf->dev != st.st_rdev)
+ mntinf->vol = st.st_rdev;
+ }
+ }
+#endif
fclose(mnt);
+}
+
+static int
+get_fdinfo(const pid_t pid, const char *fd, struct fdinfo *info)
+{
+ int ret = 0;
+ char pathname[256];
+ int mnt_id = 0, flags = 0;
+#if defined(HAS_FDINFO) /* Here we get the private namespace as well */
+ const static char delimiters[] = ": \t\n";
+ char line[BUFSIZ];
+ FILE *fp;
+# if defined(HAS_NAME_TO_HANDLE_AT)
+ static ino_t mynamespace;
+ struct stat st;
+
+ if (!mynamespace) {
+ if (stat("/proc/self/ns/mnt", &st) != 0) {
+ fprintf(stderr, _("Cannot stat %s: %s\n"),
+ "/proc/self/ns/mnt", strerror(errno));
+ exit(1);
+ }
+ mynamespace = st.st_ino;
+ }
+# endif
+ snprintf(pathname, 256, "/proc/%d/fdinfo/%s", pid, fd);
+ if ((fp = fopen(pathname, "r")) == NULL)
+ goto out; /* forbidden namesspace, try our own namespace */
+ while (fgets(line, BUFSIZ, fp) && ret < 2) {
+ char *xp, *vp, *ep;
+ unsigned long ul;
+ xp = strtok(&line[0], delimiters);
+ if (!xp || *xp == 0)
+ continue;
+ vp = strtok(NULL, delimiters);
+ if (!vp || *vp == 0)
+ continue;
+ if (strcmp(xp, "flags") == 0 && (ul = strtoul(vp, &ep, 0)) != ULONG_MAX && ep && *ep == 0) {
+ info->flags = (mode_t)ul;
+ flags++;
+ ret++;
+ }
+ if (strcmp(xp, "mnt_id") == 0 && (ul = strtoul(vp, &ep, 0)) != ULONG_MAX && ep && *ep == 0) {
+ info->mnt_id = (int)ul;
+ mnt_id++;
+ ret++;
+ }
+ }
+ fclose(fp);
+out:
+# if defined(HAS_NAME_TO_HANDLE_AT)
+ snprintf(pathname, 256, "/proc/%d/ns/mnt", pid);
+ if (stat(pathname, &st) == 0 && mynamespace != st.st_ino) {
+ char *realname;
+ snprintf(pathname, 256, "/proc/%d/fd/%s", pid, fd);
+ realname = expandpath(pathname);
+ if (realname) { /* Use our namespace for mount ID <sigh> */
+ info->mnt_id = get_mountid(realname);
+ mnt_id++;
+ }
+ }
+# endif
+#endif
+ if (!flags || !mnt_id) {
+ struct stat lst;
+
+ snprintf(pathname, 256, "/proc/%d/fd/%s", pid, fd);
+ if (!flags && lstat(pathname, &lst) == 0) {
+ if (lst.st_mode & S_IWUSR)
+ info->flags |= O_WRONLY;
+ ret++;
+ }
+
+ if (!mnt_id) {
+ char *realname = expandpath(pathname);
+ if (realname) {
+ mntinfo_t *mountinfo;
+ if (find_mountpoint(realname, &mountinfo) == 0) {
+ info->mnt_id = mountinfo->id;
+ ret++;
+ }
+ }
+ }
+ }
+ return ret == 2 ? 0 : -1;
+}
- /* 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) {
+#if defined(HAS_NAME_TO_HANDLE_AT)
+static int
+get_mountid(const char *path)
+{
+ union fh_u {
+ struct file_handle handle;
+ char buffer[sizeof(struct file_handle) + MAX_HANDLE_SZ];
+ } fh = { .handle.handle_bytes = MAX_HANDLE_SZ };
+ int mnt_id = -1;
+
+ errno = 0;
+ if (name_to_handle_at(0, path, &fh.handle, &mnt_id, 0) == -1)
+ mnt_id = -1;
+ return mnt_id;
+}
+#endif
+
+static int
+find_mountpoint(const char *path, mntinfo_t **mountinfo)
+{
+ char *use, *end;
+ ssize_t nlen;
+#if defined(HAS_NAME_TO_HANDLE_AT)
+ int mnt_id = get_mountid(path);
+#endif
+ int ret = -1;
+
+ *mountinfo = NULL;
+
+#if defined(HAS_NAME_TO_HANDLE_AT)
+ if (mnt_id >= 0) {
+ list_t *ptr;
+
+ errno = ENOENT;
+ list_for_each(ptr, &mntinfo) {
mntinfo_t *mnt = list_entry(ptr, mntinfo_t);
- if (mid != mnt->id)
+
+ if (mnt_id != mnt->id)
continue;
- move_head(ptr, &sort);
+
+ ret = 0;
+ errno = 0;
+ *mountinfo = mnt;
break;
}
- list_for_each_safe(ptr, tmp, &mntinfo) {
+ if (*mountinfo)
+ goto out;
+
+ if (strlen(path) == 1 && path[0] == '/') {
+ struct stat st;
+
+ /* could be a chroot or a container */
+
+ if (stat(path, &st) != 0) {
+ if (errno != EACCES) {
+ fprintf(stderr, _("Cannot stat %s: %s\n"),
+ path, strerror(errno));
+ exit(1);
+ }
+ st.st_dev = (dev_t)-1;
+ }
+ ret = 0;
+ errno = 0;
+ *mountinfo = add_mntinfo(path, "unkown", mnt_id, -1, st.st_dev);
+ goto out;
+ }
+ }
+#endif
+ use = strdup(path);
+ if (!use)
+ goto out;
+
+ nlen = strlen(use);
+ end = use+nlen;
+ errno = ENOENT;
+ do {
+ list_t *ptr;
+ if (*end == '/') {
+ if (end == use) { /* root file system */
+ end++;
+ if (nlen == 1) {
+ struct stat st;
+
+ /* could be a chroot or a container */
+
+ if (stat(use, &st) != 0) {
+ if (errno != EACCES) {
+ fprintf(stderr, _("Cannot stat %s: %s\n"),
+ use, strerror(errno));
+ exit(1);
+ }
+ st.st_dev = (dev_t)-1;
+ }
+ ret = 0;
+ errno = 0;
+ *mountinfo = add_mntinfo(use, "unkown", 0, -1, st.st_dev);
+ break;
+ }
+ }
+ }
+ *end = '\0';
+ nlen = end-use;
+ list_for_each(ptr, &mntinfo) {
mntinfo_t *mnt = list_entry(ptr, mntinfo_t);
- if (mid != mnt->parid)
+
+ if (nlen != mnt->nlen)
+ continue;
+
+ if (strcmp(use, mnt->mpoint))
continue;
- move_head(ptr, &sort);
+
+ ret = 0;
+ errno = 0;
+ *mountinfo = mnt;
+ break;
}
- }
- if (!list_empty(&mntinfo)) {
-#ifdef EBADE
- errno = EBADE;
-#else
- errno = ENOENT;
-#endif /* EBADE */
- }
- join(&sort, &mntinfo);
+
+ } while (!*mountinfo && (end = strrchr(use, '/')));
+ free(use);
+out:
+ return ret;
}
+#if defined(WITH_MOUNTINFO_LIST)
/*
* Determine device of links below /proc/
*/
@@ -2106,8 +2489,7 @@ static int mntstat(const char *path, str
{
char name[PATH_MAX + 1];
const char *use;
- ssize_t nlen;
- list_t *ptr;
+ mntinfo_t *mnt;
if ((use = realpath(path, name)) == NULL || *use != '/')
{
@@ -2119,27 +2501,26 @@ static int mntstat(const char *path, str
errno = 0;
return stat(path, buf);
}
+ if (strncmp("/dev/", use, 5) == 0) {
+ /*
+ * 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;
- }
+ if (find_mountpoint(use, &mnt) < 0) {
+ errno = ENOENT;
+ return -1;
}
- errno = ENOENT;
- return -1;
+
+ if (mnt->isremote) {
+ buf->st_dev = mnt->dev;
+ buf->st_ino = mnt->id; /* inode substitute */
+ return 0; /* found on NFS */
+ }
+
+ return stat(path, buf);
}
#endif /* WITH_MOUNTINFO_LIST */
--- src/fuser.h
+++ src/fuser.h 2021-05-07 15:01:31.954757006 +0000
@@ -37,10 +37,16 @@ struct procs {
#define PTYPE_KNFSD 2
#define PTYPE_SWAP 3
+struct fdinfo {
+ mode_t flags;
+ int mnt_id;
+};
+
struct names {
char *filename;
unsigned char name_space;
- struct stat st;
+ struct stat st;
+ int mnt_id;
struct procs *matched_procs;
struct names *next;
};
@@ -65,12 +71,21 @@ struct inode_list {
struct names *name;
dev_t device;
ino_t inode;
+ int mnt_id;
struct inode_list *next;
};
+struct subvol {
+ dev_t device;
+ int mnt_id;
+ struct subvol *next;
+};
+
struct device_list {
struct names *name;
+ struct subvol *vol;
dev_t device;
+ int mnt_id;
struct device_list *next;
};
@@ -79,6 +94,7 @@ struct unixsocket_list {
ino_t inode;
ino_t net_inode;
dev_t dev;
+ int mnt_id;
struct unixsocket_list *next;
};
@@ -87,18 +103,16 @@ struct mount_list {
struct mount_list *next;
};
-#if defined (__GNUC__) && defined(WITH_MOUNTINFO_LIST)
-# include "lists.h"
+#include "lists.h"
typedef struct mntinfo_s {
list_t this;
int id, parid;
+ char isremote;
dev_t dev;
size_t nlen;
+ dev_t vol;
char *mpoint;
} mntinfo_t;
-#else
-# undef WITH_MOUNTINFO_LIST
-#endif
#define NAMESPACE_FILE 0
#define NAMESPACE_TCP 1
@@ -109,5 +123,6 @@ typedef struct mntinfo_s {
#endif /* PATH_MAX */
#define KNFSD_EXPORTS "/proc/fs/nfs/exports"
+#define PROC_MOUNTINFO "/proc/self/mountinfo"
#define PROC_MOUNTS "/proc/mounts"
#define PROC_SWAPS "/proc/swaps"
--- src/timeout.c
+++ src/timeout.c 2021-05-07 15:01:31.954757006 +0000
@@ -67,9 +67,6 @@
# ifndef constructor
# define constructor __constructor__
# endif
-# ifndef packed
-# define packed __packed__
-# endif
# ifndef inline
# define inline __inline__
# endif
@@ -107,7 +104,7 @@ typedef struct _handle {
stat_t function;
size_t len;
char path[0];
-} attribute((packed)) handle_t;
+} handle_t;
/*
* Using a forked process for doing e.g. stat(2) system call as this