Index: user/dchagin/lemul/sys/compat/linprocfs/linprocfs.c =================================================================== --- user/dchagin/lemul/sys/compat/linprocfs/linprocfs.c (revision 279334) +++ user/dchagin/lemul/sys/compat/linprocfs/linprocfs.c (revision 279335) @@ -1,1557 +1,1544 @@ /*- * Copyright (c) 2000 Dag-Erling Coïdan Smørgrav * Copyright (c) 1999 Pierre Beyssac * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)procfs_status.c 8.4 (Berkeley) 6/15/94 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__i386__) || defined(__amd64__) #include #include #endif /* __i386__ || __amd64__ */ #include #include #include #include #include #include /* * Various conversion macros */ #define T2J(x) ((long)(((x) * 100ULL) / (stathz ? stathz : hz))) /* ticks to jiffies */ #define T2CS(x) ((unsigned long)(((x) * 100ULL) / (stathz ? stathz : hz))) /* ticks to centiseconds */ #define T2S(x) ((x) / (stathz ? stathz : hz)) /* ticks to seconds */ #define B2K(x) ((x) >> 10) /* bytes to kbytes */ #define B2P(x) ((x) >> PAGE_SHIFT) /* bytes to pages */ #define P2B(x) ((x) << PAGE_SHIFT) /* pages to bytes */ #define P2K(x) ((x) << (PAGE_SHIFT - 10)) /* pages to kbytes */ #define TV2J(x) ((x)->tv_sec * 100UL + (x)->tv_usec / 10000) /** * @brief Mapping of ki_stat in struct kinfo_proc to the linux state * * The linux procfs state field displays one of the characters RSDZTW to * denote running, sleeping in an interruptible wait, waiting in an * uninterruptible disk sleep, a zombie process, process is being traced * or stopped, or process is paging respectively. * * Our struct kinfo_proc contains the variable ki_stat which contains a * value out of SIDL, SRUN, SSLEEP, SSTOP, SZOMB, SWAIT and SLOCK. * * This character array is used with ki_stati-1 as an index and tries to * map our states to suitable linux states. */ static char linux_state[] = "RRSTZDD"; /* * Filler function for proc/meminfo */ static int linprocfs_domeminfo(PFS_FILL_ARGS) { unsigned long memtotal; /* total memory in bytes */ unsigned long memused; /* used memory in bytes */ unsigned long memfree; /* free memory in bytes */ unsigned long memshared; /* shared memory ??? */ unsigned long buffers, cached; /* buffer / cache memory ??? */ unsigned long long swaptotal; /* total swap space in bytes */ unsigned long long swapused; /* used swap space in bytes */ unsigned long long swapfree; /* free swap space in bytes */ vm_object_t object; int i, j; memtotal = physmem * PAGE_SIZE; /* * The correct thing here would be: * memfree = vm_cnt.v_free_count * PAGE_SIZE; memused = memtotal - memfree; * * but it might mislead linux binaries into thinking there * is very little memory left, so we cheat and tell them that * all memory that isn't wired down is free. */ memused = vm_cnt.v_wire_count * PAGE_SIZE; memfree = memtotal - memused; swap_pager_status(&i, &j); swaptotal = (unsigned long long)i * PAGE_SIZE; swapused = (unsigned long long)j * PAGE_SIZE; swapfree = swaptotal - swapused; memshared = 0; mtx_lock(&vm_object_list_mtx); TAILQ_FOREACH(object, &vm_object_list, object_list) if (object->shadow_count > 1) memshared += object->resident_page_count; mtx_unlock(&vm_object_list_mtx); memshared *= PAGE_SIZE; /* * We'd love to be able to write: * buffers = bufspace; * * but bufspace is internal to vfs_bio.c and we don't feel * like unstaticizing it just for linprocfs's sake. */ buffers = 0; cached = vm_cnt.v_cache_count * PAGE_SIZE; sbuf_printf(sb, " total: used: free: shared: buffers: cached:\n" "Mem: %lu %lu %lu %lu %lu %lu\n" "Swap: %llu %llu %llu\n" "MemTotal: %9lu kB\n" "MemFree: %9lu kB\n" "MemShared:%9lu kB\n" "Buffers: %9lu kB\n" "Cached: %9lu kB\n" "SwapTotal:%9llu kB\n" "SwapFree: %9llu kB\n", memtotal, memused, memfree, memshared, buffers, cached, swaptotal, swapused, swapfree, B2K(memtotal), B2K(memfree), B2K(memshared), B2K(buffers), B2K(cached), B2K(swaptotal), B2K(swapfree)); return (0); } #if defined(__i386__) || defined(__amd64__) /* * Filler function for proc/cpuinfo (i386 & amd64 version) */ static int linprocfs_docpuinfo(PFS_FILL_ARGS) { int hw_model[2]; char model[128]; uint64_t freq; size_t size; int class, fqmhz, fqkhz; int i; /* * We default the flags to include all non-conflicting flags, * and the Intel versions of conflicting flags. */ static char *flags[] = { "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", "cx8", "apic", "sep", "sep", "mtrr", "pge", "mca", "cmov", "pat", "pse36", "pn", "b19", "b20", "b21", "mmxext", "mmx", "fxsr", "xmm", "sse2", "b27", "b28", "b29", "3dnowext", "3dnow" }; switch (cpu_class) { #ifdef __i386__ case CPUCLASS_286: class = 2; break; case CPUCLASS_386: class = 3; break; case CPUCLASS_486: class = 4; break; case CPUCLASS_586: class = 5; break; case CPUCLASS_686: class = 6; break; default: class = 0; break; #else /* __amd64__ */ default: class = 15; break; #endif } hw_model[0] = CTL_HW; hw_model[1] = HW_MODEL; model[0] = '\0'; size = sizeof(model); if (kernel_sysctl(td, hw_model, 2, &model, &size, 0, 0, 0, 0) != 0) strcpy(model, "unknown"); for (i = 0; i < mp_ncpus; ++i) { sbuf_printf(sb, "processor\t: %d\n" "vendor_id\t: %.20s\n" "cpu family\t: %u\n" "model\t\t: %u\n" "model name\t: %s\n" "stepping\t: %u\n\n", i, cpu_vendor, CPUID_TO_FAMILY(cpu_id), CPUID_TO_MODEL(cpu_id), model, cpu_id & CPUID_STEPPING); /* XXX per-cpu vendor / class / model / id? */ } sbuf_cat(sb, "flags\t\t:"); #ifdef __i386__ switch (cpu_vendor_id) { case CPU_VENDOR_AMD: if (class < 6) flags[16] = "fcmov"; break; case CPU_VENDOR_CYRIX: flags[24] = "cxmmx"; break; } #endif for (i = 0; i < 32; i++) if (cpu_feature & (1 << i)) sbuf_printf(sb, " %s", flags[i]); sbuf_cat(sb, "\n"); freq = atomic_load_acq_64(&tsc_freq); if (freq != 0) { fqmhz = (freq + 4999) / 1000000; fqkhz = ((freq + 4999) / 10000) % 100; sbuf_printf(sb, "cpu MHz\t\t: %d.%02d\n" "bogomips\t: %d.%02d\n", fqmhz, fqkhz, fqmhz, fqkhz); } return (0); } #endif /* __i386__ || __amd64__ */ /* * Filler function for proc/mtab * * This file doesn't exist in Linux' procfs, but is included here so * users can symlink /compat/linux/etc/mtab to /proc/mtab */ static int linprocfs_domtab(PFS_FILL_ARGS) { struct nameidata nd; struct mount *mp; const char *lep; char *dlep, *flep, *mntto, *mntfrom, *fstype; size_t lep_len; int error; /* resolve symlinks etc. in the emulation tree prefix */ NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, linux_emul_path, td); flep = NULL; error = namei(&nd); lep = linux_emul_path; if (error == 0) { if (vn_fullpath(td, nd.ni_vp, &dlep, &flep) == 0) lep = dlep; vrele(nd.ni_vp); } lep_len = strlen(lep); mtx_lock(&mountlist_mtx); error = 0; TAILQ_FOREACH(mp, &mountlist, mnt_list) { /* determine device name */ mntfrom = mp->mnt_stat.f_mntfromname; /* determine mount point */ mntto = mp->mnt_stat.f_mntonname; if (strncmp(mntto, lep, lep_len) == 0 && mntto[lep_len] == '/') mntto += lep_len; /* determine fs type */ fstype = mp->mnt_stat.f_fstypename; if (strcmp(fstype, pn->pn_info->pi_name) == 0) mntfrom = fstype = "proc"; else if (strcmp(fstype, "procfs") == 0) continue; if (strcmp(fstype, "linsysfs") == 0) { sbuf_printf(sb, "/sys %s sysfs %s", mntto, mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw"); } else { /* For Linux msdosfs is called vfat */ if (strcmp(fstype, "msdosfs") == 0) fstype = "vfat"; sbuf_printf(sb, "%s %s %s %s", mntfrom, mntto, fstype, mp->mnt_stat.f_flags & MNT_RDONLY ? "ro" : "rw"); } #define ADD_OPTION(opt, name) \ if (mp->mnt_stat.f_flags & (opt)) sbuf_printf(sb, "," name); ADD_OPTION(MNT_SYNCHRONOUS, "sync"); ADD_OPTION(MNT_NOEXEC, "noexec"); ADD_OPTION(MNT_NOSUID, "nosuid"); ADD_OPTION(MNT_UNION, "union"); ADD_OPTION(MNT_ASYNC, "async"); ADD_OPTION(MNT_SUIDDIR, "suiddir"); ADD_OPTION(MNT_NOSYMFOLLOW, "nosymfollow"); ADD_OPTION(MNT_NOATIME, "noatime"); #undef ADD_OPTION /* a real Linux mtab will also show NFS options */ sbuf_printf(sb, " 0 0\n"); } mtx_unlock(&mountlist_mtx); free(flep, M_TEMP); return (error); } /* * Filler function for proc/partitions * */ static int linprocfs_dopartitions(PFS_FILL_ARGS) { struct g_class *cp; struct g_geom *gp; struct g_provider *pp; struct nameidata nd; const char *lep; char *dlep, *flep; size_t lep_len; int error; int major, minor; /* resolve symlinks etc. in the emulation tree prefix */ NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, linux_emul_path, td); flep = NULL; error = namei(&nd); lep = linux_emul_path; if (error == 0) { if (vn_fullpath(td, nd.ni_vp, &dlep, &flep) == 0) lep = dlep; vrele(nd.ni_vp); } lep_len = strlen(lep); g_topology_lock(); error = 0; sbuf_printf(sb, "major minor #blocks name rio rmerge rsect " "ruse wio wmerge wsect wuse running use aveq\n"); LIST_FOREACH(cp, &g_classes, class) { if (strcmp(cp->name, "DISK") == 0 || strcmp(cp->name, "PART") == 0) LIST_FOREACH(gp, &cp->geom, geom) { LIST_FOREACH(pp, &gp->provider, provider) { if (linux_driver_get_major_minor( pp->name, &major, &minor) != 0) { major = 0; minor = 0; } sbuf_printf(sb, "%d %d %lld %s " "%d %d %d %d %d " "%d %d %d %d %d %d\n", major, minor, (long long)pp->mediasize, pp->name, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } } } g_topology_unlock(); free(flep, M_TEMP); return (error); } /* * Filler function for proc/stat */ static int linprocfs_dostat(PFS_FILL_ARGS) { struct pcpu *pcpu; long cp_time[CPUSTATES]; long *cp; int i; read_cpu_time(cp_time); sbuf_printf(sb, "cpu %ld %ld %ld %ld\n", T2J(cp_time[CP_USER]), T2J(cp_time[CP_NICE]), T2J(cp_time[CP_SYS] /*+ cp_time[CP_INTR]*/), T2J(cp_time[CP_IDLE])); CPU_FOREACH(i) { pcpu = pcpu_find(i); cp = pcpu->pc_cp_time; sbuf_printf(sb, "cpu%d %ld %ld %ld %ld\n", i, T2J(cp[CP_USER]), T2J(cp[CP_NICE]), T2J(cp[CP_SYS] /*+ cp[CP_INTR]*/), T2J(cp[CP_IDLE])); } sbuf_printf(sb, "disk 0 0 0 0\n" "page %u %u\n" "swap %u %u\n" "intr %u\n" "ctxt %u\n" "btime %lld\n", vm_cnt.v_vnodepgsin, vm_cnt.v_vnodepgsout, vm_cnt.v_swappgsin, vm_cnt.v_swappgsout, vm_cnt.v_intr, vm_cnt.v_swtch, (long long)boottime.tv_sec); return (0); } static int linprocfs_doswaps(PFS_FILL_ARGS) { struct xswdev xsw; uintmax_t total, used; int n; char devname[SPECNAMELEN + 1]; sbuf_printf(sb, "Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n"); mtx_lock(&Giant); for (n = 0; ; n++) { if (swap_dev_info(n, &xsw, devname, sizeof(devname)) != 0) break; total = (uintmax_t)xsw.xsw_nblks * PAGE_SIZE / 1024; used = (uintmax_t)xsw.xsw_used * PAGE_SIZE / 1024; /* * The space and not tab after the device name is on * purpose. Linux does so. */ sbuf_printf(sb, "/dev/%-34s unknown\t\t%jd\t%jd\t-1\n", devname, total, used); } mtx_unlock(&Giant); return (0); } /* * Filler function for proc/uptime */ static int linprocfs_douptime(PFS_FILL_ARGS) { long cp_time[CPUSTATES]; struct timeval tv; getmicrouptime(&tv); read_cpu_time(cp_time); sbuf_printf(sb, "%lld.%02ld %ld.%02lu\n", (long long)tv.tv_sec, tv.tv_usec / 10000, T2S(cp_time[CP_IDLE] / mp_ncpus), T2CS(cp_time[CP_IDLE] / mp_ncpus) % 100); return (0); } /* * Get OS build date */ static void linprocfs_osbuild(struct thread *td, struct sbuf *sb) { #if 0 char osbuild[256]; char *cp1, *cp2; strncpy(osbuild, version, 256); osbuild[255] = '\0'; cp1 = strstr(osbuild, "\n"); cp2 = strstr(osbuild, ":"); if (cp1 && cp2) { *cp1 = *cp2 = '\0'; cp1 = strstr(osbuild, "#"); } else cp1 = NULL; if (cp1) sbuf_printf(sb, "%s%s", cp1, cp2 + 1); else #endif sbuf_cat(sb, "#4 Sun Dec 18 04:30:00 CET 1977"); } /* * Get OS builder */ static void linprocfs_osbuilder(struct thread *td, struct sbuf *sb) { #if 0 char builder[256]; char *cp; cp = strstr(version, "\n "); if (cp) { strncpy(builder, cp + 5, 256); builder[255] = '\0'; cp = strstr(builder, ":"); if (cp) *cp = '\0'; } if (cp) sbuf_cat(sb, builder); else #endif sbuf_cat(sb, "des@freebsd.org"); } /* * Filler function for proc/version */ static int linprocfs_doversion(PFS_FILL_ARGS) { char osname[LINUX_MAX_UTSNAME]; char osrelease[LINUX_MAX_UTSNAME]; linux_get_osname(td, osname); linux_get_osrelease(td, osrelease); sbuf_printf(sb, "%s version %s (", osname, osrelease); linprocfs_osbuilder(td, sb); sbuf_cat(sb, ") (gcc version " __VERSION__ ") "); linprocfs_osbuild(td, sb); sbuf_cat(sb, "\n"); return (0); } /* * Filler function for proc/loadavg */ static int linprocfs_doloadavg(PFS_FILL_ARGS) { sbuf_printf(sb, "%d.%02d %d.%02d %d.%02d %d/%d %d\n", (int)(averunnable.ldavg[0] / averunnable.fscale), (int)(averunnable.ldavg[0] * 100 / averunnable.fscale % 100), (int)(averunnable.ldavg[1] / averunnable.fscale), (int)(averunnable.ldavg[1] * 100 / averunnable.fscale % 100), (int)(averunnable.ldavg[2] / averunnable.fscale), (int)(averunnable.ldavg[2] * 100 / averunnable.fscale % 100), 1, /* number of running tasks */ nprocs, /* number of tasks */ lastpid /* the last pid */ ); return (0); } /* * Filler function for proc/pid/stat */ static int linprocfs_doprocstat(PFS_FILL_ARGS) { struct kinfo_proc kp; char state; static int ratelimit = 0; vm_offset_t startcode, startdata; sx_slock(&proctree_lock); PROC_LOCK(p); fill_kinfo_proc(p, &kp); sx_sunlock(&proctree_lock); if (p->p_vmspace) { startcode = (vm_offset_t)p->p_vmspace->vm_taddr; startdata = (vm_offset_t)p->p_vmspace->vm_daddr; } else { startcode = 0; startdata = 0; }; sbuf_printf(sb, "%d", p->p_pid); #define PS_ADD(name, fmt, arg) sbuf_printf(sb, " " fmt, arg) PS_ADD("comm", "(%s)", p->p_comm); if (kp.ki_stat > sizeof(linux_state)) { state = 'R'; if (ratelimit == 0) { printf("linprocfs: don't know how to handle unknown FreeBSD state %d/%zd, mapping to R\n", kp.ki_stat, sizeof(linux_state)); ++ratelimit; } } else state = linux_state[kp.ki_stat - 1]; PS_ADD("state", "%c", state); PS_ADD("ppid", "%d", p->p_pptr ? p->p_pptr->p_pid : 0); PS_ADD("pgrp", "%d", p->p_pgid); PS_ADD("session", "%d", p->p_session->s_sid); PROC_UNLOCK(p); PS_ADD("tty", "%ju", (uintmax_t)kp.ki_tdev); PS_ADD("tpgid", "%d", kp.ki_tpgid); PS_ADD("flags", "%u", 0); /* XXX */ PS_ADD("minflt", "%lu", kp.ki_rusage.ru_minflt); PS_ADD("cminflt", "%lu", kp.ki_rusage_ch.ru_minflt); PS_ADD("majflt", "%lu", kp.ki_rusage.ru_majflt); PS_ADD("cmajflt", "%lu", kp.ki_rusage_ch.ru_majflt); PS_ADD("utime", "%ld", TV2J(&kp.ki_rusage.ru_utime)); PS_ADD("stime", "%ld", TV2J(&kp.ki_rusage.ru_stime)); PS_ADD("cutime", "%ld", TV2J(&kp.ki_rusage_ch.ru_utime)); PS_ADD("cstime", "%ld", TV2J(&kp.ki_rusage_ch.ru_stime)); PS_ADD("priority", "%d", kp.ki_pri.pri_user); PS_ADD("nice", "%d", kp.ki_nice); /* 19 (nicest) to -19 */ PS_ADD("0", "%d", 0); /* removed field */ PS_ADD("itrealvalue", "%d", 0); /* XXX */ PS_ADD("starttime", "%lu", TV2J(&kp.ki_start) - TV2J(&boottime)); PS_ADD("vsize", "%ju", P2K((uintmax_t)kp.ki_size)); PS_ADD("rss", "%ju", (uintmax_t)kp.ki_rssize); PS_ADD("rlim", "%lu", kp.ki_rusage.ru_maxrss); PS_ADD("startcode", "%ju", (uintmax_t)startcode); PS_ADD("endcode", "%ju", (uintmax_t)startdata); PS_ADD("startstack", "%u", 0); /* XXX */ PS_ADD("kstkesp", "%u", 0); /* XXX */ PS_ADD("kstkeip", "%u", 0); /* XXX */ PS_ADD("signal", "%u", 0); /* XXX */ PS_ADD("blocked", "%u", 0); /* XXX */ PS_ADD("sigignore", "%u", 0); /* XXX */ PS_ADD("sigcatch", "%u", 0); /* XXX */ PS_ADD("wchan", "%u", 0); /* XXX */ PS_ADD("nswap", "%lu", kp.ki_rusage.ru_nswap); PS_ADD("cnswap", "%lu", kp.ki_rusage_ch.ru_nswap); PS_ADD("exitsignal", "%d", 0); /* XXX */ PS_ADD("processor", "%u", kp.ki_lastcpu); PS_ADD("rt_priority", "%u", 0); /* XXX */ /* >= 2.5.19 */ PS_ADD("policy", "%u", kp.ki_pri.pri_class); /* >= 2.5.19 */ #undef PS_ADD sbuf_putc(sb, '\n'); return (0); } /* * Filler function for proc/pid/statm */ static int linprocfs_doprocstatm(PFS_FILL_ARGS) { struct kinfo_proc kp; segsz_t lsize; sx_slock(&proctree_lock); PROC_LOCK(p); fill_kinfo_proc(p, &kp); PROC_UNLOCK(p); sx_sunlock(&proctree_lock); /* * See comments in linprocfs_doprocstatus() regarding the * computation of lsize. */ /* size resident share trs drs lrs dt */ sbuf_printf(sb, "%ju ", B2P((uintmax_t)kp.ki_size)); sbuf_printf(sb, "%ju ", (uintmax_t)kp.ki_rssize); sbuf_printf(sb, "%ju ", (uintmax_t)0); /* XXX */ sbuf_printf(sb, "%ju ", (uintmax_t)kp.ki_tsize); sbuf_printf(sb, "%ju ", (uintmax_t)(kp.ki_dsize + kp.ki_ssize)); lsize = B2P(kp.ki_size) - kp.ki_dsize - kp.ki_ssize - kp.ki_tsize - 1; sbuf_printf(sb, "%ju ", (uintmax_t)lsize); sbuf_printf(sb, "%ju\n", (uintmax_t)0); /* XXX */ return (0); } /* * Filler function for proc/pid/status */ static int linprocfs_doprocstatus(PFS_FILL_ARGS) { struct kinfo_proc kp; char *state; segsz_t lsize; struct thread *td2; struct sigacts *ps; l_sigset_t siglist, sigignore, sigcatch; int i; sx_slock(&proctree_lock); PROC_LOCK(p); td2 = FIRST_THREAD_IN_PROC(p); /* XXXKSE pretend only one thread */ if (P_SHOULDSTOP(p)) { state = "T (stopped)"; } else { switch(p->p_state) { case PRS_NEW: state = "I (idle)"; break; case PRS_NORMAL: if (p->p_flag & P_WEXIT) { state = "X (exiting)"; break; } switch(td2->td_state) { case TDS_INHIBITED: state = "S (sleeping)"; break; case TDS_RUNQ: case TDS_RUNNING: state = "R (running)"; break; default: state = "? (unknown)"; break; } break; case PRS_ZOMBIE: state = "Z (zombie)"; break; default: state = "? (unknown)"; break; } } fill_kinfo_proc(p, &kp); sx_sunlock(&proctree_lock); sbuf_printf(sb, "Name:\t%s\n", p->p_comm); /* XXX escape */ sbuf_printf(sb, "State:\t%s\n", state); /* * Credentials */ sbuf_printf(sb, "Pid:\t%d\n", p->p_pid); sbuf_printf(sb, "PPid:\t%d\n", p->p_pptr ? p->p_pptr->p_pid : 0); sbuf_printf(sb, "Uid:\t%d %d %d %d\n", p->p_ucred->cr_ruid, p->p_ucred->cr_uid, p->p_ucred->cr_svuid, /* FreeBSD doesn't have fsuid */ p->p_ucred->cr_uid); sbuf_printf(sb, "Gid:\t%d %d %d %d\n", p->p_ucred->cr_rgid, p->p_ucred->cr_gid, p->p_ucred->cr_svgid, /* FreeBSD doesn't have fsgid */ p->p_ucred->cr_gid); sbuf_cat(sb, "Groups:\t"); for (i = 0; i < p->p_ucred->cr_ngroups; i++) sbuf_printf(sb, "%d ", p->p_ucred->cr_groups[i]); PROC_UNLOCK(p); sbuf_putc(sb, '\n'); /* * Memory * * While our approximation of VmLib may not be accurate (I * don't know of a simple way to verify it, and I'm not sure * it has much meaning anyway), I believe it's good enough. * * The same code that could (I think) accurately compute VmLib * could also compute VmLck, but I don't really care enough to * implement it. Submissions are welcome. */ sbuf_printf(sb, "VmSize:\t%8ju kB\n", B2K((uintmax_t)kp.ki_size)); sbuf_printf(sb, "VmLck:\t%8u kB\n", P2K(0)); /* XXX */ sbuf_printf(sb, "VmRSS:\t%8ju kB\n", P2K((uintmax_t)kp.ki_rssize)); sbuf_printf(sb, "VmData:\t%8ju kB\n", P2K((uintmax_t)kp.ki_dsize)); sbuf_printf(sb, "VmStk:\t%8ju kB\n", P2K((uintmax_t)kp.ki_ssize)); sbuf_printf(sb, "VmExe:\t%8ju kB\n", P2K((uintmax_t)kp.ki_tsize)); lsize = B2P(kp.ki_size) - kp.ki_dsize - kp.ki_ssize - kp.ki_tsize - 1; sbuf_printf(sb, "VmLib:\t%8ju kB\n", P2K((uintmax_t)lsize)); /* * Signal masks */ PROC_LOCK(p); bsd_to_linux_sigset(&p->p_siglist, &siglist); ps = p->p_sigacts; mtx_lock(&ps->ps_mtx); bsd_to_linux_sigset(&ps->ps_sigignore, &sigignore); bsd_to_linux_sigset(&ps->ps_sigcatch, &sigcatch); mtx_unlock(&ps->ps_mtx); PROC_UNLOCK(p); sbuf_printf(sb, "SigPnd:\t%016jx\n", siglist.__mask); /* * XXX. SigBlk - target thread's signal mask, td_sigmask. * To implement SigBlk pseudofs should support proc/tid dir entries. */ sbuf_printf(sb, "SigBlk:\t%016x\n", 0); sbuf_printf(sb, "SigIgn:\t%016jx\n", sigignore.__mask); sbuf_printf(sb, "SigCgt:\t%016jx\n", sigcatch.__mask); /* * Linux also prints the capability masks, but we don't have * capabilities yet, and when we do get them they're likely to * be meaningless to Linux programs, so we lie. XXX */ sbuf_printf(sb, "CapInh:\t%016x\n", 0); sbuf_printf(sb, "CapPrm:\t%016x\n", 0); sbuf_printf(sb, "CapEff:\t%016x\n", 0); return (0); } /* * Filler function for proc/pid/cwd */ static int linprocfs_doproccwd(PFS_FILL_ARGS) { char *fullpath = "unknown"; char *freepath = NULL; vn_fullpath(td, p->p_fd->fd_cdir, &fullpath, &freepath); sbuf_printf(sb, "%s", fullpath); if (freepath) free(freepath, M_TEMP); return (0); } /* * Filler function for proc/pid/root */ static int linprocfs_doprocroot(PFS_FILL_ARGS) { struct vnode *rvp; char *fullpath = "unknown"; char *freepath = NULL; rvp = jailed(p->p_ucred) ? p->p_fd->fd_jdir : p->p_fd->fd_rdir; vn_fullpath(td, rvp, &fullpath, &freepath); sbuf_printf(sb, "%s", fullpath); if (freepath) free(freepath, M_TEMP); return (0); } /* * Filler function for proc/pid/cmdline */ static int linprocfs_doproccmdline(PFS_FILL_ARGS) { int ret; PROC_LOCK(p); if ((ret = p_cansee(td, p)) != 0) { PROC_UNLOCK(p); return (ret); } /* * Mimic linux behavior and pass only processes with usermode * address space as valid. Return zero silently otherwize. */ if (p->p_vmspace == &vmspace0) { PROC_UNLOCK(p); return (0); } if (p->p_args != NULL) { sbuf_bcpy(sb, p->p_args->ar_args, p->p_args->ar_length); PROC_UNLOCK(p); return (0); } if ((p->p_flag & P_SYSTEM) != 0) { PROC_UNLOCK(p); return (0); } PROC_UNLOCK(p); ret = proc_getargv(td, p, sb); return (ret); } /* * Filler function for proc/pid/environ */ static int linprocfs_doprocenviron(PFS_FILL_ARGS) { /* * Mimic linux behavior and pass only processes with usermode * address space as valid. Return zero silently otherwize. */ if (p->p_vmspace == &vmspace0) return (0); return (proc_getenvv(td, p, sb)); } static char l32_map_str[] = "%08lx-%08lx %s%s%s%s %08lx %02x:%02x %lu%s%s\n"; static char l64_map_str[] = "%016lx-%016lx %s%s%s%s %08lx %02x:%02x %lu%s%s\n"; static char vdso_str[] = " [vdso]"; static char stack_str[] = " [stack]"; /* * Filler function for proc/pid/maps */ static int linprocfs_doprocmaps(PFS_FILL_ARGS) { struct vmspace *vm; vm_map_t map; vm_map_entry_t entry, tmp_entry; vm_object_t obj, tobj, lobj; vm_offset_t e_start, e_end; vm_ooffset_t off = 0; vm_prot_t e_prot; unsigned int last_timestamp; char *name = "", *freename = NULL; const char *l_map_str; ino_t ino; int ref_count, shadow_count, flags; int error; struct vnode *vp; struct vattr vat; PROC_LOCK(p); error = p_candebug(td, p); PROC_UNLOCK(p); if (error) return (error); if (uio->uio_rw != UIO_READ) return (EOPNOTSUPP); error = 0; vm = vmspace_acquire_ref(p); if (vm == NULL) return (ESRCH); if (SV_CURPROC_FLAG(SV_LP64)) l_map_str = l64_map_str; else l_map_str = l32_map_str; map = &vm->vm_map; vm_map_lock_read(map); for (entry = map->header.next; entry != &map->header; entry = entry->next) { name = ""; freename = NULL; if (entry->eflags & MAP_ENTRY_IS_SUB_MAP) continue; e_prot = entry->protection; e_start = entry->start; e_end = entry->end; obj = entry->object.vm_object; for (lobj = tobj = obj; tobj; tobj = tobj->backing_object) { VM_OBJECT_RLOCK(tobj); if (lobj != obj) VM_OBJECT_RUNLOCK(lobj); lobj = tobj; } last_timestamp = map->timestamp; vm_map_unlock_read(map); ino = 0; if (lobj) { off = IDX_TO_OFF(lobj->size); if (lobj->type == OBJT_VNODE) { vp = lobj->handle; if (vp) vref(vp); } else vp = NULL; if (lobj != obj) VM_OBJECT_RUNLOCK(lobj); flags = obj->flags; ref_count = obj->ref_count; shadow_count = obj->shadow_count; VM_OBJECT_RUNLOCK(obj); if (vp) { vn_fullpath(td, vp, &name, &freename); vn_lock(vp, LK_SHARED | LK_RETRY); VOP_GETATTR(vp, &vat, td->td_ucred); ino = vat.va_fileid; vput(vp); } else if (SV_PROC_ABI(p) == SV_ABI_LINUX) { if (e_start == p->p_sysent->sv_shared_page_base) name = vdso_str; if (e_end == p->p_sysent->sv_usrstack) name = stack_str; } } else { flags = 0; ref_count = 0; shadow_count = 0; } /* * format: * start, end, access, offset, major, minor, inode, name. */ error = sbuf_printf(sb, l_map_str, (u_long)e_start, (u_long)e_end, (e_prot & VM_PROT_READ)?"r":"-", (e_prot & VM_PROT_WRITE)?"w":"-", (e_prot & VM_PROT_EXECUTE)?"x":"-", "p", (u_long)off, 0, 0, (u_long)ino, *name ? " " : "", name ); if (freename) free(freename, M_TEMP); vm_map_lock_read(map); if (error == -1) { error = 0; break; } if (last_timestamp != map->timestamp) { /* * Look again for the entry because the map was * modified while it was unlocked. Specifically, * the entry may have been clipped, merged, or deleted. */ vm_map_lookup_entry(map, e_end - 1, &tmp_entry); entry = tmp_entry; } } vm_map_unlock_read(map); vmspace_free(vm); return (error); } /* * Criteria for interface name translation */ #define IFP_IS_ETH(ifp) (ifp->if_type == IFT_ETHER) static int linux_ifname(struct ifnet *ifp, char *buffer, size_t buflen) { struct ifnet *ifscan; int ethno; IFNET_RLOCK_ASSERT(); /* Short-circuit non ethernet interfaces */ if (!IFP_IS_ETH(ifp)) return (strlcpy(buffer, ifp->if_xname, buflen)); /* Determine the (relative) unit number for ethernet interfaces */ ethno = 0; TAILQ_FOREACH(ifscan, &V_ifnet, if_link) { if (ifscan == ifp) return (snprintf(buffer, buflen, "eth%d", ethno)); if (IFP_IS_ETH(ifscan)) ethno++; } return (0); } /* * Filler function for proc/net/dev */ static int linprocfs_donetdev(PFS_FILL_ARGS) { char ifname[16]; /* XXX LINUX_IFNAMSIZ */ struct ifnet *ifp; sbuf_printf(sb, "%6s|%58s|%s\n" "%6s|%58s|%58s\n", "Inter-", " Receive", " Transmit", " face", "bytes packets errs drop fifo frame compressed multicast", "bytes packets errs drop fifo colls carrier compressed"); CURVNET_SET(TD_TO_VNET(curthread)); IFNET_RLOCK(); TAILQ_FOREACH(ifp, &V_ifnet, if_link) { linux_ifname(ifp, ifname, sizeof ifname); sbuf_printf(sb, "%6.6s: ", ifname); sbuf_printf(sb, "%7ju %7ju %4ju %4ju %4lu %5lu %10lu %9ju ", (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IBYTES), (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IPACKETS), (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IERRORS), (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IQDROPS), /* rx_missed_errors */ 0UL, /* rx_fifo_errors */ 0UL, /* rx_length_errors + * rx_over_errors + * rx_crc_errors + * rx_frame_errors */ 0UL, /* rx_compressed */ (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_IMCASTS)); /* XXX-BZ rx only? */ sbuf_printf(sb, "%8ju %7ju %4ju %4ju %4lu %5ju %7lu %10lu\n", (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_OBYTES), (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_OPACKETS), (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_OERRORS), (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_OQDROPS), 0UL, /* tx_fifo_errors */ (uintmax_t )ifp->if_get_counter(ifp, IFCOUNTER_COLLISIONS), 0UL, /* tx_carrier_errors + * tx_aborted_errors + * tx_window_errors + * tx_heartbeat_errors*/ 0UL); /* tx_compressed */ } IFNET_RUNLOCK(); CURVNET_RESTORE(); return (0); } /* * Filler function for proc/sys/kernel/osrelease */ static int linprocfs_doosrelease(PFS_FILL_ARGS) { char osrelease[LINUX_MAX_UTSNAME]; linux_get_osrelease(td, osrelease); sbuf_printf(sb, "%s\n", osrelease); return (0); } /* * Filler function for proc/sys/kernel/ostype */ static int linprocfs_doostype(PFS_FILL_ARGS) { char osname[LINUX_MAX_UTSNAME]; linux_get_osname(td, osname); sbuf_printf(sb, "%s\n", osname); return (0); } /* * Filler function for proc/sys/kernel/version */ static int linprocfs_doosbuild(PFS_FILL_ARGS) { linprocfs_osbuild(td, sb); sbuf_cat(sb, "\n"); return (0); } /* * Filler function for proc/sys/kernel/msgmni */ static int linprocfs_domsgmni(PFS_FILL_ARGS) { sbuf_printf(sb, "%d\n", msginfo.msgmni); return (0); } /* * Filler function for proc/sys/kernel/pid_max */ static int linprocfs_dopid_max(PFS_FILL_ARGS) { sbuf_printf(sb, "%i\n", PID_MAX); return (0); } /* * Filler function for proc/sys/kernel/sem */ static int linprocfs_dosem(PFS_FILL_ARGS) { sbuf_printf(sb, "%d %d %d %d\n", seminfo.semmsl, seminfo.semmns, seminfo.semopm, seminfo.semmni); return (0); } /* * Filler function for proc/scsi/device_info */ static int linprocfs_doscsidevinfo(PFS_FILL_ARGS) { return (0); } /* * Filler function for proc/scsi/scsi */ static int linprocfs_doscsiscsi(PFS_FILL_ARGS) { return (0); } /* * Filler function for proc/devices */ static int linprocfs_dodevices(PFS_FILL_ARGS) { char *char_devices; sbuf_printf(sb, "Character devices:\n"); char_devices = linux_get_char_devices(); sbuf_printf(sb, "%s", char_devices); linux_free_get_char_devices(char_devices); sbuf_printf(sb, "\nBlock devices:\n"); return (0); } /* * Filler function for proc/cmdline */ static int linprocfs_docmdline(PFS_FILL_ARGS) { sbuf_printf(sb, "BOOT_IMAGE=%s", kernelname); sbuf_printf(sb, " ro root=302\n"); return (0); } /* * Filler function for proc/filesystems */ static int linprocfs_dofilesystems(PFS_FILL_ARGS) { struct vfsconf *vfsp; mtx_lock(&Giant); TAILQ_FOREACH(vfsp, &vfsconf, vfc_list) { if (vfsp->vfc_flags & VFCF_SYNTHETIC) sbuf_printf(sb, "nodev"); sbuf_printf(sb, "\t%s\n", vfsp->vfc_name); } mtx_unlock(&Giant); return(0); } #if 0 /* * Filler function for proc/modules */ static int linprocfs_domodules(PFS_FILL_ARGS) { struct linker_file *lf; TAILQ_FOREACH(lf, &linker_files, link) { sbuf_printf(sb, "%-20s%8lu%4d\n", lf->filename, (unsigned long)lf->size, lf->refs); } return (0); } #endif /* - * Filler function for proc/pid/fd - */ -static int -linprocfs_dofdescfs(PFS_FILL_ARGS) -{ - - if (p == curproc) - sbuf_printf(sb, "/dev/fd"); - else - sbuf_printf(sb, "unknown"); - return (0); -} - - -/* * Filler function for proc/sys/kernel/random/uuid */ static int linprocfs_douuid(PFS_FILL_ARGS) { struct uuid uuid; kern_uuidgen(&uuid, 1); sbuf_printf_uuid(sb, &uuid); sbuf_printf(sb, "\n"); return(0); } /* * Filler function for proc/pid/auxv */ static int linprocfs_doauxv(PFS_FILL_ARGS) { struct sbuf *asb; off_t buflen, resid; int error; /* * Mimic linux behavior and pass only processes with usermode * address space as valid. Return zero silently otherwize. */ if (p->p_vmspace == &vmspace0) return (0); if (uio->uio_resid == 0) return (0); if (uio->uio_offset < 0 || uio->uio_resid < 0) return (EINVAL); asb = sbuf_new_auto(); if (asb == NULL) return (ENOMEM); error = proc_getauxv(td, p, asb); if (error == 0) error = sbuf_finish(asb); resid = sbuf_len(asb) - uio->uio_offset; if (resid > uio->uio_resid) buflen = uio->uio_resid; else buflen = resid; if (buflen > IOSIZE_MAX) return (EINVAL); if (buflen > MAXPHYS) buflen = MAXPHYS; if (resid <= 0) return (0); if (error == 0) error = uiomove(sbuf_data(asb) + uio->uio_offset, buflen, uio); sbuf_delete(asb); return (error); } /* * Constructor */ static int linprocfs_init(PFS_INIT_ARGS) { struct pfs_node *root; struct pfs_node *dir; root = pi->pi_root; /* /proc/... */ pfs_create_file(root, "cmdline", &linprocfs_docmdline, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "cpuinfo", &linprocfs_docpuinfo, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "devices", &linprocfs_dodevices, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "filesystems", &linprocfs_dofilesystems, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "loadavg", &linprocfs_doloadavg, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "meminfo", &linprocfs_domeminfo, NULL, NULL, NULL, PFS_RD); #if 0 pfs_create_file(root, "modules", &linprocfs_domodules, NULL, NULL, NULL, PFS_RD); #endif pfs_create_file(root, "mounts", &linprocfs_domtab, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "mtab", &linprocfs_domtab, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "partitions", &linprocfs_dopartitions, NULL, NULL, NULL, PFS_RD); pfs_create_link(root, "self", &procfs_docurproc, NULL, NULL, NULL, 0); pfs_create_file(root, "stat", &linprocfs_dostat, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "swaps", &linprocfs_doswaps, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "uptime", &linprocfs_douptime, NULL, NULL, NULL, PFS_RD); pfs_create_file(root, "version", &linprocfs_doversion, NULL, NULL, NULL, PFS_RD); /* /proc/net/... */ dir = pfs_create_dir(root, "net", NULL, NULL, NULL, 0); pfs_create_file(dir, "dev", &linprocfs_donetdev, NULL, NULL, NULL, PFS_RD); /* /proc//... */ dir = pfs_create_dir(root, "pid", NULL, NULL, NULL, PFS_PROCDEP); pfs_create_file(dir, "cmdline", &linprocfs_doproccmdline, NULL, NULL, NULL, PFS_RD); pfs_create_link(dir, "cwd", &linprocfs_doproccwd, NULL, NULL, NULL, 0); pfs_create_file(dir, "environ", &linprocfs_doprocenviron, NULL, &procfs_candebug, NULL, PFS_RD); pfs_create_link(dir, "exe", &procfs_doprocfile, NULL, &procfs_notsystem, NULL, 0); pfs_create_file(dir, "maps", &linprocfs_doprocmaps, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "mem", &procfs_doprocmem, &procfs_attr, &procfs_candebug, NULL, PFS_RDWR|PFS_RAW); pfs_create_link(dir, "root", &linprocfs_doprocroot, NULL, NULL, NULL, 0); pfs_create_file(dir, "stat", &linprocfs_doprocstat, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "statm", &linprocfs_doprocstatm, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "status", &linprocfs_doprocstatus, NULL, NULL, NULL, PFS_RD); - pfs_create_link(dir, "fd", &linprocfs_dofdescfs, - NULL, NULL, NULL, 0); pfs_create_file(dir, "auxv", &linprocfs_doauxv, NULL, &procfs_candebug, NULL, PFS_RD|PFS_RAWRD); + dir = pfs_create_dir(dir, "fd", NULL, NULL, NULL, 0); + pfs_create_link(dir, "---", &procfs_dofdlink, + NULL, &procfs_candebug, NULL, PFS_PROCFDDEP); /* /proc/scsi/... */ dir = pfs_create_dir(root, "scsi", NULL, NULL, NULL, 0); pfs_create_file(dir, "device_info", &linprocfs_doscsidevinfo, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "scsi", &linprocfs_doscsiscsi, NULL, NULL, NULL, PFS_RD); /* /proc/sys/... */ dir = pfs_create_dir(root, "sys", NULL, NULL, NULL, 0); /* /proc/sys/kernel/... */ dir = pfs_create_dir(dir, "kernel", NULL, NULL, NULL, 0); pfs_create_file(dir, "osrelease", &linprocfs_doosrelease, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "ostype", &linprocfs_doostype, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "version", &linprocfs_doosbuild, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "msgmni", &linprocfs_domsgmni, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "pid_max", &linprocfs_dopid_max, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "sem", &linprocfs_dosem, NULL, NULL, NULL, PFS_RD); /* /proc/sys/kernel/random/... */ dir = pfs_create_dir(dir, "random", NULL, NULL, NULL, 0); pfs_create_file(dir, "uuid", &linprocfs_douuid, NULL, NULL, NULL, PFS_RD); return (0); } /* * Destructor */ static int linprocfs_uninit(PFS_INIT_ARGS) { /* nothing to do, pseudofs will GC */ return (0); } PSEUDOFS(linprocfs, 1, 0); #if defined(__amd64__) MODULE_DEPEND(linprocfs, linux_common, 1, 1, 1); #else MODULE_DEPEND(linprocfs, linux, 1, 1, 1); #endif MODULE_DEPEND(linprocfs, procfs, 1, 1, 1); MODULE_DEPEND(linprocfs, sysvmsg, 1, 1, 1); MODULE_DEPEND(linprocfs, sysvsem, 1, 1, 1); Index: user/dchagin/lemul/sys/fs/procfs/procfs.c =================================================================== --- user/dchagin/lemul/sys/fs/procfs/procfs.c (revision 279334) +++ user/dchagin/lemul/sys/fs/procfs/procfs.c (revision 279335) @@ -1,212 +1,216 @@ /*- * Copyright (c) 2001 Dag-Erling Smørgrav * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)procfs_vfsops.c 8.7 (Berkeley) 5/10/95 * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Filler function for proc/pid/self */ int procfs_doprocfile(PFS_FILL_ARGS) { char *fullpath; char *freepath; struct vnode *textvp; int error; freepath = NULL; PROC_LOCK(p); textvp = p->p_textvp; vhold(textvp); PROC_UNLOCK(p); error = vn_fullpath(td, textvp, &fullpath, &freepath); vdrop(textvp); if (error == 0) sbuf_printf(sb, "%s", fullpath); if (freepath != NULL) free(freepath, M_TEMP); return (error); } /* * Filler function for proc/curproc */ int procfs_docurproc(PFS_FILL_ARGS) { sbuf_printf(sb, "%ld", (long)td->td_proc->p_pid); return (0); } /* * Adjust mode for some nodes that need it */ int procfs_attr(PFS_ATTR_ARGS) { /* XXX inefficient, split into separate functions */ if (strcmp(pn->pn_name, "ctl") == 0 || strcmp(pn->pn_name, "note") == 0 || strcmp(pn->pn_name, "notepg") == 0) vap->va_mode = 0200; else if (strcmp(pn->pn_name, "mem") == 0 || strcmp(pn->pn_name, "regs") == 0 || strcmp(pn->pn_name, "dbregs") == 0 || strcmp(pn->pn_name, "fpregs") == 0 || strcmp(pn->pn_name, "osrel") == 0) vap->va_mode = 0600; if (p != NULL) { PROC_LOCK_ASSERT(p, MA_OWNED); if ((p->p_flag & P_SUGID) && pn->pn_type != pfstype_procdir) vap->va_mode = 0; } return (0); } /* * Visibility: some files only exist for non-system processes * Non-static because linprocfs uses it. */ int procfs_notsystem(PFS_VIS_ARGS) { PROC_LOCK_ASSERT(p, MA_OWNED); return ((p->p_flag & P_SYSTEM) == 0); } /* * Visibility: some files are only visible to process that can debug * the target process. */ int procfs_candebug(PFS_VIS_ARGS) { PROC_LOCK_ASSERT(p, MA_OWNED); return ((p->p_flag & P_SYSTEM) == 0 && p_candebug(td, p) == 0); } /* * Constructor */ static int procfs_init(PFS_INIT_ARGS) { struct pfs_node *root; struct pfs_node *dir; struct pfs_node *node; root = pi->pi_root; pfs_create_link(root, "curproc", procfs_docurproc, NULL, NULL, NULL, 0); dir = pfs_create_dir(root, "pid", procfs_attr, NULL, NULL, PFS_PROCDEP); pfs_create_file(dir, "cmdline", procfs_doproccmdline, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "ctl", procfs_doprocctl, procfs_attr, NULL, NULL, PFS_WR); pfs_create_file(dir, "dbregs", procfs_doprocdbregs, procfs_attr, procfs_candebug, NULL, PFS_RDWR|PFS_RAW); pfs_create_file(dir, "etype", procfs_doproctype, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "fpregs", procfs_doprocfpregs, procfs_attr, procfs_candebug, NULL, PFS_RDWR|PFS_RAW); pfs_create_file(dir, "map", procfs_doprocmap, NULL, procfs_notsystem, NULL, PFS_RD); node = pfs_create_file(dir, "mem", procfs_doprocmem, procfs_attr, procfs_candebug, NULL, PFS_RDWR|PFS_RAW); node->pn_ioctl = procfs_ioctl; node->pn_close = procfs_close; pfs_create_file(dir, "note", procfs_doprocnote, procfs_attr, procfs_candebug, NULL, PFS_WR); pfs_create_file(dir, "notepg", procfs_doprocnote, procfs_attr, procfs_candebug, NULL, PFS_WR); pfs_create_file(dir, "regs", procfs_doprocregs, procfs_attr, procfs_candebug, NULL, PFS_RDWR|PFS_RAW); pfs_create_file(dir, "rlimit", procfs_doprocrlimit, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "status", procfs_doprocstatus, NULL, NULL, NULL, PFS_RD); pfs_create_file(dir, "osrel", procfs_doosrel, procfs_attr, procfs_candebug, NULL, PFS_RDWR); pfs_create_link(dir, "file", procfs_doprocfile, NULL, procfs_notsystem, NULL, 0); + dir = pfs_create_dir(dir, "fd", NULL, NULL, NULL, 0); + pfs_create_link(dir, "---", &procfs_dofdlink, + NULL, procfs_candebug, NULL, PFS_PROCFDDEP); + return (0); } /* * Destructor */ static int procfs_uninit(PFS_INIT_ARGS) { /* nothing to do, pseudofs will GC */ return (0); } PSEUDOFS(procfs, 1, PR_ALLOW_MOUNT_PROCFS); Index: user/dchagin/lemul/sys/fs/procfs/procfs.h =================================================================== --- user/dchagin/lemul/sys/fs/procfs/procfs.h (revision 279334) +++ user/dchagin/lemul/sys/fs/procfs/procfs.h (revision 279335) @@ -1,65 +1,66 @@ /*- * Copyright (c) 1993 Jan-Simon Pendry * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)procfs.h 8.9 (Berkeley) 5/14/95 * * From: * $FreeBSD$ */ #ifdef _KERNEL int procfs_docurproc(PFS_FILL_ARGS); +int procfs_dofdlink(PFS_FILL_ARGS); int procfs_doosrel(PFS_FILL_ARGS); int procfs_doproccmdline(PFS_FILL_ARGS); int procfs_doprocctl(PFS_FILL_ARGS); int procfs_doprocdbregs(PFS_FILL_ARGS); int procfs_doprocfile(PFS_FILL_ARGS); int procfs_doprocfpregs(PFS_FILL_ARGS); int procfs_doprocmap(PFS_FILL_ARGS); int procfs_doprocmem(PFS_FILL_ARGS); int procfs_doprocnote(PFS_FILL_ARGS); int procfs_doprocregs(PFS_FILL_ARGS); int procfs_doprocrlimit(PFS_FILL_ARGS); int procfs_doprocstatus(PFS_FILL_ARGS); int procfs_doproctype(PFS_FILL_ARGS); int procfs_ioctl(PFS_IOCTL_ARGS); int procfs_close(PFS_CLOSE_ARGS); /* Attributes */ int procfs_attr(PFS_ATTR_ARGS); /* Visibility */ int procfs_notsystem(PFS_VIS_ARGS); int procfs_candebug(PFS_VIS_ARGS); #endif /* _KERNEL */ Index: user/dchagin/lemul/sys/fs/procfs/procfs_fdlink.c =================================================================== --- user/dchagin/lemul/sys/fs/procfs/procfs_fdlink.c (nonexistent) +++ user/dchagin/lemul/sys/fs/procfs/procfs_fdlink.c (revision 279335) @@ -0,0 +1,87 @@ +/*- + * Copyright (c) 2015 Dmitry Chagin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +int +procfs_dofdlink(PFS_FILL_ARGS) +{ + char *fullpath, *freepath, *endfileno; + struct filedesc *fdp; + struct vnode *vp; + struct file *fp; + int fileno, error; + + if (vnode_name == NULL) + return (ENOENT); + + fileno = (int)strtol(vnode_name, &endfileno, 10); + if (fileno == 0 && (vnode_namelen > 1 || + (vnode_namelen == 1 && vnode_name[0] != '0'))) + return (ENOENT); + if (vnode_namelen != endfileno - vnode_name) + return (ENOENT); + + fdp = fdhold(p); + if (fdp == NULL) + return (ENOENT); + + error = fget_unlocked(fdp, fileno, NULL, &fp, NULL); + if (error != 0) + goto out; + + freepath = NULL; + fullpath = "-"; + vp = fp->f_vnode; + if (vp != NULL) { + vref(vp); + error = vn_fullpath(td, vp, &fullpath, &freepath); + vrele(vp); + } + if (error == 0) + error = sbuf_printf(sb, "%s", fullpath); + if (freepath != NULL) + free(freepath, M_TEMP); + fdrop(fp, td); + + out: + fddrop(fdp); + return (error); +} Property changes on: user/dchagin/lemul/sys/fs/procfs/procfs_fdlink.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: user/dchagin/lemul/sys/fs/pseudofs/pseudofs.c =================================================================== --- user/dchagin/lemul/sys/fs/pseudofs/pseudofs.c (revision 279334) +++ user/dchagin/lemul/sys/fs/pseudofs/pseudofs.c (revision 279335) @@ -1,458 +1,459 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_pseudofs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_PFSNODES, "pfs_nodes", "pseudofs nodes"); SYSCTL_NODE(_vfs, OID_AUTO, pfs, CTLFLAG_RW, 0, "pseudofs"); #ifdef PSEUDOFS_TRACE int pfs_trace; SYSCTL_INT(_vfs_pfs, OID_AUTO, trace, CTLFLAG_RW, &pfs_trace, 0, "enable tracing of pseudofs vnode operations"); #endif #if PFS_FSNAMELEN != MFSNAMELEN #error "PFS_FSNAMELEN is not equal to MFSNAMELEN" #endif /* * Allocate and initialize a node */ static struct pfs_node * pfs_alloc_node(struct pfs_info *pi, const char *name, pfs_type_t type) { struct pfs_node *pn; KASSERT(strlen(name) < PFS_NAMELEN, ("%s(): node name is too long", __func__)); pn = malloc(sizeof *pn, M_PFSNODES, M_WAITOK|M_ZERO); mtx_init(&pn->pn_mutex, "pfs_node", NULL, MTX_DEF | MTX_DUPOK); strlcpy(pn->pn_name, name, sizeof pn->pn_name); pn->pn_type = type; pn->pn_info = pi; return (pn); } /* * Add a node to a directory */ static void pfs_add_node(struct pfs_node *parent, struct pfs_node *pn) { #ifdef INVARIANTS struct pfs_node *iter; #endif KASSERT(parent != NULL, ("%s(): parent is NULL", __func__)); KASSERT(pn->pn_parent == NULL, ("%s(): node already has a parent", __func__)); KASSERT(parent->pn_info != NULL, ("%s(): parent has no pn_info", __func__)); KASSERT(parent->pn_type == pfstype_dir || parent->pn_type == pfstype_procdir || parent->pn_type == pfstype_root, ("%s(): parent is not a directory", __func__)); #ifdef INVARIANTS /* XXX no locking! */ if (pn->pn_type == pfstype_procdir) for (iter = parent; iter != NULL; iter = iter->pn_parent) KASSERT(iter->pn_type != pfstype_procdir, ("%s(): nested process directories", __func__)); for (iter = parent->pn_nodes; iter != NULL; iter = iter->pn_next) { KASSERT(strcmp(pn->pn_name, iter->pn_name) != 0, ("%s(): homonymous siblings", __func__)); if (pn->pn_type == pfstype_procdir) KASSERT(iter->pn_type != pfstype_procdir, ("%s(): sibling process directories", __func__)); } #endif pn->pn_parent = parent; pfs_fileno_alloc(pn); pfs_lock(parent); pn->pn_next = parent->pn_nodes; if ((parent->pn_flags & PFS_PROCDEP) != 0) pn->pn_flags |= PFS_PROCDEP; parent->pn_nodes = pn; pfs_unlock(parent); } /* * Detach a node from its aprent */ static void pfs_detach_node(struct pfs_node *pn) { struct pfs_node *parent = pn->pn_parent; struct pfs_node **iter; KASSERT(parent != NULL, ("%s(): node has no parent", __func__)); KASSERT(parent->pn_info == pn->pn_info, ("%s(): parent has different pn_info", __func__)); pfs_lock(parent); iter = &parent->pn_nodes; while (*iter != NULL) { if (*iter == pn) { *iter = pn->pn_next; break; } iter = &(*iter)->pn_next; } pn->pn_parent = NULL; pfs_unlock(parent); } /* * Add . and .. to a directory */ static void pfs_fixup_dir(struct pfs_node *parent) { struct pfs_node *pn; pn = pfs_alloc_node(parent->pn_info, ".", pfstype_this); pfs_add_node(parent, pn); pn = pfs_alloc_node(parent->pn_info, "..", pfstype_parent); pfs_add_node(parent, pn); } /* * Create a directory */ struct pfs_node * pfs_create_dir(struct pfs_node *parent, const char *name, pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, int flags) { struct pfs_node *pn; pn = pfs_alloc_node(parent->pn_info, name, (flags & PFS_PROCDEP) ? pfstype_procdir : pfstype_dir); pn->pn_attr = attr; pn->pn_vis = vis; pn->pn_destroy = destroy; pn->pn_flags = flags; pfs_add_node(parent, pn); pfs_fixup_dir(pn); return (pn); } /* * Create a file */ struct pfs_node * pfs_create_file(struct pfs_node *parent, const char *name, pfs_fill_t fill, pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, int flags) { struct pfs_node *pn; pn = pfs_alloc_node(parent->pn_info, name, pfstype_file); pn->pn_fill = fill; pn->pn_attr = attr; pn->pn_vis = vis; pn->pn_destroy = destroy; pn->pn_flags = flags; pfs_add_node(parent, pn); return (pn); } /* * Create a symlink */ struct pfs_node * pfs_create_link(struct pfs_node *parent, const char *name, pfs_fill_t fill, pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, int flags) { struct pfs_node *pn; - pn = pfs_alloc_node(parent->pn_info, name, pfstype_symlink); + pn = pfs_alloc_node(parent->pn_info, name, + (flags & PFS_PROCFDDEP) ? pfstype_fdlink : pfstype_symlink); pn->pn_fill = fill; pn->pn_attr = attr; pn->pn_vis = vis; pn->pn_destroy = destroy; pn->pn_flags = flags; pfs_add_node(parent, pn); return (pn); } /* * Locate a node by name */ struct pfs_node * pfs_find_node(struct pfs_node *parent, const char *name) { struct pfs_node *pn; pfs_lock(parent); for (pn = parent->pn_nodes; pn != NULL; pn = pn->pn_next) if (strcmp(pn->pn_name, name) == 0) break; pfs_unlock(parent); return (pn); } /* * Destroy a node and all its descendants. If the node to be destroyed * has a parent, the parent's mutex must be held. */ int pfs_destroy(struct pfs_node *pn) { struct pfs_node *iter; KASSERT(pn != NULL, ("%s(): node is NULL", __func__)); KASSERT(pn->pn_info != NULL, ("%s(): node has no pn_info", __func__)); if (pn->pn_parent) pfs_detach_node(pn); /* destroy children */ if (pn->pn_type == pfstype_dir || pn->pn_type == pfstype_procdir || pn->pn_type == pfstype_root) { pfs_lock(pn); while (pn->pn_nodes != NULL) { iter = pn->pn_nodes; pn->pn_nodes = iter->pn_next; iter->pn_parent = NULL; pfs_unlock(pn); pfs_destroy(iter); pfs_lock(pn); } pfs_unlock(pn); } /* revoke vnodes and fileno */ pfs_purge(pn); /* callback to free any private resources */ if (pn->pn_destroy != NULL) pn_destroy(pn); /* destroy the node */ pfs_fileno_free(pn); mtx_destroy(&pn->pn_mutex); free(pn, M_PFSNODES); return (0); } /* * Mount a pseudofs instance */ int pfs_mount(struct pfs_info *pi, struct mount *mp) { struct statfs *sbp; if (mp->mnt_flag & MNT_UPDATE) return (EOPNOTSUPP); MNT_ILOCK(mp); mp->mnt_flag |= MNT_LOCAL; MNT_IUNLOCK(mp); mp->mnt_data = pi; vfs_getnewfsid(mp); sbp = &mp->mnt_stat; vfs_mountedfrom(mp, pi->pi_name); sbp->f_bsize = PAGE_SIZE; sbp->f_iosize = PAGE_SIZE; sbp->f_blocks = 1; sbp->f_bfree = 0; sbp->f_bavail = 0; sbp->f_files = 1; sbp->f_ffree = 0; return (0); } /* * Compatibility shim for old mount(2) system call */ int pfs_cmount(struct mntarg *ma, void *data, uint64_t flags) { int error; error = kernel_mount(ma, flags); return (error); } /* * Unmount a pseudofs instance */ int pfs_unmount(struct mount *mp, int mntflags) { int error; error = vflush(mp, 0, (mntflags & MNT_FORCE) ? FORCECLOSE : 0, curthread); return (error); } /* * Return a root vnode */ int pfs_root(struct mount *mp, int flags, struct vnode **vpp) { struct pfs_info *pi; pi = (struct pfs_info *)mp->mnt_data; - return (pfs_vncache_alloc(mp, vpp, pi->pi_root, NO_PID)); + return (pfs_vncache_alloc(mp, vpp, pi->pi_root, NO_PID, NULL, 0)); } /* * Return filesystem stats */ int pfs_statfs(struct mount *mp, struct statfs *sbp) { /* no-op: always called with mp->mnt_stat */ return (0); } /* * Initialize a pseudofs instance */ int pfs_init(struct pfs_info *pi, struct vfsconf *vfc) { struct pfs_node *root; int error; mtx_assert(&Giant, MA_OWNED); pfs_fileno_init(pi); /* set up the root diretory */ root = pfs_alloc_node(pi, "/", pfstype_root); pi->pi_root = root; pfs_fileno_alloc(root); pfs_fixup_dir(root); /* construct file hierarchy */ error = (pi->pi_init)(pi, vfc); if (error) { pfs_destroy(root); pi->pi_root = NULL; return (error); } if (bootverbose) printf("%s registered\n", pi->pi_name); return (0); } /* * Destroy a pseudofs instance */ int pfs_uninit(struct pfs_info *pi, struct vfsconf *vfc) { int error; mtx_assert(&Giant, MA_OWNED); pfs_destroy(pi->pi_root); pi->pi_root = NULL; pfs_fileno_uninit(pi); if (bootverbose) printf("%s unregistered\n", pi->pi_name); error = (pi->pi_uninit)(pi, vfc); return (error); } /* * Handle load / unload events */ static int pfs_modevent(module_t mod, int evt, void *arg) { switch (evt) { case MOD_LOAD: pfs_vncache_load(); break; case MOD_UNLOAD: case MOD_SHUTDOWN: pfs_vncache_unload(); break; default: return EOPNOTSUPP; break; } return 0; } /* * Module declaration */ static moduledata_t pseudofs_data = { "pseudofs", pfs_modevent, NULL }; DECLARE_MODULE(pseudofs, pseudofs_data, SI_SUB_EXEC, SI_ORDER_FIRST); MODULE_VERSION(pseudofs, 1); Index: user/dchagin/lemul/sys/fs/pseudofs/pseudofs.h =================================================================== --- user/dchagin/lemul/sys/fs/pseudofs/pseudofs.h (revision 279334) +++ user/dchagin/lemul/sys/fs/pseudofs/pseudofs.h (revision 279335) @@ -1,314 +1,317 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _PSEUDOFS_H_INCLUDED #define _PSEUDOFS_H_INCLUDED #include /* * Opaque structures */ struct mntarg; struct mount; struct nameidata; struct proc; struct sbuf; struct statfs; struct thread; struct uio; struct vfsconf; struct vnode; /* * Limits and constants */ #define PFS_NAMELEN 24 #define PFS_FSNAMELEN 16 /* equal to MFSNAMELEN */ #define PFS_DELEN (8 + PFS_NAMELEN) typedef enum { pfstype_none = 0, pfstype_root, pfstype_dir, pfstype_this, pfstype_parent, pfstype_file, pfstype_symlink, - pfstype_procdir + pfstype_procdir, + pfstype_fdlink } pfs_type_t; /* * Flags */ #define PFS_RD 0x0001 /* readable */ #define PFS_WR 0x0002 /* writeable */ #define PFS_RDWR (PFS_RD|PFS_WR) #define PFS_RAWRD 0x0004 /* raw reader */ #define PFS_RAWWR 0x0008 /* raw writer */ #define PFS_RAW (PFS_RAWRD|PFS_RAWWR) #define PFS_PROCDEP 0x0010 /* process-dependent */ +#define PFS_PROCFDDEP 0x0020 /* process-filedesc-dependent */ /* * Data structures */ struct pfs_info; struct pfs_node; struct pfs_bitmap; /* * Init / uninit callback */ #define PFS_INIT_ARGS \ struct pfs_info *pi, struct vfsconf *vfc #define PFS_INIT_ARGNAMES \ pi, vfc #define PFS_INIT_PROTO(name) \ int name(PFS_INIT_ARGS); typedef int (*pfs_init_t)(PFS_INIT_ARGS); /* * Filler callback * Called with proc held but unlocked */ #define PFS_FILL_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ - struct sbuf *sb, struct uio *uio + struct sbuf *sb, struct uio *uio, char *vnode_name, \ + int vnode_namelen #define PFS_FILL_ARGNAMES \ - td, p, pn, sb, uio + td, p, pn, sb, uio, vnode_name, vnode_namelen #define PFS_FILL_PROTO(name) \ int name(PFS_FILL_ARGS); typedef int (*pfs_fill_t)(PFS_FILL_ARGS); /* * Attribute callback * Called with proc locked */ struct vattr; #define PFS_ATTR_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ struct vattr *vap #define PFS_ATTR_ARGNAMES \ td, p, pn, vap #define PFS_ATTR_PROTO(name) \ int name(PFS_ATTR_ARGS); typedef int (*pfs_attr_t)(PFS_ATTR_ARGS); struct pfs_bitmap; /* opaque */ /* * Visibility callback * Called with proc locked */ #define PFS_VIS_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn #define PFS_VIS_ARGNAMES \ td, p, pn #define PFS_VIS_PROTO(name) \ int name(PFS_VIS_ARGS); typedef int (*pfs_vis_t)(PFS_VIS_ARGS); /* * Ioctl callback * Called with proc locked */ #define PFS_IOCTL_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ unsigned long cmd, void *data #define PFS_IOCTL_ARGNAMES \ td, p, pn, cmd, data #define PFS_IOCTL_PROTO(name) \ int name(PFS_IOCTL_ARGS); typedef int (*pfs_ioctl_t)(PFS_IOCTL_ARGS); /* * Getextattr callback * Called with proc locked */ #define PFS_GETEXTATTR_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ int attrnamespace, const char *name, struct uio *uio, \ size_t *size, struct ucred *cred #define PFS_GETEXTATTR_ARGNAMES \ td, p, pn, attrnamespace, name, uio, size, cred #define PFS_GETEXTATTR_PROTO(name) \ int name(PFS_GETEXTATTR_ARGS); struct ucred; typedef int (*pfs_getextattr_t)(PFS_GETEXTATTR_ARGS); /* * Last-close callback * Called with proc locked */ #define PFS_CLOSE_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn #define PFS_CLOSE_ARGNAMES \ td, p, pn #define PFS_CLOSE_PROTO(name) \ int name(PFS_CLOSE_ARGS); typedef int (*pfs_close_t)(PFS_CLOSE_ARGS); /* * Destroy callback */ #define PFS_DESTROY_ARGS \ struct pfs_node *pn #define PFS_DESTROY_ARGNAMES \ pn #define PFS_DESTROY_PROTO(name) \ int name(PFS_DESTROY_ARGS); typedef int (*pfs_destroy_t)(PFS_DESTROY_ARGS); /* * pfs_info: describes a pseudofs instance * * The pi_mutex is only used to avoid using the global subr_unit lock for * unrhdr. The rest of struct pfs_info is only modified while Giant is * held (during vfs_init() and vfs_uninit()). */ struct pfs_info { char pi_name[PFS_FSNAMELEN]; pfs_init_t pi_init; pfs_init_t pi_uninit; /* members below this line are initialized at run time*/ struct pfs_node *pi_root; struct mtx pi_mutex; struct unrhdr *pi_unrhdr; }; /* * pfs_node: describes a node (file or directory) within a pseudofs * * - Fields marked (o) are protected by the node's own mutex. * - Fields marked (p) are protected by the node's parent's mutex. * - Remaining fields are not protected by any lock and are assumed to be * immutable once the node has been created. * * To prevent deadlocks, if a node's mutex is to be held at the same time * as its parent's (e.g. when adding or removing nodes to a directory), * the parent's mutex must always be acquired first. Unfortunately, this * is not enforcable by WITNESS. */ struct pfs_node { char pn_name[PFS_NAMELEN]; pfs_type_t pn_type; int pn_flags; struct mtx pn_mutex; void *pn_data; /* (o) */ pfs_fill_t pn_fill; pfs_ioctl_t pn_ioctl; pfs_close_t pn_close; pfs_attr_t pn_attr; pfs_vis_t pn_vis; pfs_getextattr_t pn_getextattr; pfs_destroy_t pn_destroy; struct pfs_info *pn_info; u_int32_t pn_fileno; /* (o) */ struct pfs_node *pn_parent; /* (o) */ struct pfs_node *pn_nodes; /* (o) */ struct pfs_node *pn_next; /* (p) */ }; /* * VFS interface */ int pfs_mount (struct pfs_info *pi, struct mount *mp); int pfs_cmount (struct mntarg *ma, void *data, uint64_t flags); int pfs_unmount (struct mount *mp, int mntflags); int pfs_root (struct mount *mp, int flags, struct vnode **vpp); int pfs_statfs (struct mount *mp, struct statfs *sbp); int pfs_init (struct pfs_info *pi, struct vfsconf *vfc); int pfs_uninit (struct pfs_info *pi, struct vfsconf *vfc); /* * Directory structure construction and manipulation */ struct pfs_node *pfs_create_dir (struct pfs_node *parent, const char *name, pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, int flags); struct pfs_node *pfs_create_file(struct pfs_node *parent, const char *name, pfs_fill_t fill, pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, int flags); struct pfs_node *pfs_create_link(struct pfs_node *parent, const char *name, pfs_fill_t fill, pfs_attr_t attr, pfs_vis_t vis, pfs_destroy_t destroy, int flags); struct pfs_node *pfs_find_node (struct pfs_node *parent, const char *name); void pfs_purge (struct pfs_node *pn); int pfs_destroy (struct pfs_node *pn); /* * Now for some initialization magic... */ #define PSEUDOFS(name, version, jflag) \ \ static struct pfs_info name##_info = { \ #name, \ name##_init, \ name##_uninit, \ }; \ \ static int \ _##name##_mount(struct mount *mp) { \ if (jflag && !prison_allow(curthread->td_ucred, jflag)) \ return (EPERM); \ return pfs_mount(&name##_info, mp); \ } \ \ static int \ _##name##_init(struct vfsconf *vfc) { \ return pfs_init(&name##_info, vfc); \ } \ \ static int \ _##name##_uninit(struct vfsconf *vfc) { \ return pfs_uninit(&name##_info, vfc); \ } \ \ static struct vfsops name##_vfsops = { \ .vfs_cmount = pfs_cmount, \ .vfs_init = _##name##_init, \ .vfs_mount = _##name##_mount, \ .vfs_root = pfs_root, \ .vfs_statfs = pfs_statfs, \ .vfs_uninit = _##name##_uninit, \ .vfs_unmount = pfs_unmount, \ }; \ VFS_SET(name##_vfsops, name, VFCF_SYNTHETIC | (jflag ? VFCF_JAIL : 0)); \ MODULE_VERSION(name, version); \ MODULE_DEPEND(name, pseudofs, 1, 1, 1); #endif Index: user/dchagin/lemul/sys/fs/pseudofs/pseudofs_fileno.c =================================================================== --- user/dchagin/lemul/sys/fs/pseudofs/pseudofs_fileno.c (revision 279334) +++ user/dchagin/lemul/sys/fs/pseudofs/pseudofs_fileno.c (revision 279335) @@ -1,159 +1,161 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_pseudofs.h" #include #include #include #include #include #include #include #include #include #include #include #include /* * Initialize fileno bitmap */ void pfs_fileno_init(struct pfs_info *pi) { mtx_assert(&Giant, MA_OWNED); mtx_init(&pi->pi_mutex, "pfs_fileno", NULL, MTX_DEF); pi->pi_unrhdr = new_unrhdr(3, INT_MAX / NO_PID, &pi->pi_mutex); } /* * Tear down fileno bitmap */ void pfs_fileno_uninit(struct pfs_info *pi) { mtx_assert(&Giant, MA_OWNED); delete_unrhdr(pi->pi_unrhdr); pi->pi_unrhdr = NULL; mtx_destroy(&pi->pi_mutex); } /* * Allocate a file number */ void pfs_fileno_alloc(struct pfs_node *pn) { if (pn->pn_parent) PFS_TRACE(("%s/%s", pn->pn_parent->pn_name, pn->pn_name)); else PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); switch (pn->pn_type) { case pfstype_root: /* root must always be 2 */ pn->pn_fileno = 2; break; case pfstype_dir: case pfstype_file: case pfstype_symlink: case pfstype_procdir: + case pfstype_fdlink: pn->pn_fileno = alloc_unr(pn->pn_info->pi_unrhdr); break; case pfstype_this: KASSERT(pn->pn_parent != NULL, ("%s(): pfstype_this node has no parent", __func__)); pn->pn_fileno = pn->pn_parent->pn_fileno; break; case pfstype_parent: KASSERT(pn->pn_parent != NULL, ("%s(): pfstype_parent node has no parent", __func__)); if (pn->pn_parent->pn_type == pfstype_root) { pn->pn_fileno = pn->pn_parent->pn_fileno; break; } KASSERT(pn->pn_parent->pn_parent != NULL, ("%s(): pfstype_parent node has no grandparent", __func__)); pn->pn_fileno = pn->pn_parent->pn_parent->pn_fileno; break; case pfstype_none: KASSERT(0, ("%s(): pfstype_none node", __func__)); break; } #if 0 printf("%s(): %s: ", __func__, pn->pn_info->pi_name); if (pn->pn_parent) { if (pn->pn_parent->pn_parent) { printf("%s/", pn->pn_parent->pn_parent->pn_name); } printf("%s/", pn->pn_parent->pn_name); } printf("%s -> %d\n", pn->pn_name, pn->pn_fileno); #endif } /* * Release a file number */ void pfs_fileno_free(struct pfs_node *pn) { pfs_assert_not_owned(pn); switch (pn->pn_type) { case pfstype_root: /* not allocated from unrhdr */ return; case pfstype_dir: case pfstype_file: case pfstype_symlink: case pfstype_procdir: + case pfstype_fdlink: free_unr(pn->pn_info->pi_unrhdr, pn->pn_fileno); break; case pfstype_this: case pfstype_parent: /* ignore these, as they don't "own" their file number */ break; case pfstype_none: KASSERT(0, ("pfs_fileno_free() called for pfstype_none node")); break; } } Index: user/dchagin/lemul/sys/fs/pseudofs/pseudofs_internal.h =================================================================== --- user/dchagin/lemul/sys/fs/pseudofs/pseudofs_internal.h (revision 279334) +++ user/dchagin/lemul/sys/fs/pseudofs/pseudofs_internal.h (revision 279335) @@ -1,211 +1,214 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _PSEUDOFS_INTERNAL_H_INCLUDED #define _PSEUDOFS_INTERNAL_H_INCLUDED /* * Sysctl subtree */ SYSCTL_DECL(_vfs_pfs); /* * Vnode data */ struct pfs_vdata { struct pfs_node *pvd_pn; pid_t pvd_pid; struct vnode *pvd_vnode; + char *pvd_vnode_name; + int pvd_vnode_namelen; struct pfs_vdata*pvd_prev, *pvd_next; int pvd_dead:1; }; /* * Vnode cache */ void pfs_vncache_load (void); void pfs_vncache_unload (void); int pfs_vncache_alloc (struct mount *, struct vnode **, - struct pfs_node *, pid_t pid); + struct pfs_node *, pid_t pid, + char *name, int namelen); int pfs_vncache_free (struct vnode *); /* * File number bitmap */ void pfs_fileno_init (struct pfs_info *); void pfs_fileno_uninit (struct pfs_info *); void pfs_fileno_alloc (struct pfs_node *); void pfs_fileno_free (struct pfs_node *); /* * Debugging */ #ifdef PSEUDOFS_TRACE extern int pfs_trace; #define PFS_TRACE(foo) \ do { \ if (pfs_trace) { \ printf("%s(): line %d: ", __func__, __LINE__); \ printf foo ; \ printf("\n"); \ } \ } while (0) #define PFS_RETURN(err) \ do { \ if (pfs_trace) { \ printf("%s(): line %d: returning %d\n", \ __func__, __LINE__, err); \ } \ return (err); \ } while (0) #else #define PFS_TRACE(foo) \ do { /* nothing */ } while (0) #define PFS_RETURN(err) \ return (err) #endif /* * Inline helpers for locking */ static inline void pfs_lock(struct pfs_node *pn) { mtx_lock(&pn->pn_mutex); } static inline void pfs_unlock(struct pfs_node *pn) { mtx_unlock(&pn->pn_mutex); } static inline void pfs_assert_owned(struct pfs_node *pn) { mtx_assert(&pn->pn_mutex, MA_OWNED); } static inline void pfs_assert_not_owned(struct pfs_node *pn) { mtx_assert(&pn->pn_mutex, MA_NOTOWNED); } static inline int pn_fill(PFS_FILL_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_fill != NULL, ("%s(): no callback", __func__)); if (p != NULL) { PROC_LOCK_ASSERT(p, MA_NOTOWNED); PROC_ASSERT_HELD(p); } pfs_assert_not_owned(pn); return ((pn->pn_fill)(PFS_FILL_ARGNAMES)); } static inline int pn_attr(PFS_ATTR_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_attr != NULL, ("%s(): no callback", __func__)); if (p != NULL) PROC_LOCK_ASSERT(p, MA_OWNED); pfs_assert_not_owned(pn); return ((pn->pn_attr)(PFS_ATTR_ARGNAMES)); } static inline int pn_vis(PFS_VIS_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_vis != NULL, ("%s(): no callback", __func__)); KASSERT(p != NULL, ("%s(): no process", __func__)); PROC_LOCK_ASSERT(p, MA_OWNED); pfs_assert_not_owned(pn); return ((pn->pn_vis)(PFS_VIS_ARGNAMES)); } static inline int pn_ioctl(PFS_IOCTL_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_ioctl != NULL, ("%s(): no callback", __func__)); if (p != NULL) PROC_LOCK_ASSERT(p, MA_OWNED); pfs_assert_not_owned(pn); return ((pn->pn_ioctl)(PFS_IOCTL_ARGNAMES)); } static inline int pn_getextattr(PFS_GETEXTATTR_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_getextattr != NULL, ("%s(): no callback", __func__)); if (p != NULL) PROC_LOCK_ASSERT(p, MA_OWNED); pfs_assert_not_owned(pn); return ((pn->pn_getextattr)(PFS_GETEXTATTR_ARGNAMES)); } static inline int pn_close(PFS_CLOSE_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_close != NULL, ("%s(): no callback", __func__)); if (p != NULL) PROC_LOCK_ASSERT(p, MA_OWNED); pfs_assert_not_owned(pn); return ((pn->pn_close)(PFS_CLOSE_ARGNAMES)); } static inline int pn_destroy(PFS_DESTROY_ARGS) { PFS_TRACE(("%s", pn->pn_name)); KASSERT(pn->pn_destroy != NULL, ("%s(): no callback", __func__)); pfs_assert_not_owned(pn); return ((pn->pn_destroy)(PFS_DESTROY_ARGNAMES)); } #endif Index: user/dchagin/lemul/sys/fs/pseudofs/pseudofs_vncache.c =================================================================== --- user/dchagin/lemul/sys/fs/pseudofs/pseudofs_vncache.c (revision 279334) +++ user/dchagin/lemul/sys/fs/pseudofs/pseudofs_vncache.c (revision 279335) @@ -1,328 +1,351 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_pseudofs.h" #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); static struct mtx pfs_vncache_mutex; static struct pfs_vdata *pfs_vncache; static eventhandler_tag pfs_exit_tag; static void pfs_exit(void *arg, struct proc *p); static SYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW, 0, "pseudofs vnode cache"); static int pfs_vncache_entries; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, entries, CTLFLAG_RD, &pfs_vncache_entries, 0, "number of entries in the vnode cache"); static int pfs_vncache_maxentries; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, maxentries, CTLFLAG_RD, &pfs_vncache_maxentries, 0, "highest number of entries in the vnode cache"); static int pfs_vncache_hits; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, &pfs_vncache_hits, 0, "number of cache hits since initialization"); static int pfs_vncache_misses; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, &pfs_vncache_misses, 0, "number of cache misses since initialization"); extern struct vop_vector pfs_vnodeops; /* XXX -> .h file */ /* * Initialize vnode cache */ void pfs_vncache_load(void) { mtx_assert(&Giant, MA_OWNED); mtx_init(&pfs_vncache_mutex, "pfs_vncache", NULL, MTX_DEF); pfs_exit_tag = EVENTHANDLER_REGISTER(process_exit, pfs_exit, NULL, EVENTHANDLER_PRI_ANY); } /* * Tear down vnode cache */ void pfs_vncache_unload(void) { mtx_assert(&Giant, MA_OWNED); EVENTHANDLER_DEREGISTER(process_exit, pfs_exit_tag); KASSERT(pfs_vncache_entries == 0, ("%d vncache entries remaining", pfs_vncache_entries)); mtx_destroy(&pfs_vncache_mutex); } /* * Allocate a vnode */ int -pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, - struct pfs_node *pn, pid_t pid) +pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, struct pfs_node *pn, + pid_t pid, char *name, int namelen) { struct pfs_vdata *pvd, *pvd2; struct vnode *vp; int error; + PFS_TRACE(("%s", (name != NULL? name : pn->pn_name))); + /* * See if the vnode is in the cache. * XXX linear search is not very efficient. */ retry: mtx_lock(&pfs_vncache_mutex); for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { if (pvd->pvd_pn == pn && pvd->pvd_pid == pid && pvd->pvd_vnode->v_mount == mp) { + if ((pn->pn_flags & PFS_PROCFDDEP) && + (namelen != pvd->pvd_vnode_namelen || + bcmp(name, pvd->pvd_vnode_name, namelen))) + continue; vp = pvd->pvd_vnode; VI_LOCK(vp); mtx_unlock(&pfs_vncache_mutex); if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, curthread) == 0) { ++pfs_vncache_hits; *vpp = vp; /* * Some callers cache_enter(vp) later, so * we have to make sure it's not in the * VFS cache so it doesn't get entered * twice. A better solution would be to * make pfs_vncache_alloc() responsible * for entering the vnode in the VFS * cache. */ cache_purge(vp); return (0); } goto retry; } } mtx_unlock(&pfs_vncache_mutex); /* nope, get a new one */ - pvd = malloc(sizeof *pvd, M_PFSVNCACHE, M_WAITOK); + pvd = malloc(sizeof *pvd, M_PFSVNCACHE, M_WAITOK|M_ZERO); pvd->pvd_next = pvd->pvd_prev = NULL; error = getnewvnode("pseudofs", mp, &pfs_vnodeops, vpp); if (error) { free(pvd, M_PFSVNCACHE); return (error); } pvd->pvd_pn = pn; pvd->pvd_pid = pid; + if (name != NULL && namelen > 0) { + pvd->pvd_vnode_name = malloc(namelen + 1, M_PFSVNCACHE, + M_WAITOK); + strlcpy(pvd->pvd_vnode_name, name, namelen + 1); + pvd->pvd_vnode_namelen = namelen; + } (*vpp)->v_data = pvd; switch (pn->pn_type) { case pfstype_root: (*vpp)->v_vflag = VV_ROOT; #if 0 printf("root vnode allocated\n"); #endif /* fall through */ case pfstype_dir: case pfstype_this: case pfstype_parent: case pfstype_procdir: (*vpp)->v_type = VDIR; break; case pfstype_file: (*vpp)->v_type = VREG; break; case pfstype_symlink: + case pfstype_fdlink: (*vpp)->v_type = VLNK; break; case pfstype_none: KASSERT(0, ("pfs_vncache_alloc called for null node\n")); default: panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); } /* * Propagate flag through to vnode so users know it can change * if the process changes (i.e. execve) */ if ((pn->pn_flags & PFS_PROCDEP) != 0) (*vpp)->v_vflag |= VV_PROCDEP; pvd->pvd_vnode = *vpp; vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY); VN_LOCK_AREC(*vpp); error = insmntque(*vpp, mp); if (error != 0) { free(pvd, M_PFSVNCACHE); *vpp = NULLVP; return (error); } retry2: mtx_lock(&pfs_vncache_mutex); /* * Other thread may race with us, creating the entry we are * going to insert into the cache. Recheck after * pfs_vncache_mutex is reacquired. */ for (pvd2 = pfs_vncache; pvd2; pvd2 = pvd2->pvd_next) { if (pvd2->pvd_pn == pn && pvd2->pvd_pid == pid && pvd2->pvd_vnode->v_mount == mp) { + if ((pn->pn_flags & PFS_PROCFDDEP) && + (namelen != pvd2->pvd_vnode_namelen || + bcmp(name, pvd2->pvd_vnode_name, namelen))) + continue; vp = pvd2->pvd_vnode; VI_LOCK(vp); mtx_unlock(&pfs_vncache_mutex); if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, curthread) == 0) { ++pfs_vncache_hits; vgone(*vpp); vput(*vpp); *vpp = vp; cache_purge(vp); return (0); } goto retry2; } } ++pfs_vncache_misses; if (++pfs_vncache_entries > pfs_vncache_maxentries) pfs_vncache_maxentries = pfs_vncache_entries; pvd->pvd_prev = NULL; pvd->pvd_next = pfs_vncache; if (pvd->pvd_next) pvd->pvd_next->pvd_prev = pvd; pfs_vncache = pvd; mtx_unlock(&pfs_vncache_mutex); return (0); } /* * Free a vnode */ int pfs_vncache_free(struct vnode *vp) { struct pfs_vdata *pvd; mtx_lock(&pfs_vncache_mutex); pvd = (struct pfs_vdata *)vp->v_data; + KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); + PFS_TRACE(("%s", (pvd->pvd_vnode_name != NULL ? + pvd->pvd_vnode_name : pvd->pvd_pn->pn_name))); + if (pvd->pvd_next) pvd->pvd_next->pvd_prev = pvd->pvd_prev; if (pvd->pvd_prev) { pvd->pvd_prev->pvd_next = pvd->pvd_next; --pfs_vncache_entries; } else if (pfs_vncache == pvd) { pfs_vncache = pvd->pvd_next; --pfs_vncache_entries; } mtx_unlock(&pfs_vncache_mutex); + if (pvd->pvd_vnode_name != NULL) + free(pvd->pvd_vnode_name, M_PFSVNCACHE); free(pvd, M_PFSVNCACHE); vp->v_data = NULL; return (0); } /* * Purge the cache of dead entries * * This is extremely inefficient due to the fact that vgone() not only * indirectly modifies the vnode cache, but may also sleep. We can * neither hold pfs_vncache_mutex across a vgone() call, nor make any * assumptions about the state of the cache after vgone() returns. In * consequence, we must start over after every vgone() call, and keep * trying until we manage to traverse the entire cache. * * The only way to improve this situation is to change the data structure * used to implement the cache. */ static void pfs_purge_locked(struct pfs_node *pn) { struct pfs_vdata *pvd; struct vnode *vnp; mtx_assert(&pfs_vncache_mutex, MA_OWNED); pvd = pfs_vncache; while (pvd != NULL) { if (pvd->pvd_dead || (pn != NULL && pvd->pvd_pn == pn)) { vnp = pvd->pvd_vnode; vhold(vnp); mtx_unlock(&pfs_vncache_mutex); VOP_LOCK(vnp, LK_EXCLUSIVE); vgone(vnp); VOP_UNLOCK(vnp, 0); mtx_lock(&pfs_vncache_mutex); vdrop(vnp); pvd = pfs_vncache; } else { pvd = pvd->pvd_next; } } } void pfs_purge(struct pfs_node *pn) { mtx_lock(&pfs_vncache_mutex); pfs_purge_locked(pn); mtx_unlock(&pfs_vncache_mutex); } /* * Free all vnodes associated with a defunct process */ static void pfs_exit(void *arg, struct proc *p) { struct pfs_vdata *pvd; int dead; if (pfs_vncache == NULL) return; mtx_lock(&pfs_vncache_mutex); for (pvd = pfs_vncache, dead = 0; pvd != NULL; pvd = pvd->pvd_next) if (pvd->pvd_pid == p->p_pid) dead = pvd->pvd_dead = 1; if (dead) pfs_purge_locked(NULL); mtx_unlock(&pfs_vncache_mutex); } Index: user/dchagin/lemul/sys/fs/pseudofs/pseudofs_vnops.c =================================================================== --- user/dchagin/lemul/sys/fs/pseudofs/pseudofs_vnops.c (revision 279334) +++ user/dchagin/lemul/sys/fs/pseudofs/pseudofs_vnops.c (revision 279335) @@ -1,1057 +1,1160 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_pseudofs.h" #include #include #include +#include #include #include #include +#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #define KASSERT_PN_IS_DIR(pn) \ KASSERT((pn)->pn_type == pfstype_root || \ (pn)->pn_type == pfstype_dir || \ (pn)->pn_type == pfstype_procdir, \ ("%s(): VDIR vnode refers to non-directory pfs_node", __func__)) #define KASSERT_PN_IS_FILE(pn) \ KASSERT((pn)->pn_type == pfstype_file, \ ("%s(): VREG vnode refers to non-file pfs_node", __func__)) #define KASSERT_PN_IS_LINK(pn) \ - KASSERT((pn)->pn_type == pfstype_symlink, \ + KASSERT((pn)->pn_type == pfstype_symlink || \ + (pn)->pn_type == pfstype_fdlink, \ ("%s(): VLNK vnode refers to non-link pfs_node", __func__)) /* * Returns the fileno, adjusted for target pid */ static uint32_t pn_fileno(struct pfs_node *pn, pid_t pid) { KASSERT(pn->pn_fileno > 0, ("%s(): no fileno allocated", __func__)); if (pid != NO_PID) return (pn->pn_fileno * NO_PID + pid); return (pn->pn_fileno); } /* * Returns non-zero if given file is visible to given thread. */ static int pfs_visible_proc(struct thread *td, struct pfs_node *pn, struct proc *proc) { int visible; if (proc == NULL) return (0); PROC_LOCK_ASSERT(proc, MA_OWNED); visible = ((proc->p_flag & P_WEXIT) == 0); if (visible) visible = (p_cansee(td, proc) == 0); if (visible && pn->pn_vis != NULL) visible = pn_vis(td, proc, pn); if (!visible) return (0); return (1); } static int pfs_visible(struct thread *td, struct pfs_node *pn, pid_t pid, struct proc **p) { struct proc *proc; PFS_TRACE(("%s (pid: %d, req: %d)", pn->pn_name, pid, td->td_proc->p_pid)); if (p) *p = NULL; if (pid == NO_PID) PFS_RETURN (1); if ((proc = pfind(pid)) == NULL) PFS_RETURN (0); if (pfs_visible_proc(td, pn, proc)) { if (p) *p = proc; else PROC_UNLOCK(proc); PFS_RETURN (1); } PROC_UNLOCK(proc); PFS_RETURN (0); } /* * Verify permissions */ static int pfs_access(struct vop_access_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct vattr vattr; int error; - PFS_TRACE(("%s", pvd->pvd_pn->pn_name)); + PFS_TRACE(("%s", (pvd->pvd_vnode_name != NULL ? + pvd->pvd_vnode_name : pvd->pvd_pn->pn_name))); (void)pvd; error = VOP_GETATTR(vn, &vattr, va->a_cred); if (error) PFS_RETURN (error); error = vaccess(vn->v_type, vattr.va_mode, vattr.va_uid, vattr.va_gid, va->a_accmode, va->a_cred, NULL); PFS_RETURN (error); } /* * Close a file or directory */ static int pfs_close(struct vop_close_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct proc *proc; int error; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); /* * Do nothing unless this is the last close and the node has a * last-close handler. */ if (vrefcnt(vn) > 1 || pn->pn_close == NULL) PFS_RETURN (0); if (pvd->pvd_pid != NO_PID) { proc = pfind(pvd->pvd_pid); } else { proc = NULL; } error = pn_close(va->a_td, proc, pn); if (proc != NULL) PROC_UNLOCK(proc); PFS_RETURN (error); } /* * Get file attributes */ static int pfs_getattr(struct vop_getattr_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct vattr *vap = va->a_vap; struct proc *proc; int error = 0; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc)) PFS_RETURN (ENOENT); vap->va_type = vn->v_type; vap->va_fileid = pn_fileno(pn, pvd->pvd_pid); vap->va_flags = 0; vap->va_blocksize = PAGE_SIZE; vap->va_bytes = vap->va_size = 0; vap->va_filerev = 0; vap->va_fsid = vn->v_mount->mnt_stat.f_fsid.val[0]; vap->va_nlink = 1; nanotime(&vap->va_ctime); vap->va_atime = vap->va_mtime = vap->va_ctime; switch (pn->pn_type) { case pfstype_procdir: case pfstype_root: case pfstype_dir: #if 0 pfs_lock(pn); /* compute link count */ pfs_unlock(pn); #endif vap->va_mode = 0555; break; case pfstype_file: case pfstype_symlink: + case pfstype_fdlink: vap->va_mode = 0444; break; default: printf("shouldn't be here!\n"); vap->va_mode = 0; break; } if (proc != NULL) { vap->va_uid = proc->p_ucred->cr_ruid; vap->va_gid = proc->p_ucred->cr_rgid; } else { vap->va_uid = 0; vap->va_gid = 0; } if (pn->pn_attr != NULL) error = pn_attr(curthread, proc, pn, vap); if(proc != NULL) PROC_UNLOCK(proc); PFS_RETURN (error); } /* * Perform an ioctl */ static int pfs_ioctl(struct vop_ioctl_args *va) { struct vnode *vn; struct pfs_vdata *pvd; struct pfs_node *pn; struct proc *proc; int error; vn = va->a_vp; vn_lock(vn, LK_SHARED | LK_RETRY); if (vn->v_iflag & VI_DOOMED) { VOP_UNLOCK(vn, 0); return (EBADF); } pvd = vn->v_data; pn = pvd->pvd_pn; PFS_TRACE(("%s: %lx", pn->pn_name, va->a_command)); pfs_assert_not_owned(pn); if (vn->v_type != VREG) { VOP_UNLOCK(vn, 0); PFS_RETURN (EINVAL); } KASSERT_PN_IS_FILE(pn); if (pn->pn_ioctl == NULL) { VOP_UNLOCK(vn, 0); PFS_RETURN (ENOTTY); } /* * This is necessary because process' privileges may * have changed since the open() call. */ if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc)) { VOP_UNLOCK(vn, 0); PFS_RETURN (EIO); } error = pn_ioctl(curthread, proc, pn, va->a_command, va->a_data); if (proc != NULL) PROC_UNLOCK(proc); VOP_UNLOCK(vn, 0); PFS_RETURN (error); } /* * Perform getextattr */ static int pfs_getextattr(struct vop_getextattr_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct proc *proc; int error; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); /* * This is necessary because either process' privileges may * have changed since the open() call. */ if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc)) PFS_RETURN (EIO); if (pn->pn_getextattr == NULL) error = EOPNOTSUPP; else error = pn_getextattr(curthread, proc, pn, va->a_attrnamespace, va->a_name, va->a_uio, va->a_size, va->a_cred); if (proc != NULL) PROC_UNLOCK(proc); PFS_RETURN (error); } /* * Convert a vnode to its component name */ static int pfs_vptocnp(struct vop_vptocnp_args *ap) { struct vnode *vp = ap->a_vp; struct vnode **dvp = ap->a_vpp; struct pfs_vdata *pvd = vp->v_data; struct pfs_node *pd = pvd->pvd_pn; struct pfs_node *pn; struct mount *mp; char *buf = ap->a_buf; int *buflen = ap->a_buflen; char pidbuf[PFS_NAMELEN]; pid_t pid = pvd->pvd_pid; int len, i, error, locked; i = *buflen; error = 0; pfs_lock(pd); if (vp->v_type == VDIR && pd->pn_type == pfstype_root) { *dvp = vp; vhold(*dvp); pfs_unlock(pd); PFS_RETURN (0); } else if (vp->v_type == VDIR && pd->pn_type == pfstype_procdir) { len = snprintf(pidbuf, sizeof(pidbuf), "%d", pid); i -= len; if (i < 0) { error = ENOMEM; goto failed; } bcopy(pidbuf, buf + i, len); } else { len = strlen(pd->pn_name); i -= len; if (i < 0) { error = ENOMEM; goto failed; } bcopy(pd->pn_name, buf + i, len); } pn = pd->pn_parent; pfs_unlock(pd); mp = vp->v_mount; error = vfs_busy(mp, 0); if (error) return (error); /* * vp is held by caller. */ locked = VOP_ISLOCKED(vp); VOP_UNLOCK(vp, 0); - error = pfs_vncache_alloc(mp, dvp, pn, pid); + error = pfs_vncache_alloc(mp, dvp, pn, pid, NULL, 0); if (error) { vn_lock(vp, locked | LK_RETRY); vfs_unbusy(mp); PFS_RETURN(error); } *buflen = i; VOP_UNLOCK(*dvp, 0); vn_lock(vp, locked | LK_RETRY); vfs_unbusy(mp); PFS_RETURN (0); failed: pfs_unlock(pd); PFS_RETURN(error); } /* * Look up a file or directory */ static int pfs_lookup(struct vop_cachedlookup_args *va) { struct vnode *vn = va->a_dvp; struct vnode **vpp = va->a_vpp; struct componentname *cnp = va->a_cnp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pd = pvd->pvd_pn; struct pfs_node *pn, *pdn = NULL; + struct thread *td = curthread; struct mount *mp; + cap_rights_t rights; pid_t pid = pvd->pvd_pid; - char *pname; - int error, i, namelen, visible; + char *pname, *pnameend; + int error, i, namelen, visible, fileno; + struct filedesc *fdp; + struct file *fp; + struct proc *p; PFS_TRACE(("%.*s", (int)cnp->cn_namelen, cnp->cn_nameptr)); pfs_assert_not_owned(pd); if (vn->v_type != VDIR) PFS_RETURN (ENOTDIR); KASSERT_PN_IS_DIR(pd); error = VOP_ACCESS(vn, VEXEC, cnp->cn_cred, cnp->cn_thread); if (error) PFS_RETURN (error); /* * Don't support DELETE or RENAME. CREATE is supported so * that O_CREAT will work, but the lookup will still fail if * the file does not exist. */ if ((cnp->cn_flags & ISLASTCN) && (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) PFS_RETURN (EOPNOTSUPP); /* shortcut: check if the name is too long */ if (cnp->cn_namelen >= PFS_NAMELEN) PFS_RETURN (ENOENT); /* check that parent directory is visible... */ if (!pfs_visible(curthread, pd, pvd->pvd_pid, NULL)) PFS_RETURN (ENOENT); /* self */ namelen = cnp->cn_namelen; pname = cnp->cn_nameptr; if (namelen == 1 && pname[0] == '.') { pn = pd; *vpp = vn; VREF(vn); PFS_RETURN (0); } mp = vn->v_mount; /* parent */ if (cnp->cn_flags & ISDOTDOT) { if (pd->pn_type == pfstype_root) PFS_RETURN (EIO); error = vfs_busy(mp, MBF_NOWAIT); if (error != 0) { vfs_ref(mp); VOP_UNLOCK(vn, 0); error = vfs_busy(mp, 0); vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); vfs_rel(mp); if (error != 0) PFS_RETURN(ENOENT); if (vn->v_iflag & VI_DOOMED) { vfs_unbusy(mp); PFS_RETURN(ENOENT); } } VOP_UNLOCK(vn, 0); KASSERT(pd->pn_parent != NULL, ("%s(): non-root directory has no parent", __func__)); /* * This one is tricky. Descendents of procdir nodes * inherit their parent's process affinity, but * there's no easy reverse mapping. For simplicity, * we assume that if this node is a procdir, its * parent isn't (which is correct as long as * descendents of procdir nodes are never procdir * nodes themselves) */ if (pd->pn_type == pfstype_procdir) pid = NO_PID; pfs_lock(pd); pn = pd->pn_parent; pfs_unlock(pd); goto got_pnode; } pfs_lock(pd); /* named node */ for (pn = pd->pn_nodes; pn != NULL; pn = pn->pn_next) - if (pn->pn_type == pfstype_procdir) + if (pn->pn_type == pfstype_procdir || + pn->pn_type == pfstype_fdlink) pdn = pn; else if (pn->pn_name[namelen] == '\0' && bcmp(pname, pn->pn_name, namelen) == 0) { pfs_unlock(pd); goto got_pnode; } /* process dependent node */ - if ((pn = pdn) != NULL) { + if ((pn = pdn) != NULL && pn->pn_type == pfstype_procdir) { pid = 0; for (pid = 0, i = 0; i < namelen && isdigit(pname[i]); ++i) if ((pid = pid * 10 + pname[i] - '0') > PID_MAX) break; if (i == cnp->cn_namelen) { pfs_unlock(pd); goto got_pnode; } } - pfs_unlock(pd); + /* process filedesc dependent node */ + if ((pn = pdn) != NULL && pn->pn_type == pfstype_fdlink) { + pfs_unlock(pd); + fileno = (int)strtol(pname, &pnameend, 10); + if ((fileno == 0 && (namelen > 1 || + (namelen == 1 && pname[0] != '0'))) || + (namelen != pnameend - pname)) { + goto bad; + } + if ((p = pfind(pid)) == NULL) + goto bad; + fdp = fdhold(p); + if (fdp == NULL) { + PROC_UNLOCK(p); + goto bad; + } + error = fget_unlocked(fdp, fileno, + cap_rights_init(&rights, CAP_READ), &fp, NULL); + if (error == 0) { + fdrop(fp, td); + fddrop(fdp); + PROC_UNLOCK(p); + goto got_pnode; + } + fddrop(fdp); + PROC_UNLOCK(p); + goto bad; + } + + pfs_unlock(pd); + bad: PFS_RETURN (ENOENT); got_pnode: pfs_assert_not_owned(pd); pfs_assert_not_owned(pn); visible = pfs_visible(curthread, pn, pid, NULL); if (!visible) { error = ENOENT; goto failed; } - error = pfs_vncache_alloc(mp, vpp, pn, pid); + error = pfs_vncache_alloc(mp, vpp, pn, pid, pname, namelen); if (error) goto failed; if (cnp->cn_flags & ISDOTDOT) { vfs_unbusy(mp); vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); if (vn->v_iflag & VI_DOOMED) { vput(*vpp); *vpp = NULL; PFS_RETURN(ENOENT); } } if (cnp->cn_flags & MAKEENTRY && !(vn->v_iflag & VI_DOOMED)) cache_enter(vn, *vpp, cnp); PFS_RETURN (0); failed: if (cnp->cn_flags & ISDOTDOT) { vfs_unbusy(mp); vn_lock(vn, LK_EXCLUSIVE | LK_RETRY); *vpp = NULL; } PFS_RETURN(error); } /* * Open a file or directory. */ static int pfs_open(struct vop_open_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; int mode = va->a_mode; PFS_TRACE(("%s (mode 0x%x)", pn->pn_name, mode)); pfs_assert_not_owned(pn); /* check if the requested mode is permitted */ if (((mode & FREAD) && !(mode & PFS_RD)) || ((mode & FWRITE) && !(mode & PFS_WR))) PFS_RETURN (EPERM); /* we don't support locking */ if ((mode & O_SHLOCK) || (mode & O_EXLOCK)) PFS_RETURN (EOPNOTSUPP); PFS_RETURN (0); } /* * Read from a file */ static int pfs_read(struct vop_read_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct uio *uio = va->a_uio; struct proc *proc; struct sbuf *sb = NULL; int error, locked; off_t buflen; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); if (vn->v_type != VREG) PFS_RETURN (EINVAL); KASSERT_PN_IS_FILE(pn); if (!(pn->pn_flags & PFS_RD)) PFS_RETURN (EBADF); if (pn->pn_fill == NULL) PFS_RETURN (EIO); /* * This is necessary because either process' privileges may * have changed since the open() call. */ if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc)) PFS_RETURN (EIO); if (proc != NULL) { _PHOLD(proc); PROC_UNLOCK(proc); } vhold(vn); locked = VOP_ISLOCKED(vn); VOP_UNLOCK(vn, 0); if (pn->pn_flags & PFS_RAWRD) { PFS_TRACE(("%zd resid", uio->uio_resid)); - error = pn_fill(curthread, proc, pn, NULL, uio); + error = pn_fill(curthread, proc, pn, NULL, uio, + pvd->pvd_vnode_name, pvd->pvd_vnode_namelen); PFS_TRACE(("%zd resid", uio->uio_resid)); goto ret; } if (uio->uio_resid < 0 || uio->uio_offset < 0 || uio->uio_resid > OFF_MAX - uio->uio_offset) { error = EINVAL; goto ret; } buflen = uio->uio_offset + uio->uio_resid; if (buflen > MAXPHYS) buflen = MAXPHYS; sb = sbuf_new(sb, NULL, buflen + 1, 0); if (sb == NULL) { error = EIO; goto ret; } - error = pn_fill(curthread, proc, pn, sb, uio); + error = pn_fill(curthread, proc, pn, sb, uio, + pvd->pvd_vnode_name, pvd->pvd_vnode_namelen); if (error) { sbuf_delete(sb); goto ret; } /* * XXX: If the buffer overflowed, sbuf_len() will not return * the data length. Then just use the full length because an * overflowed sbuf must be full. */ if (sbuf_finish(sb) == 0) buflen = sbuf_len(sb); error = uiomove_frombuf(sbuf_data(sb), buflen, uio); sbuf_delete(sb); ret: vn_lock(vn, locked | LK_RETRY); vdrop(vn); if (proc != NULL) PRELE(proc); PFS_RETURN (error); } /* * Iterate through directory entries */ static int pfs_iterate(struct thread *td, struct proc *proc, struct pfs_node *pd, - struct pfs_node **pn, struct proc **p) + struct pfs_node **pn, struct proc **p, struct filedesc **fdp, int *fileno) { - int visible; + struct file *fp; + cap_rights_t rights; + int error, visible; sx_assert(&allproc_lock, SX_SLOCKED); pfs_assert_owned(pd); again: if (*pn == NULL) { /* first node */ *pn = pd->pn_nodes; - } else if ((*pn)->pn_type != pfstype_procdir) { + } else if ((*pn)->pn_type != pfstype_procdir && + (*pn)->pn_type != pfstype_fdlink) { /* next node */ *pn = (*pn)->pn_next; } if (*pn != NULL && (*pn)->pn_type == pfstype_procdir) { /* next process */ if (*p == NULL) *p = LIST_FIRST(&allproc); else *p = LIST_NEXT(*p, p_list); /* out of processes: next node */ if (*p == NULL) *pn = (*pn)->pn_next; else PROC_LOCK(*p); } + if (*pn != NULL && (*pn)->pn_type == pfstype_fdlink) { + /* Next fileno */ + KASSERT(proc != NULL, + ("%s(): fdlink has no proc", __func__)); + if (*fdp == NULL) { + *fdp = fdhold(proc); + if (*fdp != NULL) + *fileno = -1; + } + while (*fdp != NULL) { + (*fileno) += 1; + if ((*fdp)->fd_nfiles == 0 || + *fileno > (*fdp)->fd_lastfile) { + fddrop(*fdp); + *fdp = NULL; + break; + } + error = fget_unlocked(*fdp, *fileno, + cap_rights_init(&rights, CAP_READ), &fp, NULL); + if (error == 0) { + fdrop(fp, td); + break; + } + } + /* Out of process files: next node */ + if (*fdp == NULL) + *pn = (*pn)->pn_next; + } if ((*pn) == NULL) return (-1); if (*p != NULL) { visible = pfs_visible_proc(td, *pn, *p); PROC_UNLOCK(*p); } else if (proc != NULL) { visible = pfs_visible_proc(td, *pn, proc); } else { visible = 1; } if (!visible) goto again; return (0); } /* Directory entry list */ struct pfsentry { STAILQ_ENTRY(pfsentry) link; struct dirent entry; }; STAILQ_HEAD(pfsdirentlist, pfsentry); /* * Return directory entries. */ static int pfs_readdir(struct vop_readdir_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pd = pvd->pvd_pn; pid_t pid = pvd->pvd_pid; struct proc *p, *proc; + struct filedesc *fdp; struct pfs_node *pn; struct uio *uio; struct pfsentry *pfsent, *pfsent2; struct pfsdirentlist lst; off_t offset; - int error, i, resid; + int error, i, resid, fileno; STAILQ_INIT(&lst); error = 0; KASSERT(pd->pn_info == vn->v_mount->mnt_data, ("%s(): pn_info does not match mountpoint", __func__)); PFS_TRACE(("%s pid %lu", pd->pn_name, (unsigned long)pid)); pfs_assert_not_owned(pd); if (vn->v_type != VDIR) PFS_RETURN (ENOTDIR); KASSERT_PN_IS_DIR(pd); uio = va->a_uio; /* only allow reading entire entries */ offset = uio->uio_offset; resid = uio->uio_resid; if (offset < 0 || offset % PFS_DELEN != 0 || (resid && resid < PFS_DELEN)) PFS_RETURN (EINVAL); if (resid == 0) PFS_RETURN (0); sx_slock(&allproc_lock); pfs_lock(pd); /* check if the directory is visible to the caller */ if (!pfs_visible(curthread, pd, pid, &proc)) { sx_sunlock(&allproc_lock); pfs_unlock(pd); PFS_RETURN (ENOENT); } KASSERT(pid == NO_PID || proc != NULL, ("%s(): no process for pid %lu", __func__, (unsigned long)pid)); + fdp = NULL; /* skip unwanted entries */ for (pn = NULL, p = NULL; offset > 0; offset -= PFS_DELEN) { - if (pfs_iterate(curthread, proc, pd, &pn, &p) == -1) { + if (pfs_iterate(curthread, + proc, pd, &pn, &p, &fdp, &fileno) == -1) { /* nothing left... */ if (proc != NULL) PROC_UNLOCK(proc); + if (fdp != NULL) + fddrop(fdp); pfs_unlock(pd); sx_sunlock(&allproc_lock); PFS_RETURN (0); } } /* fill in entries */ - while (pfs_iterate(curthread, proc, pd, &pn, &p) != -1 && + while (pfs_iterate(curthread, proc, pd, &pn, &p, &fdp, &fileno) != -1 && resid >= PFS_DELEN) { if ((pfsent = malloc(sizeof(struct pfsentry), M_IOV, M_NOWAIT | M_ZERO)) == NULL) { error = ENOMEM; break; } pfsent->entry.d_reclen = PFS_DELEN; pfsent->entry.d_fileno = pn_fileno(pn, pid); /* PFS_DELEN was picked to fit PFS_NAMLEN */ - for (i = 0; i < PFS_NAMELEN - 1 && pn->pn_name[i] != '\0'; ++i) - pfsent->entry.d_name[i] = pn->pn_name[i]; + + if (pn->pn_type != pfstype_procdir && + pn->pn_type != pfstype_fdlink) + for (i = 0; i < PFS_NAMELEN - 1 && + pn->pn_name[i] != '\0'; ++i) + pfsent->entry.d_name[i] = pn->pn_name[i]; pfsent->entry.d_name[i] = 0; pfsent->entry.d_namlen = i; switch (pn->pn_type) { case pfstype_procdir: KASSERT(p != NULL, ("reached procdir node with p == NULL")); pfsent->entry.d_namlen = snprintf(pfsent->entry.d_name, PFS_NAMELEN, "%d", p->p_pid); /* fall through */ case pfstype_root: case pfstype_dir: case pfstype_this: case pfstype_parent: pfsent->entry.d_type = DT_DIR; break; case pfstype_file: pfsent->entry.d_type = DT_REG; break; + case pfstype_fdlink: + pfsent->entry.d_namlen = snprintf(pfsent->entry.d_name, + PFS_NAMELEN, "%d", fileno); + /* FALLTHROUGH */ case pfstype_symlink: pfsent->entry.d_type = DT_LNK; break; default: panic("%s has unexpected node type: %d", pn->pn_name, pn->pn_type); } PFS_TRACE(("%s", pfsent->entry.d_name)); STAILQ_INSERT_TAIL(&lst, pfsent, link); offset += PFS_DELEN; resid -= PFS_DELEN; } if (proc != NULL) PROC_UNLOCK(proc); + if (fdp != NULL) + fddrop(fdp); pfs_unlock(pd); sx_sunlock(&allproc_lock); i = 0; STAILQ_FOREACH_SAFE(pfsent, &lst, link, pfsent2) { if (error == 0) error = uiomove(&pfsent->entry, PFS_DELEN, uio); free(pfsent, M_IOV); i++; } PFS_TRACE(("%d bytes", i * PFS_DELEN)); PFS_RETURN (error); } /* * Read a symbolic link */ static int pfs_readlink(struct vop_readlink_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct uio *uio = va->a_uio; struct proc *proc = NULL; char buf[PATH_MAX]; struct sbuf sb; int error, locked; - PFS_TRACE(("%s", pn->pn_name)); + PFS_TRACE(("%s", (pvd->pvd_vnode_name != NULL ? + pvd->pvd_vnode_name : pn->pn_name))); pfs_assert_not_owned(pn); if (vn->v_type != VLNK) PFS_RETURN (EINVAL); KASSERT_PN_IS_LINK(pn); if (pn->pn_fill == NULL) PFS_RETURN (EIO); if (pvd->pvd_pid != NO_PID) { if ((proc = pfind(pvd->pvd_pid)) == NULL) PFS_RETURN (EIO); if (proc->p_flag & P_WEXIT) { PROC_UNLOCK(proc); PFS_RETURN (EIO); } _PHOLD(proc); PROC_UNLOCK(proc); } vhold(vn); locked = VOP_ISLOCKED(vn); VOP_UNLOCK(vn, 0); /* sbuf_new() can't fail with a static buffer */ sbuf_new(&sb, buf, sizeof buf, 0); - error = pn_fill(curthread, proc, pn, &sb, NULL); + error = pn_fill(curthread, proc, pn, &sb, NULL, + pvd->pvd_vnode_name, pvd->pvd_vnode_namelen); if (proc != NULL) PRELE(proc); vn_lock(vn, locked | LK_RETRY); - vdrop(vn); + /* + * Vgone file descriptor dependent node right after use as + * is not trivial to control file operations from pseudofs + */ + if (pn->pn_flags & PFS_PROCFDDEP) + vgone(vn); + else + vdrop(vn); + if (error) { sbuf_delete(&sb); PFS_RETURN (error); } if (sbuf_finish(&sb) != 0) { sbuf_delete(&sb); PFS_RETURN (ENAMETOOLONG); } error = uiomove_frombuf(sbuf_data(&sb), sbuf_len(&sb), uio); sbuf_delete(&sb); PFS_RETURN (error); } /* * Reclaim a vnode */ static int pfs_reclaim(struct vop_reclaim_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; - PFS_TRACE(("%s", pn->pn_name)); + PFS_TRACE(("%s", (pvd->pvd_vnode_name != NULL ? + pvd->pvd_vnode_name : pn->pn_name))); pfs_assert_not_owned(pn); return (pfs_vncache_free(va->a_vp)); } /* * Set attributes */ static int pfs_setattr(struct vop_setattr_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); PFS_RETURN (EOPNOTSUPP); } /* * Write to a file */ static int pfs_write(struct vop_write_args *va) { struct vnode *vn = va->a_vp; struct pfs_vdata *pvd = vn->v_data; struct pfs_node *pn = pvd->pvd_pn; struct uio *uio = va->a_uio; struct proc *proc; struct sbuf sb; int error; PFS_TRACE(("%s", pn->pn_name)); pfs_assert_not_owned(pn); if (vn->v_type != VREG) PFS_RETURN (EINVAL); KASSERT_PN_IS_FILE(pn); if (!(pn->pn_flags & PFS_WR)) PFS_RETURN (EBADF); if (pn->pn_fill == NULL) PFS_RETURN (EIO); /* * This is necessary because either process' privileges may * have changed since the open() call. */ if (!pfs_visible(curthread, pn, pvd->pvd_pid, &proc)) PFS_RETURN (EIO); if (proc != NULL) { _PHOLD(proc); PROC_UNLOCK(proc); } if (pn->pn_flags & PFS_RAWWR) { - error = pn_fill(curthread, proc, pn, NULL, uio); + error = pn_fill(curthread, proc, pn, NULL, uio, + pvd->pvd_vnode_name, pvd->pvd_vnode_namelen); if (proc != NULL) PRELE(proc); PFS_RETURN (error); } sbuf_uionew(&sb, uio, &error); if (error) { if (proc != NULL) PRELE(proc); PFS_RETURN (error); } - error = pn_fill(curthread, proc, pn, &sb, uio); + error = pn_fill(curthread, proc, pn, &sb, uio, + pvd->pvd_vnode_name, pvd->pvd_vnode_namelen); sbuf_delete(&sb); if (proc != NULL) PRELE(proc); PFS_RETURN (error); } /* * Vnode operations */ struct vop_vector pfs_vnodeops = { .vop_default = &default_vnodeops, .vop_access = pfs_access, .vop_cachedlookup = pfs_lookup, .vop_close = pfs_close, .vop_create = VOP_EOPNOTSUPP, .vop_getattr = pfs_getattr, .vop_getextattr = pfs_getextattr, .vop_ioctl = pfs_ioctl, .vop_link = VOP_EOPNOTSUPP, .vop_lookup = vfs_cache_lookup, .vop_mkdir = VOP_EOPNOTSUPP, .vop_mknod = VOP_EOPNOTSUPP, .vop_open = pfs_open, .vop_read = pfs_read, .vop_readdir = pfs_readdir, .vop_readlink = pfs_readlink, .vop_reclaim = pfs_reclaim, .vop_remove = VOP_EOPNOTSUPP, .vop_rename = VOP_EOPNOTSUPP, .vop_rmdir = VOP_EOPNOTSUPP, .vop_setattr = pfs_setattr, .vop_symlink = VOP_EOPNOTSUPP, .vop_vptocnp = pfs_vptocnp, .vop_write = pfs_write, /* XXX I've probably forgotten a few that need VOP_EOPNOTSUPP */ }; Index: user/dchagin/lemul/sys/modules/procfs/Makefile =================================================================== --- user/dchagin/lemul/sys/modules/procfs/Makefile (revision 279334) +++ user/dchagin/lemul/sys/modules/procfs/Makefile (revision 279335) @@ -1,43 +1,45 @@ # $FreeBSD$ .PATH: ${.CURDIR}/../../fs/procfs KMOD= procfs SRCS= SRCS+= opt_compat.h SRCS+= vnode_if.h SRCS+= procfs_ctl.c SRCS+= procfs_dbregs.c +SRCS+= procfs_fdlink.c SRCS+= procfs_fpregs.c SRCS+= procfs_ioctl.c SRCS+= procfs_map.c SRCS+= procfs_mem.c SRCS+= procfs_note.c SRCS+= procfs_osrel.c SRCS+= procfs_regs.c SRCS+= procfs_rlimit.c SRCS+= procfs_status.c SRCS+= procfs_type.c SRCS+= procfs.c EXPORT_SYMS= EXPORT_SYMS+= procfs_attr EXPORT_SYMS+= procfs_candebug EXPORT_SYMS+= procfs_docurproc EXPORT_SYMS+= procfs_doprocfile EXPORT_SYMS+= procfs_doprocmem EXPORT_SYMS+= procfs_notsystem +EXPORT_SYMS+= procfs_dofdlink .if !defined(KERNBUILDDIR) opt_compat.h: echo "#define COMPAT_43 1" > ${.TARGET} echo "#define COMPAT_FREEBSD4 1" >> ${.TARGET} echo "#define COMPAT_FREEBSD5 1" >> ${.TARGET} echo "#define COMPAT_FREEBSD6 1" >> ${.TARGET} .if ${MACHINE_CPUARCH} == "amd64" echo "#define COMPAT_FREEBSD32 1" >> ${.TARGET} echo "#define COMPAT_LINUX32 1" >> ${.TARGET} .endif .endif .include