--- 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 #include #include +#include #include #include #include +#include #include #include #include @@ -45,6 +47,7 @@ #include #include #include +#include #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