psmisc/psmisc-22.6-nfs4fuser.patch

769 lines
23 KiB
Diff

--- doc/fuser.1
+++ doc/fuser.1 2008-05-16 15:18:59.000000000 +0200
@@ -80,8 +80,14 @@ List all known signal names.
\fIname\fP specifies a file on a mounted file system or a block device that
is mounted. All processes accessing files on that file system are listed.
If a directory file is specified, it is automatically changed to
-\fIname\fP/. to use any file system that might be mounted on that
-directory.
+\fIname\fP/. To use any file system that might be mounted on that
+directory. Please note that due the required device ID comparision all
+mounted file systems the
+.BR stat (2)
+system call will applied to every file system even on network file system
+(NFS). If the NFS server does not respond or the network is down the
+.BR stat (2)
+may hang forever.
.IP \fB\-n\ \fIspace\fP
Select a different name space. The name spaces \fBfile\fP (file names, the
default), \fBudp\fP (local UDP ports), and \fBtcp\fP (local TCP ports) are
@@ -152,10 +158,13 @@ The \fB\-k\fP option only works on proce
\fBfuser\fP will print an advice, but take no action beyond that.
.SH BUGS
.PP
-fuser \-m /dev/sgX will show (or kill with the \-k flag) all processes, even
+\fBfuser \-m \fI/dev/sgX\fR will show (or kill with the \fB\-k\fR flag) all processes, even
if you don't have that device configured. There may be other devices it
does this for too.
.PP
+\fBfuser \-m \fIname\fR may hang forever if there are NFS file systems mounted
+and one of the NFS servers do not respond or the corresponding network is down.
+.PP
.B fuser
cannot report on any processes that it doesn't have permission to look at
the file descriptor table for. The most common time this problem occurs
--- src/fuser.c
+++ src/fuser.c 2009-03-27 14:06:48.860001275 +0100
@@ -32,9 +32,11 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
+#include <sys/wait.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pwd.h>
@@ -45,6 +47,7 @@
#include <mntent.h>
#include <signal.h>
#include <getopt.h>
+#include <setjmp.h>
#include "fuser.h"
#include "signals.h"
@@ -62,7 +65,7 @@
static void add_matched_proc(struct names *name_list, const pid_t pid, const uid_t uid, const char access);
static void check_dir(const pid_t pid, const char *dirname, struct device_list *dev_head, struct inode_list *ino_head, const uid_t uid, const char access);
static void check_map(const pid_t pid, const char *filename, 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 opt_type opts, const pid_t pid, const char *filename, char *real);
static uid_t getpiduid(const pid_t pid);
static int print_matches(struct names *names_head, const opt_type opts, const int sig_number);
static void kill_matched_proc(struct procs *pptr, const opt_type opts, const int sig_number);
@@ -72,13 +75,19 @@ static void add_device(struct device_lis
void scan_mount_devices(const opt_type opts, struct mountdev_list **mount_devices);
void fill_unix_cache(struct unixsocket_list **unixsocket_head);
static dev_t find_net_dev(void);
-static void scan_procs(struct names *names_head, struct inode_list *ino_head, struct device_list *dev_head);
+static void scan_procs(const opt_type opts, struct names *names_head, struct inode_list *ino_head, struct device_list *dev_head);
#ifdef NFS_CHECKS
static void scan_knfsd(struct names *names_head, struct device_list *dev_head);
#endif /* NFS_CHECKS */
#ifdef DEBUG
static void debug_match_lists(struct names *names_head, struct inode_list *ino_head, struct device_list *dev_head);
#endif
+static struct nfs_points *mnts;
+static void clear_mnt(void);
+static int check4nfs(const char * path, char * real);
+
+typedef int (*stat_t)(const char*, struct stat*);
+static int nfssafe(stat_t func, const char *path, struct stat *buf);
static void usage (const char *errormsg)
{
@@ -127,7 +136,14 @@ void print_version()
"For more information about these matters, see the files named COPYING.\n"));
}
-static void scan_procs(struct names *names_head, struct inode_list *ino_head, struct device_list *dev_head)
+static int islocatedon(const char * path, const char * loc)
+{
+ if (!path || *path == '\0')
+ return 0;
+ return (strstr(path, loc) == path);
+}
+
+static void scan_procs(const opt_type opts, struct names *names_head, struct inode_list *ino_head, struct device_list *dev_head)
{
DIR *topproc_dir;
struct dirent *topproc_dent;
@@ -137,6 +153,9 @@ static void scan_procs(struct names *nam
pid_t pid, my_pid;
uid_t uid;
struct stat *cwd_stat, *exe_stat, *root_stat;
+ char root_real[PATH_MAX+1];
+ char cwd_real[PATH_MAX+1];
+ char exe_real[PATH_MAX+1];
if ( (fd_dirpath = malloc(MAX_PATHNAME)) == NULL)
return;
@@ -157,9 +176,10 @@ static void scan_procs(struct names *nam
continue;
uid = getpiduid(pid);
- root_stat = get_pidstat(pid, "root");
- cwd_stat = get_pidstat(pid, "cwd");
- exe_stat = get_pidstat(pid, "exe");
+ root_real[0] = cwd_real[0] = exe_real[0] = '\0';
+ root_stat = get_pidstat(opts, pid, "root", root_real);
+ cwd_stat = get_pidstat(opts, pid, "cwd", cwd_real);
+ exe_stat = get_pidstat(opts, pid, "exe", exe_real);
/* Scan the devices */
for (dev_tmp = dev_head ; dev_tmp != NULL ; dev_tmp = dev_tmp->next) {
if (exe_stat != NULL && exe_stat->st_dev == dev_tmp->device)
@@ -168,6 +188,14 @@ static void scan_procs(struct names *nam
add_matched_proc(dev_tmp->name, pid, uid, ACCESS_ROOT);
if (cwd_stat != NULL && cwd_stat->st_dev == dev_tmp->device)
add_matched_proc(dev_tmp->name, pid, uid, ACCESS_CWD);
+ if ((dev_tmp->name->name_space & NAMESPACE_NFS) == 0)
+ continue;
+ if (islocatedon(&exe_real[0], dev_tmp->name->filename))
+ add_matched_proc(dev_tmp->name, pid, uid, ACCESS_EXE);
+ if (islocatedon(&root_real[0], dev_tmp->name->filename))
+ add_matched_proc(dev_tmp->name, pid, uid, ACCESS_ROOT);
+ if (islocatedon(&cwd_real[0], dev_tmp->name->filename))
+ add_matched_proc(dev_tmp->name, pid, uid, ACCESS_CWD);
}
for (ino_tmp = ino_head ; ino_tmp != NULL ; ino_tmp = ino_tmp->next) {
if (exe_stat != NULL) {
@@ -186,9 +214,19 @@ static void scan_procs(struct names *nam
add_matched_proc(ino_tmp->name, pid, uid, ACCESS_CWD);
}
}
+ if ((ino_tmp->name->name_space & NAMESPACE_NFS) == 0)
+ continue;
+ if (islocatedon(&exe_real[0], ino_tmp->name->filename))
+ add_matched_proc(ino_tmp->name, pid, uid, ACCESS_EXE);
+ if (islocatedon(&root_real[0], ino_tmp->name->filename))
+ add_matched_proc(ino_tmp->name, pid, uid, ACCESS_ROOT);
+ if (islocatedon(&cwd_real[0], ino_tmp->name->filename))
+ add_matched_proc(ino_tmp->name, pid, uid, ACCESS_CWD);
}
+#ifndef __linux__
check_dir(pid, "lib", dev_head, ino_head, uid, ACCESS_MMAP);
check_dir(pid, "mmap", dev_head, ino_head, uid, ACCESS_MMAP);
+#endif
check_dir(pid, "fd", dev_head, ino_head, uid, ACCESS_FILE);
check_map(pid, "maps", dev_head, ino_head, uid, ACCESS_MMAP);
@@ -325,10 +363,26 @@ int parse_mount(struct names *this_name,
return 0;
}
-int parse_file(struct names *this_name, struct inode_list **ino_list)
+int parse_file(struct names *this_name, struct inode_list **ino_list, const opt_type opts)
{
+ char real[PATH_MAX+1] = "";
struct stat st;
+ real[0] = '\0';
+ if (check4nfs(this_name->filename, real)) {
+ if ((opts & (OPT_MOUNTPOINT|OPT_MOUNTS)) == 0) {
+ free(this_name->filename);
+ this_name->filename = strdup(real);
+ this_name->name_space |= NAMESPACE_NFS;
+ add_inode(ino_list, this_name, (dev_t)-1, (ino_t)-1);
+ return 0;
+ }
+ }
+ if (real[0] != '\0') {
+ free(this_name->filename);
+ this_name->filename = strdup(real);
+ }
+
if (stat(this_name->filename, &st) != 0) {
fprintf(stderr,_("Cannot stat %s: %s\n"), this_name->filename,
strerror(errno));
@@ -342,34 +396,44 @@ int parse_file(struct names *this_name,
return 0;
}
-int parse_unixsockets(struct names *this_name, struct inode_list **ino_list, struct unixsocket_list *sun_head)
+int parse_unixsockets(struct names *this_name, struct inode_list **ino_list, struct unixsocket_list *sun_head, dev_t net_dev, const opt_type opts)
{
struct unixsocket_list *sun_tmp;
struct stat st;
- dev_t net_dev;
-
+
+ if (check4nfs(this_name->filename, NULL)) {
+ this_name->name_space |= NAMESPACE_NFS;
+ return 0;
+ }
+
if (stat(this_name->filename, &st) != 0) {
fprintf(stderr,_("Cannot stat %s: %s\n"), this_name->filename,
strerror(errno));
return -1;
}
- net_dev = find_net_dev();
for (sun_tmp = sun_head; sun_tmp != NULL ; sun_tmp = sun_tmp->next)
{
- if (sun_tmp->dev == st.st_dev && sun_tmp->inode == st.st_ino) {
+ if (sun_tmp->dev == st.st_dev && sun_tmp->inode == st.st_ino) {
add_inode(ino_list, this_name, net_dev, sun_tmp->net_inode);
- return 0;
+ return 0;
}
}
return 0;
}
-int parse_mounts(struct names *this_name, struct mountdev_list *mounts, struct device_list **dev_list, const char opts)
+int parse_mounts(struct names *this_name, struct mountdev_list *mounts, struct device_list **dev_list, const opt_type opts)
{
struct stat st;
struct mountdev_list *mountptr;
dev_t match_device;
+ char real[PATH_MAX+1] = "";
+
+ real[0] = '\0';
+ if (check4nfs(this_name->filename, real)) {
+ this_name->name_space |= NAMESPACE_NFS;
+ goto skip;
+ }
if (stat(this_name->filename, &st) != 0) {
fprintf(stderr,_("Cannot stat %s: %s\n"), this_name->filename,
@@ -388,6 +452,16 @@ int parse_mounts(struct names *this_name
}
}
return 0;
+skip:
+ match_device = -1;
+ for (mountptr = mounts ; mountptr != NULL ; mountptr = mountptr->next) {
+ if (strcmp(mountptr->dir, real) == 0) {
+ /*printf("Debug: adding parse_mounts() adding %s\n",
+ this_name->filename);*/
+ add_device(dev_list, this_name, match_device);
+ }
+ }
+ return 0;
}
#ifdef WITH_IPV6
@@ -652,6 +726,150 @@ void find_net6_sockets(struct inode_list
}
#endif
+/*
+ * Remove struct nfs_points and its sahdows from memory
+ */
+static void clear_shadow(struct shadow_list *restrict shadow)
+{
+ struct shadow_list *s, *n, *l;
+
+ n = shadow;
+ l = (struct shadow_list*)0;
+ for (s = shadow; n; s = n) {
+ l = s->prev;
+ n = s->next;
+ if (s == shadow) {
+ if (n) n->prev = (struct shadow_list*)0;
+ shadow = n;
+ } else if (l) {
+ if (n) n->prev = l;
+ l->next = n;
+ }
+ free(s);
+ }
+}
+
+static void clear_mnt(void)
+{
+ struct nfs_points *p, *n, *l;
+
+ n = mnts;
+ l = (struct nfs_points*)0;
+ for (p = mnts; n; p = n) {
+ l = p->prev;
+ n = p->next;
+ if (p == mnts) {
+ if (n) n->prev = (struct nfs_points*)0;
+ mnts = n;
+ } else if (l) {
+ if (n) n->prev = l;
+ l->next = n;
+ }
+ if (p->shadow)
+ clear_shadow(p->shadow);
+ free(p);
+ }
+}
+
+/*
+ * Check if path is ia shadow off a NFS partition.
+ */
+static int shadow(struct shadow_list *restrict this, const char *restrict name, const size_t nlen)
+{
+ struct shadow_list *s;
+
+ if (!this)
+ goto out;
+ for (s = this; s; s = s->next) {
+ if (nlen < s->nlen)
+ continue;
+ if (name[s->nlen] != '\0' && name[s->nlen] != '/')
+ continue;
+ if (strncmp(name, s->name, s->nlen) == 0)
+ return 1;
+ }
+out:
+ return 0;
+}
+
+/*
+ * Check path is located on a NFS partition.
+ */
+static int check4nfs(const char * path, char * real)
+{
+ char buf[PATH_MAX+1];
+ const char *curr;
+ int deep = MAXSYMLINKS;
+
+ if (!mnts) return 0;
+
+ curr = path;
+ do {
+ const char *prev;
+ int len;
+
+ if ((prev = strdupa(curr)) == NULL)
+ return 0;
+
+ errno = 0;
+ if ((len = readlink(curr, buf, PATH_MAX)) < 0)
+ break;
+ buf[len] = '\0'; /* Don't be fooled by readlink(2) */
+
+ if (strncmp(prev, "/proc/", 6) == 0) {
+ curr = &buf[0];
+ break; /* /proc/ provides the real path! */
+ }
+
+ if (len > 10) {
+ char *const ptr = &buf[len - 10];
+ if (strcmp(ptr, " (deleted)") == 0) {
+ *ptr = '\0';
+ curr = &buf[0];
+ break; /* Path is deleted from VFS cache */
+ }
+ }
+
+ if (buf[0] != '/') {
+ const char *slash;
+
+ if ((slash = strrchr(prev, '/'))) {
+ size_t off = slash - prev + 1;
+
+ if (off + len > PATH_MAX)
+ len = PATH_MAX - off;
+
+ memmove(&buf[off], &buf[0], len + 1);
+ memcpy(&buf[0], prev, off);
+ }
+ }
+ curr = &buf[0];
+
+ if (deep-- <= 0) return 0;
+
+ } while (1);
+
+ if (real) strcpy(real, curr);
+
+ if (errno == EINVAL) {
+ const size_t nlen = strlen(curr);
+ struct nfs_points *p;
+ for (p = mnts; p; p = p->next) {
+ if (nlen < p->nlen)
+ continue;
+ if (curr[p->nlen] != '\0' && curr[p->nlen] != '/')
+ continue;
+ if (!strncmp(curr, p->name, p->nlen)) {
+ if (shadow(p->shadow, curr, nlen))
+ continue;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
int main(int argc, char *argv[])
{
opt_type opts;
@@ -676,6 +894,7 @@ int main(int argc, char *argv[])
int optc;
char *option;
char *nsptr;
+ size_t len;
#ifdef WITH_IPV6
ipv4_only = ipv6_only = 0;
@@ -692,7 +911,6 @@ int main(int argc, char *argv[])
#endif
netdev = find_net_dev();
- scan_mount_devices(opts, &mount_devices);
fill_unix_cache(&unixsockets);
/* getopt doesnt like things like -SIGBLAH */
@@ -782,6 +1000,10 @@ int main(int argc, char *argv[])
}
continue;
}
+
+ if (!mount_devices)
+ scan_mount_devices(opts, &mount_devices);
+
/* File specifications */
if ( (this_name = malloc(sizeof(struct names))) == NULL)
continue;
@@ -828,10 +1050,14 @@ int main(int argc, char *argv[])
break;
default: /* FILE */
this_name->filename = strdup(argv[optc]);
- parse_file(this_name, &match_inodes);
- parse_unixsockets(this_name, &match_inodes, unixsockets);
- if (opts & OPT_MOUNTPOINT || opts & OPT_MOUNTS)
+ len = strlen(this_name->filename);
+ if (len > 1 && this_name->filename[len-1] == '/')
+ this_name->filename[len-1] = '\0';
+ parse_file(this_name, &match_inodes, opts);
+ parse_unixsockets(this_name, &match_inodes, unixsockets, netdev, opts);
+ if (opts & (OPT_MOUNTPOINT | OPT_MOUNTS)) {
parse_mounts(this_name, mount_devices, &match_devices, opts);
+ }
break;
}
@@ -857,25 +1083,26 @@ int main(int argc, char *argv[])
if (!ipv4_only) {
#endif
if (tcp_connection_list != NULL)
- find_net_sockets(&match_inodes, tcp_connection_list, "tcp",netdev);
+ find_net_sockets(&match_inodes, tcp_connection_list, "tcp", netdev);
if (udp_connection_list != NULL)
- find_net_sockets(&match_inodes, udp_connection_list, "udp",netdev);
+ find_net_sockets(&match_inodes, udp_connection_list, "udp", netdev);
#ifdef WITH_IPV6
}
if (!ipv6_only) {
if (tcp6_connection_list != NULL)
- find_net6_sockets(&match_inodes, tcp6_connection_list, "tcp",netdev);
+ find_net6_sockets(&match_inodes, tcp6_connection_list, "tcp", netdev);
if (udp6_connection_list != NULL)
- find_net6_sockets(&match_inodes, udp6_connection_list, "udp",netdev);
+ find_net6_sockets(&match_inodes, udp6_connection_list, "udp", netdev);
}
#endif
#ifdef DEBUG
debug_match_lists(names_head, match_inodes, match_devices);
#endif
- scan_procs(names_head, match_inodes, match_devices);
+ scan_procs(opts, names_head, match_inodes, match_devices);
#ifdef NFS_CHECKS
scan_knfsd(names_head, match_devices);
#endif /* NFS_CHECKS */
+ clear_mnt();
return print_matches(names_head,opts, sig_number);
}
@@ -978,7 +1205,7 @@ static int print_matches(struct names *n
}
-static struct stat *get_pidstat(const pid_t pid, const char *filename)
+static struct stat *get_pidstat(const opt_type opts, const pid_t pid, const char *filename, char *real)
{
char pathname[256];
struct stat *st;
@@ -986,6 +1213,10 @@ static struct stat *get_pidstat(const pi
if ( (st = malloc(sizeof(struct stat))) == NULL)
return NULL;
snprintf(pathname, 256, "/proc/%d/%s", pid, filename);
+ if (check4nfs(pathname, real)) {
+ if ((opts & (OPT_MOUNTPOINT|OPT_MOUNTS)) == 0)
+ return NULL;
+ }
if (stat(pathname, st) != 0)
return NULL;
else
@@ -1012,13 +1243,14 @@ static void check_dir(const pid_t pid, c
while ( (direntry = readdir(dirp)) != NULL) {
if (direntry->d_name[0] < '0' || direntry->d_name[0] > '9')
continue;
-
snprintf(filepath, MAX_PATHNAME, "/proc/%d/%s/%s",
pid, dirname, direntry->d_name);
if (stat(filepath, &st) != 0) {
fprintf(stderr, _("Cannot stat file %s: %s\n"),filepath, strerror(errno));
} else {
for (dev_tmp = dev_head ; dev_tmp != NULL ; dev_tmp = dev_tmp->next) {
+ if (dev_tmp->name->name_space & NAMESPACE_NFS)
+ continue;
if (st.st_dev == dev_tmp->device) {
if (access == ACCESS_FILE && (lstat(filepath, &lst)==0) && (lst.st_mode & S_IWUSR)) {
add_matched_proc(dev_tmp->name, pid,uid, ACCESS_FILEWR|access);
@@ -1028,6 +1260,8 @@ static void check_dir(const pid_t pid, c
}
}
for (ino_tmp = ino_head ; ino_tmp != NULL ; ino_tmp = ino_tmp->next) {
+ if (ino_tmp->name->name_space & NAMESPACE_NFS)
+ continue;
if (st.st_dev == ino_tmp->device && st.st_ino == ino_tmp->inode) {
if (access == ACCESS_FILE && (lstat(filepath, &lst)==0) && (lst.st_mode & S_IWUSR)) {
add_matched_proc(ino_tmp->name, pid,uid, ACCESS_FILEWR|access);
@@ -1039,6 +1273,8 @@ static void check_dir(const pid_t pid, c
}
} /* while fd_dent */
closedir(dirp);
+ free(dirpath);
+ free(filepath);
}
static void check_map(const pid_t pid, const char *filename, struct device_list *dev_head, struct inode_list *ino_head, const uid_t uid, const char access)
@@ -1059,12 +1295,18 @@ static void check_map(const pid_t pid, c
if (sscanf(line, "%*s %*s %*s %x:%x %lld",
&tmp_maj, &tmp_min, &tmp_inode) == 3) {
tmp_device = tmp_maj * 256 + tmp_min;
- for(dev_tmp = dev_head ; dev_tmp != NULL ; dev_tmp = dev_tmp->next)
+ for(dev_tmp = dev_head ; dev_tmp != NULL ; dev_tmp = dev_tmp->next) {
+ if (dev_tmp->name->name_space & NAMESPACE_NFS)
+ continue;
if (dev_tmp->device == tmp_device)
add_matched_proc(dev_tmp->name, pid, uid, access);
- for(ino_tmp = ino_head ; ino_tmp != NULL ; ino_tmp = ino_tmp->next)
+ }
+ for(ino_tmp = ino_head ; ino_tmp != NULL ; ino_tmp = ino_tmp->next) {
+ if (ino_tmp->name->name_space & NAMESPACE_NFS)
+ continue;
if (ino_tmp->device == tmp_device && ino_tmp->inode == tmp_inode)
add_matched_proc(ino_tmp->name, pid, uid, access);
+ }
}
}
fclose(fp);
@@ -1135,6 +1377,16 @@ void fill_unix_cache(struct unixsocket_l
}
+static inline int isnetfs(const char * type)
+{
+ static const char* netfs[] = {"nfs", "nfs4", "smbfs", "cifs", "afs", "ncpfs", (char*)0};
+ int n;
+ for (n = 0; netfs[n]; n++)
+ if (!strcasecmp(netfs[n], type))
+ return 1;
+ return 0;
+}
+
/*
* scan_mount_devices : Create a list of mount points and devices
* This list is used later for matching purposes
@@ -1144,17 +1396,94 @@ void scan_mount_devices(const opt_type o
FILE *mntfp;
struct mntent *mnt_ptr;
struct stat st;
-
- if ( (mntfp = setmntent("/etc/mtab","r")) == NULL) {
+ const char * mtab;
+
+ if (stat("/proc/version", &st) < 0)
+ mtab = "/etc/mtab";
+ else
+ mtab = "/proc/mounts";
+
+ if ( (mntfp = setmntent(mtab,"r")) == NULL) {
fprintf(stderr, _("Cannot open /etc/mtab: %s\n"),
strerror(errno));
return;
}
while ( (mnt_ptr = getmntent(mntfp)) != NULL) {
+ if (isnetfs(mnt_ptr->mnt_type)) {
+ /*
+ * Remember all NFS typed partitions, required to make check4nfs() work.
+ */
+ size_t nlen = strlen(mnt_ptr->mnt_dir);
+ struct nfs_points *restrict p;
+ if (posix_memalign((void*)&p, sizeof(void*), alignof(struct nfs_points)+(nlen+1)) != 0)
+ goto out;
+ p->name = ((char*)p)+alignof(struct nfs_points);
+ p->nlen = nlen;
+ p->shadow = (struct shadow_list*)0;
+
+ strcpy(p->name, mnt_ptr->mnt_dir);
+ if (mnts)
+ mnts->prev = p;
+ p->next = mnts;
+ p->prev = (struct nfs_points*)0;
+ mnts = p;
+ if ((opts & (OPT_MOUNTPOINT|OPT_MOUNTS)) == 0) {
+ add_mount_device(mount_devices, mnt_ptr->mnt_fsname, mnt_ptr->mnt_dir, (dev_t)-1);
+ continue;
+ }
+ if (nfssafe(stat, mnt_ptr->mnt_dir, &st) == 0) {
+ add_mount_device(mount_devices, mnt_ptr->mnt_fsname, mnt_ptr->mnt_dir, st.st_dev);
+ } else {
+ fprintf(stderr, _("Cannot stat file %s: %s\n"), mnt_ptr->mnt_dir, strerror(errno));
+ }
+ continue;
+ }
+ if ((opts & (OPT_MOUNTPOINT|OPT_MOUNTS)) == 0)
+ continue;
if (stat(mnt_ptr->mnt_dir, &st) == 0) {
add_mount_device(mount_devices, mnt_ptr->mnt_fsname, mnt_ptr->mnt_dir, st.st_dev);
+ } else {
+ fprintf(stderr, _("Cannot stat file %s: %s\n"), mnt_ptr->mnt_dir, strerror(errno));
+ }
+ }
+ endmntent(mntfp);
+
+ if (!mnts)
+ return;
+
+ if ((mntfp = setmntent("/proc/mounts", "r")) == NULL) {
+ fprintf(stderr, _("Cannot open /etc/mtab: %s\n"), strerror(errno));
+ return;
+ }
+
+ while ((mnt_ptr = getmntent(mntfp)) != NULL) {
+ struct nfs_points *p;
+
+ for (p = mnts; p; p = p->next) {
+ struct shadow_list *s;
+ size_t nlen;
+
+ if (strcmp(mnt_ptr->mnt_dir, p->name) == 0)
+ continue;
+ if (strncmp(mnt_ptr->mnt_dir, p->name, p->nlen) != 0)
+ continue;
+
+ nlen = strlen(mnt_ptr->mnt_dir);
+ if (posix_memalign((void*)&s, sizeof(void*), alignof(struct shadow_list)+(nlen+1)) != 0)
+ goto out;
+ s->name = ((char*)s)+alignof(struct shadow_list);
+ s->nlen = nlen;
+
+ strcpy(s->name, mnt_ptr->mnt_dir);
+ if (p->shadow)
+ p->shadow->prev = s;
+ s->next = p->shadow;
+ s->prev = (struct shadow_list*)0;
+ p->shadow = s;
}
}
+out:
+ endmntent(mntfp);
}
#ifdef DEBUG
@@ -1280,3 +1609,56 @@ static void scan_knfsd(struct names *nam
}
}
#endif /* NFSCHECKS */
+
+static sigjmp_buf jenv;
+static int timeout = 5;
+static void sigalarm(int sig)
+{
+ if (sig == SIGALRM)
+ siglongjmp(jenv, 1);
+}
+static int nfssafe(stat_t func, const char *path, struct stat *buf)
+{
+ pid_t pid = 0;
+ int ret = 0, pipes[4];
+
+ if (pipe(&pipes[0]) < 0)
+ goto err;
+ switch ((pid = fork ())) {
+ case -1:
+ close(pipes[0]);
+ close(pipes[1]);
+ goto err;
+ case 0:
+ (void) signal(SIGALRM, SIG_DFL);
+ close(pipes[0]);
+ if ((ret = func(path, buf)) == 0)
+ write(pipes[1], buf, sizeof(struct stat));
+ close(pipes[1]);
+ exit(ret);
+ default:
+ close(pipes[1]);
+ if (sigsetjmp(jenv, 1)) {
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+ if (waitpid(0, (int*)0, WNOHANG) == 0)
+ kill(pid, SIGKILL);
+ errno = ETIMEDOUT;
+ timeout = 1;
+ goto err;
+ }
+ (void) signal(SIGALRM, sigalarm);
+ (void) alarm(timeout);
+ if (read(pipes[0], buf, sizeof(struct stat)) == 0) {
+ errno = EFAULT;
+ ret = -1;
+ }
+ (void) alarm(0);
+ (void) signal(SIGALRM, SIG_DFL);
+ close(pipes[0]);
+ break;
+ }
+ return ret;
+err:
+ return -1;
+}
--- src/fuser.h
+++ src/fuser.h 2009-03-27 13:23:36.136000974 +0100
@@ -80,9 +80,33 @@ struct unixsocket_list {
struct unixsocket_list *next;
};
+struct shadow_list
+{
+ struct shadow_list *next;
+ struct shadow_list *prev;
+ size_t nlen;
+ char * name;
+};
+
+struct nfs_points {
+ struct nfs_points *next, *prev;
+ struct shadow_list *shadow;
+ size_t nlen;
+ char * name;
+};
+
+#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
+# ifndef restrict
+# define restrict __restrict__
+# endif
+#endif
+#define alignof(type) ((sizeof(type)+(sizeof(void*)-1)) & ~(sizeof(void*)-1))
+
+
#define NAMESPACE_FILE 0
#define NAMESPACE_TCP 1
#define NAMESPACE_UDP 2
+#define NAMESPACE_NFS 4
#define MAX_PATHNAME 200
#define MAX_CMDNAME 16