forked from pool/psmisc
904 lines
26 KiB
Diff
904 lines
26 KiB
Diff
|
--- doc/fuser.1
|
||
|
+++ doc/fuser.1 2010-07-13 11:19:41.000000000 +0000
|
||
|
@@ -88,8 +88,14 @@ List all known signal names.
|
||
|
\fINAME\fR 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\fR/. to use any file system that might be mounted on that
|
||
|
-directory.
|
||
|
+\fINAME\fR/. 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.
|
||
|
.TP
|
||
|
\fB\-M\f, \fB\-\-ismountpoint\fR
|
||
|
Request will be fulfilled only if \fINAME\fR specifies a mountpoint.
|
||
|
@@ -174,10 +180,13 @@ The \fB\-k\fR option only works on proce
|
||
|
\fBfuser\fR 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 2010-07-13 16:28:32.051424747 +0000
|
||
|
@@ -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"
|
||
|
@@ -67,7 +70,8 @@ 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 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);
|
||
|
@@ -79,12 +83,12 @@ static void add_device(struct device_lis
|
||
|
struct names *this_name, dev_t device);
|
||
|
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,
|
||
|
struct unixsocket_list *sockets, dev_t netdev);
|
||
|
static void scan_knfsd(struct names *names_head, struct inode_list *ino_head,
|
||
|
struct device_list *dev_head);
|
||
|
-static void scan_mounts(struct names *names_head,
|
||
|
+static void scan_mounts(struct names *names_head, struct mount_list *mounts,
|
||
|
struct inode_list *ino_head,
|
||
|
struct device_list *dev_head);
|
||
|
static void scan_swaps(struct names *names_head, struct inode_list *ino_head,
|
||
|
@@ -95,6 +99,13 @@ static void debug_match_lists(struct nam
|
||
|
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)
|
||
|
{
|
||
|
if (errormsg != NULL)
|
||
|
@@ -141,8 +152,15 @@ void print_version()
|
||
|
"For more information about these matters, see the files named COPYING.\n"));
|
||
|
}
|
||
|
|
||
|
+static int islocatedon(const char * path, const char * loc)
|
||
|
+{
|
||
|
+ if (!path || *path == '\0')
|
||
|
+ return 0;
|
||
|
+ return (strstr(path, loc) == path);
|
||
|
+}
|
||
|
+
|
||
|
static void
|
||
|
-scan_procs(struct names *names_head, struct inode_list *ino_head,
|
||
|
+scan_procs(const opt_type opts, struct names *names_head, struct inode_list *ino_head,
|
||
|
struct device_list *dev_head, struct unixsocket_list *sockets,
|
||
|
dev_t netdev)
|
||
|
{
|
||
|
@@ -153,6 +171,9 @@ scan_procs(struct names *names_head, str
|
||
|
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 ((topproc_dir = opendir("/proc")) == NULL) {
|
||
|
fprintf(stderr, _("Cannot open /proc directory: %s\n"),
|
||
|
@@ -169,9 +190,10 @@ scan_procs(struct names *names_head, str
|
||
|
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) {
|
||
|
@@ -187,6 +209,17 @@ scan_procs(struct names *names_head, str
|
||
|
&& 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) {
|
||
|
@@ -212,14 +245,27 @@ scan_procs(struct names *names_head, str
|
||
|
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);
|
||
|
}
|
||
|
if (root_stat) free(root_stat);
|
||
|
if (cwd_stat) free(cwd_stat);
|
||
|
if (exe_stat) free(exe_stat);
|
||
|
+#ifndef __linux__
|
||
|
check_dir(pid, "lib", dev_head, ino_head, uid, ACCESS_MMAP,
|
||
|
sockets, netdev);
|
||
|
check_dir(pid, "mmap", dev_head, ino_head, uid, ACCESS_MMAP,
|
||
|
sockets, netdev);
|
||
|
+#endif
|
||
|
check_dir(pid, "fd", dev_head, ino_head, uid, ACCESS_FILE,
|
||
|
sockets, netdev);
|
||
|
check_map(pid, "maps", dev_head, ino_head, uid, ACCESS_MMAP);
|
||
|
@@ -394,6 +440,23 @@ add_special_proc(struct names *name_list
|
||
|
|
||
|
int parse_file(struct names *this_name, struct inode_list **ino_list)
|
||
|
{
|
||
|
+ char real[PATH_MAX+1] = "";
|
||
|
+
|
||
|
+ real[0] = '\0';
|
||
|
+ if (check4nfs(this_name->filename, real)) {
|
||
|
+ if (this_name->filename)
|
||
|
+ 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') {
|
||
|
+ if (this_name->filename)
|
||
|
+ free(this_name->filename);
|
||
|
+ this_name->filename = strdup(real);
|
||
|
+ }
|
||
|
+
|
||
|
if (stat(this_name->filename, &(this_name->st)) != 0) {
|
||
|
if (errno == ENOENT)
|
||
|
fprintf(stderr, _("Specified filename %s does not exist.\n"), this_name->filename);
|
||
|
@@ -411,12 +474,12 @@ int parse_file(struct names *this_name,
|
||
|
|
||
|
int
|
||
|
parse_unixsockets(struct names *this_name, struct inode_list **ino_list,
|
||
|
- struct unixsocket_list *sun_head)
|
||
|
+ struct unixsocket_list *sun_head, dev_t net_dev)
|
||
|
{
|
||
|
struct unixsocket_list *sun_tmp;
|
||
|
- dev_t net_dev;
|
||
|
|
||
|
- net_dev = find_net_dev();
|
||
|
+ if (this_name->name_space & NAMESPACE_NFS)
|
||
|
+ return 0;
|
||
|
|
||
|
for (sun_tmp = sun_head; sun_tmp != NULL; sun_tmp = sun_tmp->next) {
|
||
|
if (sun_tmp->dev == this_name->st.st_dev && sun_tmp->inode == this_name->st.st_ino) {
|
||
|
@@ -429,10 +492,14 @@ parse_unixsockets(struct names *this_nam
|
||
|
}
|
||
|
|
||
|
int
|
||
|
-parse_mounts(struct names *this_name, struct device_list **dev_list,
|
||
|
- const char opts)
|
||
|
+parse_mounts(struct names *this_name, struct mount_list *mounts,
|
||
|
+ struct device_list **dev_list)
|
||
|
{
|
||
|
dev_t match_device;
|
||
|
+ struct mount_list *mountptr;
|
||
|
+
|
||
|
+ if (this_name->name_space & NAMESPACE_NFS)
|
||
|
+ goto skip;
|
||
|
|
||
|
if (S_ISBLK(this_name->st.st_mode))
|
||
|
match_device = this_name->st.st_rdev;
|
||
|
@@ -440,6 +507,13 @@ parse_mounts(struct names *this_name, st
|
||
|
match_device = this_name->st.st_dev;
|
||
|
add_device(dev_list, this_name, match_device);
|
||
|
return 0;
|
||
|
+skip:
|
||
|
+ match_device = -1;
|
||
|
+ for (mountptr = mounts ; mountptr != NULL ; mountptr = mountptr->next) {
|
||
|
+ if (strcmp(mountptr->mountpoint, this_name->filename) == 0)
|
||
|
+ add_device(dev_list, this_name, match_device);
|
||
|
+ }
|
||
|
+ return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef WITH_IPV6
|
||
|
@@ -758,34 +832,101 @@ find_net6_sockets(struct inode_list **in
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
+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;
|
||
|
+}
|
||
|
+
|
||
|
static void
|
||
|
read_proc_mounts(struct mount_list **mnt_list)
|
||
|
{
|
||
|
- FILE *fp;
|
||
|
- char line[BUFSIZ];
|
||
|
- char *find_mountp;
|
||
|
- char *find_space;
|
||
|
- struct mount_list *mnt_tmp;
|
||
|
+ FILE *mntfp;
|
||
|
+ struct mntent *mnt_ptr;
|
||
|
+ struct stat st;
|
||
|
+ const char * mtab;
|
||
|
+
|
||
|
+ if (stat("/proc/version", &st) < 0)
|
||
|
+ mtab = PROC_MOUNTS;
|
||
|
+ else
|
||
|
+ mtab = "/etc/mtab";
|
||
|
|
||
|
- if ((fp = fopen(PROC_MOUNTS, "r")) == NULL) {
|
||
|
- fprintf(stderr, "Cannot open %s\n", PROC_MOUNTS);
|
||
|
+ if ( (mntfp = setmntent(mtab,"r")) == NULL) {
|
||
|
+ fprintf(stderr, _("Cannot open %s: %s\n"), mtab,
|
||
|
+ strerror(errno));
|
||
|
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 ((mnt_tmp = malloc(sizeof(struct mount_list))) == NULL)
|
||
|
+ while ((mnt_ptr = getmntent(mntfp)) != NULL) {
|
||
|
+ struct mount_list *mnt_tmp;
|
||
|
+ 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 ((mnt_tmp = (struct mount_list*)malloc(sizeof(struct mount_list))) == NULL)
|
||
|
continue;
|
||
|
- if ((mnt_tmp->mountpoint = strdup(find_mountp)) == NULL)
|
||
|
+ if ((mnt_tmp->mountpoint = strdup(mnt_ptr->mnt_dir)) == NULL)
|
||
|
continue;
|
||
|
mnt_tmp->next = *mnt_list;
|
||
|
*mnt_list = mnt_tmp;
|
||
|
}
|
||
|
- fclose(fp);
|
||
|
+ endmntent(mntfp);
|
||
|
+
|
||
|
+ if (!mnts)
|
||
|
+ return;
|
||
|
+
|
||
|
+ if ((mntfp = setmntent(mtab, "r")) == NULL) {
|
||
|
+ fprintf(stderr, _("Cannot open %s: %s\n"), mtab,
|
||
|
+ 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);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
@@ -808,6 +949,150 @@ is_mountpoint(struct mount_list **mnt_li
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+/*
|
||
|
+ * 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 a shadow of 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;
|
||
|
@@ -835,6 +1120,7 @@ int main(int argc, char *argv[])
|
||
|
struct option *optr;
|
||
|
char *nsptr;
|
||
|
int skip_argv;
|
||
|
+ size_t len;
|
||
|
|
||
|
struct option options[] = {
|
||
|
{"all", 0, NULL, 'a'},
|
||
|
@@ -931,7 +1217,6 @@ int main(int argc, char *argv[])
|
||
|
break;
|
||
|
case 'M':
|
||
|
opts |= OPT_ISMOUNTPOINT;
|
||
|
- read_proc_mounts(&mounts);
|
||
|
break;
|
||
|
case 'n':
|
||
|
argc_cnt++;
|
||
|
@@ -979,6 +1264,9 @@ int main(int argc, char *argv[])
|
||
|
} /* an option */
|
||
|
/* Not an option, must be a file specification */
|
||
|
|
||
|
+ if (!mounts)
|
||
|
+ read_proc_mounts(&mounts);
|
||
|
+
|
||
|
if ((this_name = malloc(sizeof(struct names))) == NULL)
|
||
|
continue;
|
||
|
this_name->next = NULL;
|
||
|
@@ -1030,11 +1318,24 @@ int main(int argc, char *argv[])
|
||
|
break;
|
||
|
default: /* FILE */
|
||
|
this_name->filename = strdup(current_argv);
|
||
|
+ len = strlen(this_name->filename);
|
||
|
+ if (len > 1 && this_name->filename[len-1] == '/')
|
||
|
+ this_name->filename[len-1] = '\0';
|
||
|
+ if (len > 1 && this_name->filename[0] != '/') {
|
||
|
+ char pwd[MAX_PATHNAME];
|
||
|
+ if (getcwd(pwd, MAX_PATHNAME-1)) {
|
||
|
+ char *new, *old = this_name->filename;
|
||
|
+ if (asprintf(&new, "%s/%s", (strlen(pwd) > 1 ? pwd : ""), old) > 0) {
|
||
|
+ this_name->filename = new;
|
||
|
+ free(old);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
if (parse_file(this_name, &match_inodes) == 0) {
|
||
|
- parse_unixsockets(this_name, &match_inodes, unixsockets);
|
||
|
+ parse_unixsockets(this_name, &match_inodes, unixsockets, netdev);
|
||
|
if (opts & OPT_MOUNTS)
|
||
|
- parse_mounts(this_name, &match_devices, opts);
|
||
|
- }
|
||
|
+ parse_mounts(this_name, mounts, &match_devices);
|
||
|
+ }
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
@@ -1080,11 +1381,14 @@ int main(int argc, char *argv[])
|
||
|
#ifdef DEBUG
|
||
|
debug_match_lists(names_head, match_inodes, match_devices);
|
||
|
#endif
|
||
|
- scan_procs(names_head, match_inodes, match_devices, unixsockets,
|
||
|
- netdev);
|
||
|
- scan_knfsd(names_head, match_inodes, match_devices);
|
||
|
- scan_mounts(names_head, match_inodes, match_devices);
|
||
|
- scan_swaps(names_head, match_inodes, match_devices);
|
||
|
+ scan_procs(opts, names_head, match_inodes, match_devices,
|
||
|
+ unixsockets, netdev);
|
||
|
+ if (opts & OPT_VERBOSE) {
|
||
|
+ scan_knfsd(names_head, match_inodes, match_devices);
|
||
|
+ scan_mounts(names_head, mounts, match_inodes, match_devices);
|
||
|
+ scan_swaps(names_head, match_inodes, match_devices);
|
||
|
+ }
|
||
|
+ clear_mnt();
|
||
|
return print_matches(names_head, opts, sig_number);
|
||
|
}
|
||
|
|
||
|
@@ -1250,7 +1554,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 opt_type opts, const pid_t pid, const char *filename, char *real)
|
||
|
{
|
||
|
char pathname[256];
|
||
|
struct stat *st;
|
||
|
@@ -1258,6 +1562,10 @@ static struct stat *get_pidstat(const pi
|
||
|
if ((st = (struct stat*)malloc(sizeof(struct stat))) == NULL)
|
||
|
return NULL;
|
||
|
snprintf(pathname, 256, "/proc/%d/%s", pid, filename);
|
||
|
+ if (check4nfs(pathname, real)) {
|
||
|
+ if ((opts & OPT_MOUNTS) == 0)
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
if (stat(pathname, st) != 0)
|
||
|
goto out;
|
||
|
return st;
|
||
|
@@ -1309,6 +1617,16 @@ check_dir(const pid_t pid, const char *d
|
||
|
}
|
||
|
for (dev_tmp = dev_head; dev_tmp != NULL;
|
||
|
dev_tmp = dev_tmp->next) {
|
||
|
+ if (dev_tmp->name->name_space & NAMESPACE_NFS) {
|
||
|
+ char buf[PATH_MAX+1];
|
||
|
+ ssize_t len;
|
||
|
+ if ((len = readlink(filepath, buf, PATH_MAX)) < 0)
|
||
|
+ continue;
|
||
|
+ buf[len] = '\0'; /* Don't be fooled by readlink(2) */
|
||
|
+ if (islocatedon(buf, dev_tmp->name->filename))
|
||
|
+ add_matched_proc(dev_tmp->name, pid,uid, access);
|
||
|
+ continue;
|
||
|
+ }
|
||
|
if (st.st_dev == dev_tmp->device) {
|
||
|
if (access == ACCESS_FILE
|
||
|
&& (lstat(filepath, &lst) == 0)
|
||
|
@@ -1326,6 +1644,16 @@ check_dir(const pid_t pid, const char *d
|
||
|
}
|
||
|
for (ino_tmp = ino_head; ino_tmp != NULL;
|
||
|
ino_tmp = ino_tmp->next) {
|
||
|
+ if (ino_tmp->name->name_space & NAMESPACE_NFS) {
|
||
|
+ char buf[PATH_MAX+1];
|
||
|
+ ssize_t len;
|
||
|
+ if ((len = readlink(filepath, buf, PATH_MAX)) < 0)
|
||
|
+ continue;
|
||
|
+ buf[len] = '\0'; /* Don't be fooled by readlink(2) */
|
||
|
+ if (islocatedon(buf, ino_tmp->name->filename))
|
||
|
+ add_matched_proc(ino_tmp->name, pid,uid, access);
|
||
|
+ continue;
|
||
|
+ }
|
||
|
if (st.st_dev == ino_tmp->device
|
||
|
&& st.st_ino == ino_tmp->inode) {
|
||
|
if (access == ACCESS_FILE
|
||
|
@@ -1372,18 +1700,41 @@ check_map(const pid_t pid, const char *f
|
||
|
while (fgets(line, BUFSIZ, fp)) {
|
||
|
if (sscanf(line, "%*s %*s %*s %x:%x %lld",
|
||
|
&tmp_maj, &tmp_min, &tmp_inode) == 3) {
|
||
|
+ const char * filepath = strchr(line, '/');
|
||
|
tmp_device = tmp_maj * 256 + tmp_min;
|
||
|
for (dev_tmp = dev_head; dev_tmp != NULL;
|
||
|
- dev_tmp = dev_tmp->next)
|
||
|
+ dev_tmp = dev_tmp->next) {
|
||
|
+ if (dev_tmp->name->name_space & NAMESPACE_NFS) {
|
||
|
+ char *nl;
|
||
|
+ if (!filepath)
|
||
|
+ continue;
|
||
|
+ if ((nl = strchr(filepath, '\n')))
|
||
|
+ nl = '\0';
|
||
|
+ if (islocatedon(filepath, dev_tmp->name->filename))
|
||
|
+ add_matched_proc(dev_tmp->name, pid,uid, access);
|
||
|
+ 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)
|
||
|
+ ino_tmp = ino_tmp->next) {
|
||
|
+ if (ino_tmp->name->name_space & NAMESPACE_NFS) {
|
||
|
+ char *nl;
|
||
|
+ if (!filepath)
|
||
|
+ continue;
|
||
|
+ if ((nl = strchr(filepath, '\n')))
|
||
|
+ nl = '\0';
|
||
|
+ if (islocatedon(filepath, ino_tmp->name->filename))
|
||
|
+ add_matched_proc(ino_tmp->name, pid,uid, access);
|
||
|
+ continue;
|
||
|
+ }
|
||
|
if (ino_tmp->device == tmp_device
|
||
|
&& ino_tmp->inode == tmp_inode)
|
||
|
add_matched_proc(ino_tmp->name, pid,
|
||
|
uid, access);
|
||
|
+ }
|
||
|
}
|
||
|
}
|
||
|
fclose(fp);
|
||
|
@@ -1571,7 +1922,7 @@ scan_knfsd(struct names *names_head, str
|
||
|
if ((find_space = strpbrk(line, " \t")) == NULL)
|
||
|
continue;
|
||
|
*find_space = '\0';
|
||
|
- if (stat(line, &st) != 0) {
|
||
|
+ if (nfssafe(stat, line, &st) != 0) {
|
||
|
continue;
|
||
|
}
|
||
|
/* Scan the devices */
|
||
|
@@ -1580,6 +1931,11 @@ scan_knfsd(struct names *names_head, str
|
||
|
if (st.st_dev == dev_tmp->device)
|
||
|
add_special_proc(dev_tmp->name, PTYPE_KNFSD, 0,
|
||
|
line);
|
||
|
+ if ((dev_tmp->name->name_space & NAMESPACE_NFS) == 0)
|
||
|
+ continue;
|
||
|
+ if (strcmp(line, dev_tmp->name->filename))
|
||
|
+ continue;
|
||
|
+ add_special_proc(dev_tmp->name, PTYPE_MOUNT, 0, line);
|
||
|
}
|
||
|
for (ino_tmp = ino_head; ino_tmp != NULL;
|
||
|
ino_tmp = ino_tmp->next) {
|
||
|
@@ -1587,53 +1943,55 @@ scan_knfsd(struct names *names_head, str
|
||
|
&& st.st_ino == ino_tmp->inode)
|
||
|
add_special_proc(ino_tmp->name, PTYPE_KNFSD, 0,
|
||
|
line);
|
||
|
+ if ((ino_tmp->name->name_space & NAMESPACE_NFS) == 0)
|
||
|
+ continue;
|
||
|
+ if (strcmp(line, ino_tmp->name->filename))
|
||
|
+ continue;
|
||
|
+ add_special_proc(ino_tmp->name, PTYPE_MOUNT, 0, line);
|
||
|
}
|
||
|
}
|
||
|
fclose(fp);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
-scan_mounts(struct names *names_head, struct inode_list *ino_head,
|
||
|
- struct device_list *dev_head)
|
||
|
+scan_mounts(struct names *names_head, struct mount_list *mounts,
|
||
|
+ struct inode_list *ino_head, struct device_list *dev_head)
|
||
|
{
|
||
|
struct device_list *dev_tmp;
|
||
|
struct inode_list *ino_tmp;
|
||
|
- FILE *fp;
|
||
|
- char line[BUFSIZ];
|
||
|
- char *find_mountp;
|
||
|
- char *find_space;
|
||
|
+ struct mount_list *mountptr;
|
||
|
struct stat st;
|
||
|
|
||
|
- 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)
|
||
|
+ for (mountptr = mounts ; mountptr != NULL ; mountptr = mountptr->next) {
|
||
|
+ if (nfssafe(stat, mountptr->mountpoint, &st) != 0)
|
||
|
continue;
|
||
|
- *find_space = '\0';
|
||
|
- if (stat(find_mountp, &st) != 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)
|
||
|
add_special_proc(dev_tmp->name, PTYPE_MOUNT, 0,
|
||
|
- find_mountp);
|
||
|
+ mountptr->mountpoint);
|
||
|
+ if ((dev_tmp->name->name_space & NAMESPACE_NFS) == 0)
|
||
|
+ continue;
|
||
|
+ if (strcmp(mountptr->mountpoint, dev_tmp->name->filename))
|
||
|
+ continue;
|
||
|
+ add_special_proc(dev_tmp->name, PTYPE_MOUNT, 0,
|
||
|
+ mountptr->mountpoint);
|
||
|
}
|
||
|
for (ino_tmp = ino_head; ino_tmp != NULL;
|
||
|
ino_tmp = ino_tmp->next) {
|
||
|
if (st.st_dev == ino_tmp->device
|
||
|
&& st.st_ino == ino_tmp->inode)
|
||
|
add_special_proc(ino_tmp->name, PTYPE_MOUNT, 0,
|
||
|
- find_mountp);
|
||
|
+ mountptr->mountpoint);
|
||
|
+ if ((ino_tmp->name->name_space & NAMESPACE_NFS) == 0)
|
||
|
+ continue;
|
||
|
+ if (strcmp(mountptr->mountpoint, ino_tmp->name->filename))
|
||
|
+ continue;
|
||
|
+ add_special_proc(ino_tmp->name, PTYPE_MOUNT, 0,
|
||
|
+ mountptr->mountpoint);
|
||
|
}
|
||
|
}
|
||
|
- fclose(fp);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
@@ -1682,3 +2040,59 @@ scan_swaps(struct names *names_head, str
|
||
|
}
|
||
|
fclose(fp);
|
||
|
}
|
||
|
+
|
||
|
+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 2010-07-13 12:53:15.000000000 +0000
|
||
|
@@ -66,6 +66,11 @@ struct inode_list {
|
||
|
struct inode_list *next;
|
||
|
};
|
||
|
|
||
|
+struct mountdev_list {
|
||
|
+ char *dir;
|
||
|
+ struct mountdev_list *next;
|
||
|
+};
|
||
|
+
|
||
|
struct device_list {
|
||
|
struct names *name;
|
||
|
dev_t device;
|
||
|
@@ -85,9 +90,33 @@ struct mount_list {
|
||
|
struct mount_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
|