/* * readlink reads the link of `curproc' or `file' */ static int procfs_readlink(struct vop_readlink_args *ap) { char buf[16]; /* should be enough */ struct proc *procp; struct vnode *vp = ap->a_vp; struct pfsnode *pfs = VTOPFS(vp); char *fullpath, *freepath; int error, len; switch (pfs->pfs_type) { case Pcurproc: if (pfs->pfs_fileno != PROCFS_FILENO(0, Pcurproc)) return (EINVAL); len = ksnprintf(buf, sizeof(buf), "%ld", (long)curproc->p_pid); return (uiomove(buf, len, ap->a_uio)); /* * There _should_ be no way for an entire process to disappear * from under us... */ case Pfile: procp = pfs_pfind(pfs->pfs_pid); if (procp == NULL || procp->p_ucred == NULL) { kprintf("procfs_readlink: pid %d disappeared\n", pfs->pfs_pid); if (procp) PRELE(procp); return (uiomove("unknown", sizeof("unknown") - 1, ap->a_uio)); } error = cache_fullpath(procp, &procp->p_textnch, &fullpath, &freepath, 0); if (error != 0) { if (procp) PRELE(procp); return (uiomove("unknown", sizeof("unknown") - 1, ap->a_uio)); } error = uiomove(fullpath, strlen(fullpath), ap->a_uio); kfree(freepath, M_TEMP); if (procp) PRELE(procp); return (error); default: return (EINVAL); } }
static int procfs_readdir_proc(struct vop_readdir_args *ap) { struct pfsnode *pfs; int error, i, retval; struct proc *p; struct lwp *lp; struct proc_target *pt; struct uio *uio = ap->a_uio; pfs = VTOPFS(ap->a_vp); p = pfs_pfind(pfs->pfs_pid); if (p == NULL) return(0); if (!PRISON_CHECK(ap->a_cred, p->p_ucred)) { error = 0; goto done; } /* XXX lwp, not MPSAFE */ lp = FIRST_LWP_IN_PROC(p); error = 0; i = (int)uio->uio_offset; if (i < 0) { error = EINVAL; goto done; } for (pt = &proc_targets[i]; !error && uio->uio_resid > 0 && i < nproc_targets; pt++, i++) { if (pt->pt_valid && (*pt->pt_valid)(lp) == 0) continue; retval = vop_write_dirent(&error, uio, PROCFS_FILENO(pfs->pfs_pid, pt->pt_pfstype), pt->pt_type, pt->pt_namlen, pt->pt_name); if (retval) break; } uio->uio_offset = (off_t)i; error = 0; done: PRELE(p); return error; }
/* * readdir returns directory entries from pfsnode (vp). * * the strategy here with procfs is to generate a single * directory entry at a time (struct dirent) and then * copy that out to userland using uiomove. a more efficent * though more complex implementation, would try to minimize * the number of calls to uiomove(). for procfs, this is * hardly worth the added code complexity. * * this should just be done through read() */ int procfs_readdir(void *v) { struct vop_readdir_args *ap = v; struct uio *uio = ap->a_uio; struct dirent d; struct pfsnode *pfs; struct vnode *vp; int i; int error; vp = ap->a_vp; pfs = VTOPFS(vp); if (uio->uio_resid < UIO_MX) return (EINVAL); error = 0; i = uio->uio_offset; if (i < 0) return (EINVAL); bzero(&d, UIO_MX); d.d_reclen = UIO_MX; switch (pfs->pfs_type) { /* * this is for the process-specific sub-directories. * all that is needed to is copy out all the entries * from the procent[] table (top of this file). */ case Pproc: { struct proc *p; const struct proc_target *pt; p = pfind(pfs->pfs_pid); if (p == NULL) break; for (pt = &proc_targets[i]; uio->uio_resid >= UIO_MX && i < nproc_targets; pt++, i++) { if (pt->pt_valid && (*pt->pt_valid)(p, vp->v_mount) == 0) continue; d.d_fileno = PROCFS_FILENO(pfs->pfs_pid, pt->pt_pfstype); d.d_namlen = pt->pt_namlen; bcopy(pt->pt_name, d.d_name, pt->pt_namlen + 1); d.d_type = pt->pt_type; if ((error = uiomove(&d, UIO_MX, uio)) != 0) break; } break; } /* * this is for the root of the procfs filesystem * what is needed is a special entry for "curproc" * followed by an entry for each process on allproc #ifdef PROCFS_ZOMBIE * and zombproc. #endif */ case Proot: { #ifdef PROCFS_ZOMBIE int doingzomb = 0; #endif int pcnt = i; volatile struct proc *p = LIST_FIRST(&allproc); if (pcnt > 3) pcnt = 3; #ifdef PROCFS_ZOMBIE again: #endif for (; p && uio->uio_resid >= UIO_MX; i++, pcnt++) { switch (i) { case 0: /* `.' */ case 1: /* `..' */ d.d_fileno = PROCFS_FILENO(0, Proot); d.d_namlen = i + 1; bcopy("..", d.d_name, d.d_namlen); d.d_name[i + 1] = '\0'; d.d_type = DT_DIR; break; case 2: d.d_fileno = PROCFS_FILENO(0, Pcurproc); d.d_namlen = 7; bcopy("curproc", d.d_name, 8); d.d_type = DT_LNK; break; case 3: d.d_fileno = PROCFS_FILENO(0, Pself); d.d_namlen = 4; bcopy("self", d.d_name, 5); d.d_type = DT_LNK; break; case 4: if (VFSTOPROC(vp->v_mount)->pmnt_flags & PROCFSMNT_LINUXCOMPAT) { d.d_fileno = PROCFS_FILENO(0, Pcpuinfo); d.d_namlen = 7; bcopy("cpuinfo", d.d_name, 8); d.d_type = DT_REG; break; } /* fall through */ case 5: if (VFSTOPROC(vp->v_mount)->pmnt_flags & PROCFSMNT_LINUXCOMPAT) { d.d_fileno = PROCFS_FILENO(0, Pmeminfo); d.d_namlen = 7; bcopy("meminfo", d.d_name, 8); d.d_type = DT_REG; break; } /* fall through */ default: while (pcnt < i) { pcnt++; p = LIST_NEXT(p, p_list); if (!p) goto done; } d.d_fileno = PROCFS_FILENO(p->p_pid, Pproc); d.d_namlen = snprintf(d.d_name, sizeof(d.d_name), "%ld", (long)p->p_pid); d.d_type = DT_REG; p = LIST_NEXT(p, p_list); break; } if ((error = uiomove(&d, UIO_MX, uio)) != 0) break; } done: #ifdef PROCFS_ZOMBIE if (p == 0 && doingzomb == 0) { doingzomb = 1; p = LIST_FIRST(&zombproc); goto again; } #endif break; } default: error = ENOTDIR; break; } uio->uio_offset = i; return (error); }
int procfs_loadvnode(struct mount *mp, struct vnode *vp, const void *key, size_t key_len, const void **new_key) { int error; struct pfskey pfskey; struct pfsnode *pfs; KASSERT(key_len == sizeof(pfskey)); memcpy(&pfskey, key, key_len); pfs = kmem_alloc(sizeof(*pfs), KM_SLEEP); pfs->pfs_pid = pfskey.pk_pid; pfs->pfs_type = pfskey.pk_type; pfs->pfs_fd = pfskey.pk_fd; pfs->pfs_vnode = vp; pfs->pfs_flags = 0; pfs->pfs_fileno = PROCFS_FILENO(pfs->pfs_pid, pfs->pfs_type, pfs->pfs_fd); vp->v_tag = VT_PROCFS; vp->v_op = procfs_vnodeop_p; vp->v_data = pfs; switch (pfs->pfs_type) { case PFSroot: /* /proc = dr-xr-xr-x */ vp->v_vflag |= VV_ROOT; /*FALLTHROUGH*/ case PFSproc: /* /proc/N = dr-xr-xr-x */ pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; vp->v_type = VDIR; break; case PFStask: /* /proc/N/task = dr-xr-xr-x */ if (pfs->pfs_fd == -1) { pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP| S_IROTH|S_IXOTH; vp->v_type = VDIR; break; } /*FALLTHROUGH*/ case PFScurproc: /* /proc/curproc = lr-xr-xr-x */ case PFSself: /* /proc/self = lr-xr-xr-x */ case PFScwd: /* /proc/N/cwd = lr-xr-xr-x */ case PFSchroot: /* /proc/N/chroot = lr-xr-xr-x */ case PFSexe: /* /proc/N/exe = lr-xr-xr-x */ pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; vp->v_type = VLNK; break; case PFSfd: if (pfs->pfs_fd == -1) { /* /proc/N/fd = dr-x------ */ pfs->pfs_mode = S_IRUSR|S_IXUSR; vp->v_type = VDIR; } else { /* /proc/N/fd/M = [ps-]rw------- */ file_t *fp; vnode_t *vxp; struct proc *p; mutex_enter(proc_lock); p = proc_find(pfs->pfs_pid); mutex_exit(proc_lock); if (p == NULL) { error = ENOENT; goto bad; } KASSERT(rw_read_held(&p->p_reflock)); if ((fp = fd_getfile2(p, pfs->pfs_fd)) == NULL) { error = EBADF; goto bad; } pfs->pfs_mode = S_IRUSR|S_IWUSR; switch (fp->f_type) { case DTYPE_VNODE: vxp = fp->f_vnode; /* * We make symlinks for directories * to avoid cycles. */ if (vxp->v_type == VDIR) goto symlink; vp->v_type = vxp->v_type; break; case DTYPE_PIPE: vp->v_type = VFIFO; break; case DTYPE_SOCKET: vp->v_type = VSOCK; break; case DTYPE_KQUEUE: case DTYPE_MISC: case DTYPE_SEM: symlink: pfs->pfs_mode = S_IRUSR|S_IXUSR|S_IRGRP| S_IXGRP|S_IROTH|S_IXOTH; vp->v_type = VLNK; break; default: error = EOPNOTSUPP; closef(fp); goto bad; } closef(fp); } break; case PFSfile: /* /proc/N/file = -rw------- */ case PFSmem: /* /proc/N/mem = -rw------- */ case PFSregs: /* /proc/N/regs = -rw------- */ case PFSfpregs: /* /proc/N/fpregs = -rw------- */ pfs->pfs_mode = S_IRUSR|S_IWUSR; vp->v_type = VREG; break; case PFSctl: /* /proc/N/ctl = --w------ */ case PFSnote: /* /proc/N/note = --w------ */ case PFSnotepg: /* /proc/N/notepg = --w------ */ pfs->pfs_mode = S_IWUSR; vp->v_type = VREG; break; case PFSmap: /* /proc/N/map = -r--r--r-- */ case PFSmaps: /* /proc/N/maps = -r--r--r-- */ case PFSstatus: /* /proc/N/status = -r--r--r-- */ case PFSstat: /* /proc/N/stat = -r--r--r-- */ case PFScmdline: /* /proc/N/cmdline = -r--r--r-- */ case PFSemul: /* /proc/N/emul = -r--r--r-- */ case PFSmeminfo: /* /proc/meminfo = -r--r--r-- */ case PFScpustat: /* /proc/stat = -r--r--r-- */ case PFSdevices: /* /proc/devices = -r--r--r-- */ case PFScpuinfo: /* /proc/cpuinfo = -r--r--r-- */ case PFSuptime: /* /proc/uptime = -r--r--r-- */ case PFSmounts: /* /proc/mounts = -r--r--r-- */ case PFSloadavg: /* /proc/loadavg = -r--r--r-- */ case PFSstatm: /* /proc/N/statm = -r--r--r-- */ case PFSversion: /* /proc/version = -r--r--r-- */ pfs->pfs_mode = S_IRUSR|S_IRGRP|S_IROTH; vp->v_type = VREG; break; #ifdef __HAVE_PROCFS_MACHDEP PROCFS_MACHDEP_NODETYPE_CASES procfs_machdep_allocvp(vp); break; #endif default: panic("procfs_allocvp"); } uvm_vnp_setsize(vp, 0); *new_key = &pfs->pfs_key; return 0; bad: vp->v_tag =VT_NON; vp->v_type = VNON; vp->v_op = NULL; vp->v_data = NULL; kmem_free(pfs, sizeof(*pfs)); return error; }
static int procfs_readdir_root_callback(struct proc *p, void *data) { struct procfs_readdir_root_info *info = data; struct uio *uio; int retval; ino_t d_ino; const char *d_name; char d_name_pid[20]; size_t d_namlen; uint8_t d_type; uio = info->uio; if (uio->uio_resid <= 0 || info->error) return(-1); switch (info->pcnt) { case 0: /* `.' */ d_ino = PROCFS_FILENO(0, Proot); d_name = "."; d_namlen = 1; d_type = DT_DIR; break; case 1: /* `..' */ d_ino = PROCFS_FILENO(0, Proot); d_name = ".."; d_namlen = 2; d_type = DT_DIR; break; case 2: d_ino = PROCFS_FILENO(0, Pcurproc); d_namlen = 7; d_name = "curproc"; d_type = DT_LNK; break; default: if (!PRISON_CHECK(info->cred, p->p_ucred)) return(0); if (ps_showallprocs == 0 && info->cred->cr_uid != 0 && info->cred->cr_uid != p->p_ucred->cr_uid) { return(0); } /* * Skip entries we have already returned (optimization) */ if (info->pcnt < info->i) { ++info->pcnt; return(0); } d_ino = PROCFS_FILENO(p->p_pid, Pproc); d_namlen = ksnprintf(d_name_pid, sizeof(d_name_pid), "%ld", (long)p->p_pid); d_name = d_name_pid; d_type = DT_DIR; break; } /* * Skip entries we have already returned (optimization) */ if (info->pcnt < info->i) { ++info->pcnt; return(0); } retval = vop_write_dirent(&info->error, uio, d_ino, d_type, d_namlen, d_name); if (retval) return(-1); ++info->pcnt; ++info->i; return(0); }