/* * dmnt.c - FreeBSD mount support functions for lsof */ /* * Copyright 1994 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Victor A. Abell * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ #ifndef lint static char copyright[] = "@(#) Copyright 1994 Purdue Research Foundation.\nAll rights reserved.\n"; static char *rcsid = "$Id: dmnt.c,v 1.16 2009/03/25 19:23:06 abe Exp $"; #endif #include "lsof.h" /* * Local static information */ static struct mounts *Lmi = (struct mounts *)NULL; /* local mount info */ static int Lmist = 0; /* Lmi status */ #undef HAS_MNT_NAMES #if FREEBSDV<2000 static char *mnt_names[] = { "none", "ufs", "nfs", "mfs", "pc", "iso9600", "procfs", "devfs" }; #define HAS_MNT_NAMES 1 #else /* FREEBSDV>=2000 */ # if defined(MOUNT_NONE) static char *mnt_names[] = INITMOUNTNAMES; #define HAS_MNT_NAMES 1 # endif /* defined(MOUNT_NONE)) */ #endif /* FREEBSDV<2000 */ #if FREEBSDV>=5000 && defined(HAS_NO_SI_UDEV) /* * Dev2Udev() -- convert a kernel device number to a user device number */ dev_t Dev2Udev(c) # if defined(HAS_CONF_MINOR) || defined(HAS_CDEV2PRIV) KA_T c; # else /* !defined(HAS_CONF_MINOR) && !defined(HAS_CDEV2PRIV) */ struct cdev *c; # endif /* defined(HAS_CONF_MINOR) || defined(HAS_CDEV2PRIV) */ { # if !defined(HAS_CONF_MINOR) && !defined(HAS_CDEV2PRIV) char *cp; char *dn = (char *)NULL; char *ln = (char *)NULL; struct statfs *mb; int n, sr; static u_int s; struct stat sb; static int ss = 0; # endif /* !defined(HAS_CONF_MINOR) && !defined(HAS_CDEV2PRIV) */ # if defined(HAS_CONF_MINOR) || defined(HAS_CDEV2PRIV) KA_T ca; struct cdev_priv cp; if (!c) return(NODEV); # if defined(HAS_CDEV2PRIV) ca = (KA_T)cdev2priv(c); # else /* !defined(HAS_CDEV2PRIV) */ ca = (KA_T)member2struct(cdev_priv, cdp_c, c); # endif /* defined(HAS_CDEV2PRIV) */ if (kread((KA_T)ca, (char *)&cp, sizeof(cp))) return(NODEV); return((dev_t)cp.cdp_inode); # else /* !defined(HAS_CONF_MINOR) && !defined(HAS_CDEV2PRIV) */ # if defined(HAS_SI_PRIV) /* * If the cdev structure has a private sub-structure, read it. */ struct cdev_priv sp; if (!c->si_priv || kread((KA_T)c->si_priv, (char *)&sp, sizeof(sp))) return(0); # endif /* defined(HAS_SI_PRIV) */ if (ss) { # if defined(HAS_SI_PRIV) return(sp.cdp_inode ^ s); # else /* !defined(HAS_SI_PRIV) */ return(c->si_inode ^ s); # endif /* defined(HAS_SI_PRIV) */ } /* * Determine the random udev seed from stat(2) operations on "/" and * its device. */ if ((n = getmntinfo(&mb, MNT_NOWAIT)) <= 0) { (void) fprintf(stderr, "%s: no mount information\n", Pn); Exit(1); } for (; n; n--, mb++) { # if defined(MOUNT_NONE) if (mb->f_type == MOUNT_NONE || mb->f_type >= MOUNT_MAXTYPE) # else /* !defined(MOUNT_NONE) */ if (!mb->f_type) # endif /* defined(MOUNT_NONE) */ continue; /* * Get the real directory name. Ignore all but the root directory; * safely stat("/"). */ if (dn) (void) free((FREE_P *)dn); if (!(dn = mkstrcpy(mb->f_mntonname, (MALLOC_S *)NULL))) { Dev2Udev_no_space: (void) fprintf(stderr, "%s: no space for mount at ", Pn); safestrprt(mb->f_mntonname, stderr, 0); (void) fprintf(stderr, " ("); safestrprt(mb->f_mntfromname, stderr, 0); (void) fprintf(stderr, ")\n"); Exit(1); } if (!(ln = Readlink(dn))) { if (!Fwarn) { (void) fprintf(stderr, " Output information may be incomplete.\n"); } continue; } if (ln != dn) { (void) free((FREE_P *)dn); dn = ln; } ln = (char *)NULL; if (strcmp(dn, "/")) continue; if (statsafely(dn, &sb)) continue; /* * Get the real device name and safely stat(2) it. */ (void) free((FREE_P *)dn); if (!(dn = mkstrcpy(mb->f_mntfromname, (MALLOC_S *)NULL))) goto Dev2Udev_no_space; ln = Readlink(dn); if ((sr = statsafely(ln, &sb))) { /* * If the device stat(2) failed, see if the device name indicates * an NFS mount, a cd9660 device, or a ZFS mount. If any condition * is true, set the user device number seed to zero. */ if (((cp = strrchr(ln, ':')) && (*(cp + 1) == '/')) || !strcasecmp(mb->f_fstypename, "cd9660") || !strcasecmp(mb->f_fstypename, "zfs") ) { ss = 1; s = (u_int)0; } } if (ln != dn) (void) free((FREE_P *)ln); ln = (char *)NULL; (void) free((FREE_P *)dn); dn = (char *)NULL; if (sr && !ss) continue; if (!ss) { ss = 1; s = (u_int)sb.st_ino ^ (u_int)sb.st_rdev; } break; } /* * Free string copies, as required. */ if (dn) (void) free((FREE_P *)dn); if (ln) (void) free((FREE_P *)ln); /* * If the device seed is known, return its application to the cdev structure's * inode. */ if (ss) { # if defined(HAS_SI_PRIV) return(sp.cdp_inode ^ s); # else /* !defined(HAS_SI_PRIV) */ return(c->si_inode ^ s); # endif /* defined(HAS_SI_PRIV) */ } (void) fprintf(stderr, "%s: can't determine user device random seed.\n", Pn); Exit(1); # endif /* !defined(HAS_CONF_MINOR) */ } #endif /* FREEBSDV>=5000 && defined(HAS_NO_SI_UDEV) */ /* * readmnt() - read mount table */ struct mounts * readmnt() { char *dn = (char *)NULL; char *ln; struct statfs *mb; struct mounts *mtp; int n; struct stat sb; #if defined(HASPROCFS) unsigned char procfs = 0; #endif /* defined(HASPROCFS) */ if (Lmi || Lmist) return(Lmi); /* * Access mount information. */ if ((n = getmntinfo(&mb, MNT_NOWAIT)) <= 0) { (void) fprintf(stderr, "%s: no mount information\n", Pn); return(0); } /* * Read mount information. */ for (; n; n--, mb++) { #if defined(MOUNT_NONE) if (mb->f_type == MOUNT_NONE || mb->f_type >= MOUNT_MAXTYPE) #else /* !defined(MOUNT_NONE) */ if (!mb->f_type) #endif /* defined(MOUNT_NONE) */ continue; /* * Interpolate a possible symbolic directory link. */ if (dn) (void) free((FREE_P *)dn); if (!(dn = mkstrcpy(mb->f_mntonname, (MALLOC_S *)NULL))) { no_space_for_mount: (void) fprintf(stderr, "%s: no space for mount at ", Pn); safestrprt(mb->f_mntonname, stderr, 0); (void) fprintf(stderr, " ("); safestrprt(mb->f_mntfromname, stderr, 0); (void) fprintf(stderr, ")\n"); Exit(1); } if (!(ln = Readlink(dn))) { if (!Fwarn) { (void) fprintf(stderr, " Output information may be incomplete.\n"); } continue; } if (ln != dn) { (void) free((FREE_P *)dn); dn = ln; } if (*dn != '/') continue; /* * Stat() the directory. */ if (statsafely(dn, &sb)) { if (!Fwarn) { (void) fprintf(stderr, "%s: WARNING: can't stat() ", Pn); #if defined(HAS_MNT_NAMES) safestrprt(mnt_names[mb->f_type], stderr, 0); #else /* !defined(HAS_MNT_NAMES) */ safestrprt(mb->f_fstypename, stderr, 0); #endif /* defined(HAS_MNT_NAMES) */ (void) fprintf(stderr, " file system "); safestrprt(mb->f_mntonname, stderr, 1); (void) fprintf(stderr, " Output information may be incomplete.\n"); } (void) bzero((char *)&sb, sizeof(sb)); sb.st_dev = (dev_t)mb->f_fsid.val[0]; sb.st_mode = S_IFDIR | 0777; if (!Fwarn) { (void) fprintf(stderr, " assuming \"dev=%x\" from mount table\n", sb.st_dev); } } /* * Allocate and fill a local mount structure. */ if (!(mtp = (struct mounts *)malloc(sizeof(struct mounts)))) goto no_space_for_mount; mtp->dir = dn; dn = (char *)NULL; #if defined(HASPROCFS) #if defined(MOUNT_NONE) if (mb->f_type == MOUNT_PROCFS) #else /* !defined(MOUNT_NONE) */ if (strcasecmp(mb->f_fstypename, "procfs") == 0) #endif /* defined(MOUNT_NONE) */ { /* * Save information on exactly one procfs file system. */ if (procfs) Mtprocfs = (struct mounts *)NULL; else { procfs = 1; Mtprocfs = mtp; } } #endif /* defined(HASPROCFS) */ mtp->next = Lmi; mtp->dev = sb.st_dev; mtp->rdev = sb.st_rdev; mtp->inode = (INODETYPE)sb.st_ino; mtp->mode = sb.st_mode; /* * Interpolate a possible file system (mounted-on) device name link. */ if (!(dn = mkstrcpy(mb->f_mntfromname, (MALLOC_S *)NULL))) goto no_space_for_mount; mtp->fsname = dn; ln = Readlink(dn); dn = (char *)NULL; /* * Stat() the file system (mounted-on) name and add file system * information to the local mount table entry. */ if (!ln || statsafely(ln, &sb)) sb.st_mode = 0; mtp->fsnmres = ln; mtp->fs_mode = sb.st_mode; Lmi = mtp; } /* * Clean up and return the local mount info table address. */ if (dn) (void) free((FREE_P *)dn); Lmist = 1; return(Lmi); } /* * readvfs() - read vfs structure */ struct l_vfs * readvfs(vm) KA_T vm; /* kernel mount address from vnode */ { struct mount m; struct l_vfs *vp; /* * Search for match on existing entry. */ for (vp = Lvfs; vp; vp = vp->next) { if (vm == vp->addr) return(vp); } /* * Read the (new) mount structure, allocate a local entry, and fill it. */ if (kread((KA_T)vm, (char *)&m, sizeof(m)) != 0) return((struct l_vfs *)NULL); if (!(vp = (struct l_vfs *)malloc(sizeof(struct l_vfs)))) { (void) fprintf(stderr, "%s: PID %d, no space for vfs\n", Pn, Lp->pid); Exit(1); } if (!(vp->dir = mkstrcpy(m.m_stat.f_mntonname, (MALLOC_S *)NULL)) || !(vp->fsname = mkstrcpy(m.m_stat.f_mntfromname, (MALLOC_S *)NULL))) { (void) fprintf(stderr, "%s: PID %d, no space for mount names\n", Pn, Lp->pid); Exit(1); } vp->addr = vm; vp->fsid = m.m_stat.f_fsid; #if defined(MOUNT_NONE) vp->type = m.m_stat.f_type; #else /* !defined(MOUNT_NONE) */ { int len; if ((len = strlen(m.m_stat.f_fstypename))) { if (len > (MFSNAMELEN - 1)) len = MFSNAMELEN - 1; if (!(vp->typnm = mkstrcat(m.m_stat.f_fstypename, len, (char *)NULL, -1, (char *)NULL, -1, (MALLOC_S *)NULL))) { (void) fprintf(stderr, "%s: no space for fs type name: ", Pn); safestrprt(m.m_stat.f_fstypename, stderr, 1); Exit(1); } } else vp->typnm = ""; } #endif /* defined(MOUNT_NONE) */ vp->next = Lvfs; Lvfs = vp; return(vp); }