/* * dproc.c - DEC OSF/1, Digital UNIX, Tru64 UNIX process access functions for * lsof */ /* * Copyright 1994 Purdue Research Foundation, West Lafayette, Indiana * 47907. All rights reserved. * * Written by Victor A. Abell * * This software is not subject to any license of the American Telephone * and Telegraph Company or the Regents of the University of California. * * Permission is granted to anyone to use this software for any purpose on * any computer system, and to alter it and redistribute it freely, subject * to the following restrictions: * * 1. Neither the authors nor Purdue University are responsible for any * consequences of the use of this software. * * 2. The origin of this software must not be misrepresented, either by * explicit claim or by omission. Credit to the authors and Purdue * University must appear in documentation and sources. * * 3. Altered versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 4. This notice may not be removed or altered. */ #ifndef lint static char copyright[] = "@(#) Copyright 1994 Purdue Research Foundation.\nAll rights reserved.\n"; static char *rcsid = "$Id: dproc.c,v 1.23 2005/08/08 19:56:44 abe Exp $"; #endif #include "lsof.h" #if DUV>=50000 # if DUV>=50100 && defined(HASNCACHE) #include #include #define _KERNEL 1 #include #include #undef _KERNEL #include #include #include # endif /* DUV>=50100 && defined(HASNCACHE) */ _PROTOTYPE(static KA_T vpo2vp,(struct vm_ubc_object *vpo)); #endif /* DUV>=50000 */ _PROTOTYPE(static void enter_vn_text,(KA_T va, int *n)); _PROTOTYPE(static void get_kernel_access,(void)); #if DUV<30000 _PROTOTYPE(static void process_text,(KA_T tp, KA_T utp)); #else /* DUV>=30000 */ _PROTOTYPE(static void process_text,(KA_T tp)); #endif /* DUV<30000 */ _PROTOTYPE(static void read_proc,(void)); /* * Local defintions */ #define PAPSINCR 1024 /* Pa and Ps table increment */ #define PAPSINIT 512 /* Pa and Ps table initial size */ /* * Local static values */ #if DUV<30000 static KA_T Kp; /* kernel proc[] address */ #endif /* DUV<30000 */ static int Np = 0; /* number of processes */ static MALLOC_S Nv = 0; /* allocateed Vp[] entries */ #if DUV>=30000 static KA_T Pidtab; /* kernel pidtab[] address */ #endif /* DUV>=30000 */ static KA_T *Vp = NULL; /* vnode address cache */ /* * enter_vn_text() - enter a vnode text reference */ static void enter_vn_text(va, n) KA_T va; /* vnode address */ int *n; /* number of vnodes in vp[] */ { int i; /* * Ignore the request if the vnode has already been printed. */ for (i = 0; i < *n; i++) { if (va == Vp[i]) return; } /* * Print the vnode. */ alloc_lfile(" txt", -1); FILEPTR = (struct file *)NULL; process_node(va); if (Lf->sf) link_lfile(); if (i >= Nv) { /* * Allocate space for remembering the vnode. */ Nv += 10; if (!Vp) Vp=(KA_T *)malloc((MALLOC_S)(sizeof(KA_T) * 10)); else Vp=(KA_T *)realloc((MALLOC_P *)Vp,(MALLOC_S)(Nv*sizeof(KA_T))); if (!Vp) { (void) fprintf(stderr, "%s: no txt ptr space, PID %d\n", Pn, Lp->pid); Exit(1); } } /* * Remember the vnode. */ Vp[*n] = va; (*n)++; } /* * gather_proc_info() -- gather process information */ void gather_proc_info() { MALLOC_S b; struct file *fp; int i, j; struct pgrp pg; int pgid, px; struct proc *p; short pss, sf; struct ucred pcred; uid_t uid; struct utask ut, *utp; #if DUV>=30000 struct pid_entry pe; #endif /* DUV>=30000 */ #if DUV<50000 static int nufb = 0; static struct file **ufb = (struct file **)NULL; #else /* DUV>=50000 */ int k, l; KA_T ka; struct ufile_entry *ofb[U_FE_OF_ALLOC_SIZE]; struct ufile_entry ufe[U_FE_ALLOC_SIZE]; struct ufile_entry ufeo[U_FE_OF_ALLOC_SIZE]; #endif /* DUV<50000 */ #if defined(HASFSTRUCT) && DUV>=40000 static char *pof = (char *)NULL; static int pofb = 0; long pv; #endif /* defined(HASFSTRUCT) && DUV>=40000 */ /* * Clear file lock information. */ (void) clr_flinfo(); /* * Read process table entries. */ read_proc(); /* * Examine proc structures and their associated information. */ for (p = Ps, px = 0, utp = &ut; px < Psn; p++, px++) { if (p->p_stat == 0 || p->p_stat == SZOMB) continue; if (Fpgid) { if (!p->p_pgrp || kread((KA_T)p->p_pgrp, (char *)&pg, sizeof(pg))) continue; pgid = pg.pg_id; } else pgid = 0; if (p->p_rcred == NULL || kread((KA_T)p->p_rcred, (char *)&pcred, sizeof(pcred))) continue; uid = (uid_t)pcred.cr_uid; if (is_proc_excl(p->p_pid, pgid, (UID_ARG)uid, &pss, &sf)) continue; #if DUV<30000 if (!p->utask || kread((KA_T)p->utask, (char *)&ut, sizeof(ut))) #else /* DUV>=30000 */ if (kread((KA_T)((char *)Pa[px] + sizeof(struct proc)), (char *)&ut, sizeof(ut))) #endif /* DUV<30000 */ continue; /* * Allocate a local process structure. */ if (is_cmd_excl(utp->u_comm, &pss, &sf)) continue; alloc_lproc((int)p->p_pid, pgid, (int)p->p_ppid, (UID_ARG)uid, utp->u_comm, (int)pss, (int)sf); Plf = (struct lfile *)NULL; /* * Save current working directory information. */ if (utp->uu_utnd.utnd_cdir) { alloc_lfile(CWD, -1); FILEPTR = (struct file *)NULL; process_node((KA_T)utp->uu_utnd.utnd_cdir); if (Lf->sf) link_lfile(); } /* * Save root directory information. */ if (utp->uu_utnd.utnd_rdir) { alloc_lfile(RTD, -1); FILEPTR = (struct file *)NULL; process_node((KA_T)utp->uu_utnd.utnd_rdir); if (Lf->sf) link_lfile(); } /* * Print information on the text file. */ #if DUV<30000 if (p->task) process_text((KA_T)p->task, (KA_T)p->utask); #else /* DUV>=30000 */ process_text((KA_T)((char *)Pa[px] - sizeof(struct task))); #endif /* DUV<30000 */ /* * Save information on file descriptors. */ #if DUV<50000 for (i = j = 0; i <= utp->uu_file_state.uf_lastfile; i++) { if (i < NOFILE_IN_U) { fp = utp->uu_file_state.uf_ofile[i]; # if defined(HASFSTRUCT) && DUV>=40000 if (Fsv & FSV_FG) pv = (long)utp->uu_file_state.uf_pofile[i]; # endif /* defined(HASFSTRUCT) && DUV>=40000 */ } else { if (!j) { b = (MALLOC_S)(utp->uu_file_state.uf_of_count * sizeof(struct file *)); if (b > nufb) { if (!ufb) ufb = (struct file **)malloc(b); else ufb = (struct file **)realloc((MALLOC_P *)ufb, b); if (!ufb) { (void) fprintf(stderr, "%s: PID %d, no file * space\n", Pn, Lp->pid); Exit(1); } nufb = b; } if (kread((KA_T)utp->uu_file_state.uf_ofile_of, (char *)ufb, b)) break; # if defined(HASFSTRUCT) && DUV>=40000 if (Fsv & FSV_FG) { b = (MALLOC_S)(utp->uu_file_state.uf_of_count * sizeof(char)); if (b > pofb) { if (!pof) pof = (char *)malloc(b); else pof = (char *)realloc((MALLOC_P *)pof, b); if (!pof) { (void) fprintf(stderr, "%s: PID %d: no file flags space\n", Pn, Lp->pid); Exit(1); } pofb = b; } if (kread((KA_T)utp->uu_file_state.uf_pofile_of, pof, b)) zeromem(pof, b); } # endif /* defined(HASFSTRUCT) && DUV>=40000 */ } fp = ufb[j]; # if defined(HASFSTRUCT) && DUV>=40000 if (Fsv & FSV_FG) pv = pof[j]; # endif /* defined(HASFSTRUCT) && DUV>=40000 */ j++; } if (fp && (ulong)fp != 0xffffffffffffffff) { alloc_lfile(NULL, i); process_file((KA_T)fp); if (Lf->sf) { # if defined(HASFSTRUCT) && DUV>=40000 if (Fsv & FSV_FG) { if ((Lf->pof = pv)) Lf->fsv |= FSV_FG; } # endif /* defined(HASFSTRUCT) && DUV>=40000 */ link_lfile(); } } } #else /* DUV>=50000 */ for (i = j = k = 0; i <= utp->uu_file_state.uf_lastfile; i++) { if (i < NOFILE_IN_U) { if (!k) { l = i/U_FE_ALLOC_SIZE; if (!(ka = (KA_T)utp->uu_file_state.uf_entry[l])) { i += U_FE_ALLOC_SIZE - 1; continue; } } } else { if (!j) { ka = (KA_T)utp->uu_file_state.uf_of_entry; if (!ka || kread(ka, (char *)&ofb, sizeof(ofb))) break; k = 0; } if (!k) { l = j/U_FE_OF_ALLOC_SIZE; if (!(ka = (KA_T)ofb[l])) { j += U_FE_OF_ALLOC_SIZE; i += U_FE_OF_ALLOC_SIZE - 1; continue; } if (kread(ka, (char *)&ufeo, sizeof(ufeo))) break; } fp = ufeo[k].ufe_ofile; # if defined(HASFSTRUCT) && DUV>=40000 if (Fsv & FSV_FG) pv = ufeo[k].ufe_oflags; # endif /* defined(HASFSTRUCT) && DUV>=40000 */ if (++k >= U_FE_OF_ALLOC_SIZE) k = 0; j++; } if (!j) { if (!k) { if (kread(ka, (char *)&ufe, sizeof(ufe))) break; } fp = ufe[k].ufe_ofile; # if defined(HASFSTRUCT) && DUV>=40000 if (Fsv & FSV_FG) pv = ufe[k].ufe_oflags; # endif /* defined(HASFSTRUCT) && DUV>=40000 */ if (++k >= U_FE_ALLOC_SIZE) k = 0; } if (fp && (ulong)fp != 0xffffffffffffffff) { alloc_lfile(NULL, i); process_file((KA_T)fp); if (Lf->sf) { # if defined(HASFSTRUCT) && DUV>=40000 if (Fsv & FSV_FG) { if ((Lf->pof = pv)) Lf->fsv |= FSV_FG; } # endif /* defined(HASFSTRUCT) && DUV>=40000 */ link_lfile(); } } } #endif /* DUV>=50000 */ /* * Examine results. */ if (examine_lproc()) return; } } /* * get_kernel_access() - get access to kernel memory */ static void get_kernel_access() { dev_t dev; int rv; KA_T v; /* * Check kernel version. */ (void) ckkv("DU", LSOF_VSTR, (char *)NULL, (char *)NULL); /* * Set name list file path. */ #if DUV<40000 if (!Nmlst) { if (!(Nmlst = get_nlist_path(1))) { (void) fprintf(stderr, "%s: can't get kernel name list path\n", Pn); Exit(1); } } #endif /* DUV<40000 */ #if defined(WILLDROPGID) /* * If kernel memory isn't coming from KMEM, drop setgid permission * before attempting to open the (Memory) file. */ if (Memory) (void) dropgid(); #else /* !defined(WILLDROPGID) */ /* * See if the non-KMEM memory file is readable. */ if (Memory && !is_readable(Memory, 1)) Exit(1); #endif /* defined(WILLDROPGID) */ /* * Open kernel memory access. */ if ((Kd = open(Memory ? Memory : KMEM, O_RDONLY, 0)) < 0) { (void) fprintf(stderr, "%s: can't open %s: %s\n", Pn, Memory ? Memory : KMEM, sys_errlist[errno]); Exit(1); } #if defined(WILLDROPGID) /* * Drop setgid permission, if necessary. */ if (!Memory) (void) dropgid(); #else /* !defined(WILLDROPGID) */ /* * See if the name list file is readable. */ if (Nmlst && !is_readable(Nmlst, 1)) Exit(1); #endif /* defined(WILLDROPGID) */ /* * Access kernel symbols. */ (void) build_Nl(Drive_Nl); #if DUV>=40000 if (!Nmlst) rv = knlist(Nl); else #endif /* DUV>=40000 */ rv = nlist(Nmlst, Nl); if (rv == -1) { (void) fprintf(stderr, "%s: can't read kernel name list from %s: %s\n", Pn, Nmlst ? Nmlst : "knlist(3)", strerror(errno)); Exit(1); } #if DUV<30000 if (get_Nl_value("proc", Drive_Nl, &v) < 0 || !v || kread(v, (char *)&Kp, sizeof(Kp)) || get_Nl_value("nproc", Drive_Nl, &v) < 0 || !v || kread(v, (char *)&Np, sizeof(Np))) #else /* DUV>=30000 */ if (get_Nl_value("npid", Drive_Nl, &v) < 0 || !v || kread(v, (char *)&Np, sizeof(Np)) || get_Nl_value("pidt", Drive_Nl, &v) < 0 || !v || kread(v, (char *)&Pidtab, sizeof(Pidtab))) #endif /* DUV<30000 */ { (void) fprintf(stderr, "%s: can't read proc table info\n", Pn); Exit(1); } if (get_Nl_value("vnmaxp", Drive_Nl, &v) < 0 || !v || kread(v, (char *)&Vnmxp, sizeof(Vnmxp))) { (void) fprintf(stderr, "%s: can't determine vnode length\n", Pn); Exit(1); } if (get_Nl_value("cldev", Drive_Nl, &v) < 0 || !v || kread(v, (char *)&dev, sizeof(dev))) { if (!Fwarn) (void) fprintf(stderr, "%s: can't read clone device number\n", Pn); HaveCloneMaj = 0; } else { CloneMaj = GET_MAJ_DEV(dev); HaveCloneMaj = 1; } } /* * get_nlist_path() - get kernel name list path */ char * get_nlist_path(ap) int ap; /* on success, return an allocated path * string pointer if 1; return a * constant character pointer if 0; * return NULL if failure */ { char *ba, buf[MAXPATHLEN+2], *ps; int len, rv; /* * Get bootfile name. */ len = 0; if ((rv = getsysinfo(GSI_BOOTEDFILE, &buf[1], sizeof(buf) - 1, &len, (char *)NULL)) != 1) { if (rv < 0) { (void) fprintf(stderr, "%s: can't get booted file name: %s\n", Pn, strerror(errno)); Exit(1); } return((char *)NULL); } /* * Check for a non-NULL path. */ buf[sizeof(buf) - 2] = '\0'; len = strlen(&buf[1]); if (len < 1) return((char *)NULL); /* * If no path return is requested by the value of ap, return a NULL string * pointer. */ if (!ap) return(""); /* * Make sure the path has a leading '/'. */ if (buf[1] != '/') { buf[0] = '/'; ba = buf; len++; } else ba = &buf[1]; /* * Allocate permanent space for the path, copy it to the space, and return * a pointer to the space. */ len++; if (!(ps = (char *)malloc(len))) { (void) fprintf(stderr, "%s: can't allocate %d bytes for boot file path: %s\n", Pn, len, ba); Exit(1); } (void) snpf(ps, len, "%s", ba); return(ps); } /* * initialize() - perform all initialization */ void initialize() { get_kernel_access(); } /* * kread() - read from kernel memory */ int kread(addr, buf, len) KA_T addr; /* kernel memory address */ char *buf; /* buffer to receive data */ READLEN_T len; /* length to read */ { int br; if (lseek(Kd, addr, L_SET) == (off_t)-1L) return(-1); br = read(Kd, buf, len); return((br == len) ? 0 : 1); } /* * process_text() - print text information */ static void #if DUV<30000 process_text(tp, utp) #else /* DUV>=30000 */ process_text(tp) #endif /* DUV<30000 */ KA_T tp; /* kernel task structure */ #if DUV<30000 KA_T utp; /* user task structure address for * the task */ #endif /* DUV<30000 */ { int i; KA_T ka, kb; int n = 0; struct task t; struct vm_anon_object vmao; struct vm_map_entry vmme; struct vm_map vmm; struct vm_object vmo; struct vm_seg vms; #if DUV<40000 struct vm_vp_object vpo; #else /* DUV>=40000 */ struct vm_ubc_object vpo; #endif /* DUV<40000 */ /* * Read task structure from kernel. */ if (kread(tp, (char *)&t, sizeof(t)) #if DUV<30000 || (KA_T)t.u_address != utp #endif /* DUV<30000 */ ) return; /* * Print information about the text vnode referenced in the procfs * structure inside the task structure. */ if (t.procfs.pr_exvp) enter_vn_text((KA_T)t.procfs.pr_exvp, &n); /* * Read the vm_map structure. Search its vm_map_entry structure list. */ if (!t.map || kread((KA_T)t.map, (char *)&vmm, sizeof(vmm))) return; if (!vmm.vm_is_mainmap) return; #if defined(VM_SKIPLIST) for (i = 0, ka = (KA_T)vmm.vm_links.vml_sl_next[0]; i < vmm.vm_nentries && ka != (KA_T)t.map; i++, ka = (KA_T)vmme.vme_links.vml_sl_next[0]) #else /* !defined(VM_SKIPLIST) */ for (i = 0, ka = (KA_T)vmm.vm_links.next; i < vmm.vm_nentries && ka != (KA_T)t.map; i++, ka = (KA_T)vmme.vme_links.next) #endif /* defined(VM_SKIPLIST) */ { /* * Read the next vm_map_entry structure and its object. */ if (kread(ka, (char *)&vmme, sizeof(vmme))) return; if (!(kb = (KA_T)vmme.vme_uobject.vm_object) || kread(kb, (char *)&vmo, sizeof(vmo))) continue; /* * Process by object type. */ switch (vmo.ob_type) { case OT_ANON: /* * If an anonymous object is backed by an OT_UBC or OT_VP object, * read its vm_ubc_object or vm_vp_object to find a vnode pointer. */ if (kread(kb, (char *)&vmao, sizeof(vmao))) break; if (!vmao.ao_bobject || kread((KA_T)vmao.ao_bobject, (char *)&vmo, sizeof(vmo))) break; #if DUV<40000 if (vmo.ob_type != OT_VP || kread((KA_T)vmao.ao_bobject, (char *)&vpo, sizeof(vpo))) break; enter_vn_text((KA_T)vpo.vo_vp, &n); #else /* DUV>=40000 */ if (vmo.ob_type != OT_UBC || kread((KA_T)vmao.ao_bobject, (char *)&vpo, sizeof(vpo))) break; # if DUV>=50000 enter_vn_text(vpo2vp(&vpo), &n); # else /* DUV<50000 */ enter_vn_text((KA_T)vpo.vu_vfp.vp, &n); #endif /* DUV>=50000 */ #endif /* DUV<40000 */ break; /* * If this is a segment object, read the segment map, and search * for backing objects whose object type is OT_UBC or OT_VP. */ case OT_SEG: for (kb=(KA_T)vmme.vme_seg; kb; kb=(KA_T)vms.seg_vnext) { if (kread(kb, (char *)&vms, sizeof(vms))) break; if (!vms.seg_vop || kread((KA_T)vms.seg_vop, (char *)&vmo, sizeof(vmo))) continue; #if DUV<40000 if (vmo.ob_type != OT_VP) #else /* DUV>=40000 */ if (vmo.ob_type != OT_UBC) #endif /* DUV<40000 */ continue; if (kread((KA_T)vms.seg_vop, (char *)&vpo, sizeof(vpo))) break; #if DUV<40000 enter_vn_text((KA_T)vpo.vo_vp, &n); #else /* DUV>=40000 */ # if DUV>=50000 enter_vn_text(vpo2vp(&vpo), &n); # else /* DUV<50000 */ enter_vn_text((KA_T)vpo.vu_vfp.vp, &n); #endif /* DUV<40000 */ #endif /* DUV>=50000 */ } } } } /* * read_proc() - read process table entries */ static void read_proc() { static int ap = 0; MALLOC_S len; struct proc *p; KA_T pa; int px, try; #if DUV>=30000 struct pid_entry pe; #endif /* DUV>=30000 */ if (!Ps) { /* * Allocate local proc table space. */ if (Np < 1) { (void) fprintf(stderr, "%s: proc table has no entries\n", Pn); Exit(1); } len = (MALLOC_S)(PAPSINIT * sizeof(struct proc)); if (!(Ps = (struct proc *)malloc(len))) { (void) fprintf(stderr, "%s: no proc table space (%d bytes)\n", Pn, len); Exit(1); } #if DUV>=30000 /* * Allocate kernel proc address table space. */ len = (MALLOC_S)(PAPSINIT * sizeof(KA_T)); if (!(Pa = (KA_T *)malloc(len))) { (void) fprintf(stderr, "%s: no proc address table space (%d bytes)\n", Pn, len); Exit(1); } #endif /* DUV>=30000 */ ap = PAPSINIT; } /* * Try to read the proc structure table PROCTRYLM times. * The operation must yield PROCMIN structures. */ for (try = 0; try < PROCTRYLM; try++) { for (p = Ps, Psn = px = 0; px < Np; px++) { /* * Insure Ps and Psa space. */ if (Psn >= ap) { ap += PAPSINCR; len = (MALLOC_S)(ap * sizeof(struct proc)); if (!(Ps = (struct proc *)realloc((MALLOC_P *)Ps, len))) { (void) fprintf(stderr, "%s: no more proc table space (%d bytes)\n", Pn, len); Exit(1); } p = &Ps[Psn]; #if DUV>=30000 len = (MALLOC_S)(ap * sizeof(KA_T)); if (!(Pa = (KA_T *)realloc((MALLOC_P *)Pa, len))) { (void) fprintf(stderr, "%s: no more proc address table space (%d bytes)\n", Pn, len); Exit(1); } #endif /* DUV>=30000 */ } #if DUV<30000 pa = Kp + (KA_T)(px * sizeof(struct proc)); if (kread(pa, (char *)p, sizeof(struct proc))) continue; #else /* DUV>=30000 */ pa = Pidtab + (KA_T)(px * sizeof(struct pid_entry)); if (kread(pa, (char *)&pe, sizeof(struct pid_entry))) continue; if ((pa = (KA_T)pe.pe_proc) == NULL || kread(pa, (char *)p, sizeof(struct proc))) continue; if (pe.pe_pid != p->p_pid) continue; Pa[Psn] = pa; #endif /* DUV<30000 */ Psn++; p++; } /* * Check the results of the scan. */ if (Psn >= PROCMIN) break; } /* * Quit if not enough proc structures could be accumulated. */ if (try >= PROCTRYLM) { (void) fprintf(stderr, "%s: can't read proc table\n", Pn); Exit(1); } if (Psn < Np && !RptTm) { /* * Reduce the local proc structure tables to their minimum if * not in repeat mode. */ ap = Psn; len = (MALLOC_S)(Psn * sizeof(struct proc)); if (!(Ps = (struct proc *)realloc((MALLOC_P *)Ps, len))) { (void) fprintf(stderr, "%s: can't reduce proc table to %d bytes\n", Pn, len); Exit(1); } #if DUV>=30000 len = (MALLOC_S)(Psn * sizeof(KA_T)); if (!(Pa = (KA_T *)realloc((MALLOC_P *)Pa, len))) { (void) fprintf(stderr, "%s: can't reduce proc address table to %d bytes\n", Pn, len); Exit(1); } #endif /* DUV>=30000 */ } } #if DUV>=50000 /* * vfp2vp() -- convert VM object's vu_vfp to a vnode pointer */ static KA_T vpo2vp(vpo) struct vm_ubc_object *vpo; /* pointer to local vm_ubc_object */ { struct advfsbfs { /* This structure is referenced in * vm_ubc.h (as msfsbfs), but never * defined in a distributed header * file, so we make a hack definition * here. */ unsigned long d1[18]; /* dummies */ struct vnode *vp; /* vnode */ } bfa; static int ft = 1; KA_T ka; static KA_T ops = (KA_T)0; /* * If this is the first time, get the msfs (AdvFS) UBC operation switch * address. */ if (ft) { ft = 0; #if defined(ADVFSV) if (get_Nl_value("msfsubc", Drive_Nl, &ops) < 0) #endif /* defined(ADVFSV) */ ops = (KA_T)0; } ka = (KA_T)vpo->vu_vfp.vp; if (!ops || ((KA_T)vpo->vu_ops != ops)) return(ka); if (!ka || kread(ka, (char *)&bfa, sizeof(bfa))) return(ka); return((KA_T)bfa.vp); } #endif /* DUV>=50000 */ #if DUV>=50100 && defined(HASNCACHE) /* * Kernel name cache functions and associate definiitions for Tru64 UNIX * 5.1 and above. */ /* * Definitions */ /* * Structures */ struct l_nch { struct namecache *nc; /* namecache entry */ struct l_nch *nxt; /* next hashed entry */ }; /* * Static variables */ static int Hmsk = 0; /* Nchash[] mask -- (size - 1), where * size is a power of two */ static int Nc; /* number of cached namecache structs */ static struct l_nch **Nchash = (struct l_nch **)NULL; /* hash pointers buckets */ static int Ncfirst = 1; /* first-call status */ /* * Definitions */ #define ncachehash(i) (((int)(i*31415)>>3)&Hmsk) /* * Prototypes */ _PROTOTYPE(static struct l_nch *ncache_addr,(unsigned long id)); _PROTOTYPE(static int ncache_ckrootid,(KA_T na, unsigned long id)); _PROTOTYPE(static int ncache_isroot,(KA_T na, char *cp)); /* * ncache_addr() -- look up a node's local ncache address */ static struct l_nch * ncache_addr(id) unsigned long id; /* node's capability ID */ { register struct l_nch *hp; for (hp = Nchash[ncachehash(id)]; hp; hp = hp->nxt) { if ((hp->nc)->nc_vpid == id) return(hp); } return((struct l_nch *)NULL); } /* * ncache_ckrootid() - check for a root node ID */ static int ncache_ckrootid(na, id) KA_T na; /* vnode address */ unsigned long id; /* root ID to check */ { #if defined(ADVFSV) struct advfsmount { /* This structure should be defined in * a distributed header file, but it * isn't, so we make a hack definition * here. */ u_long d1[10]; /* dummies */ struct vnode *am_rootvp; /* root vnode pointer */ } am; static KA_T aops = (KA_T)0; #endif /* defined(ADVFSV) */ struct cdfsmount cm; static KA_T cops = (KA_T)0; struct dvdfsmount dm; static KA_T dops = (KA_T)0; static KA_T fops = (KA_T)0; static KA_T frvp = (KA_T)0; static int ft = 1; register int i; static unsigned long *ic = (unsigned long *)NULL; MALLOC_S len; struct mount m; static int nia = 0; static int niu = 0; struct mntinfo nm; static KA_T nops = (KA_T)0; static KA_T n3ops = (KA_T)0; KA_T rv; struct ufsmount um; static KA_T uops = (KA_T)0; struct vnode v; /* * Check the cache. */ if (ic && niu) { for (i = 0; i < niu; i++) { if (id == ic[i]) return(1); } } /* * Read the vnode and the associated mount structure. */ if (!na || kread(na, (char *)&v, sizeof(v))) return(0); if (!v.v_mount || kread((KA_T)v.v_mount, (char *)&m, sizeof(m))) return(0); /* * If this is the first time this function has been used, get the necessary * kernel addresses. */ if (ft) { ft = 0; #if defined(ADVFSV) if (get_Nl_value("advfsvfs", (struct drive_Nl *)NULL, &aops) < 0) aops = (KA_T)0; #endif /* defined(ADVFSV) */ if (get_Nl_value("cdfsvfs", (struct drive_Nl *)NULL, &cops) < 0) cops = (KA_T)0; if (get_Nl_value("dvdfsvfs", (struct drive_Nl *)NULL, &dops) < 0) dops = (KA_T)0; if (get_Nl_value("fdfsvfs", (struct drive_Nl *)NULL, &fops) < 0) fops = (KA_T)0; if (get_Nl_value("fsfsrvp", (struct drive_Nl *)NULL, &frvp) < 0) frvp = (KA_T)0; if (get_Nl_value("nfsvfs", (struct drive_Nl *)NULL, &nops) < 0) nops = (KA_T)0; if (get_Nl_value("nfs3vfs", (struct drive_Nl *)NULL, &n3ops) < 0) n3ops = (KA_T)0; if (get_Nl_value("ufsvfs", (struct drive_Nl *)NULL, &uops) < 0) uops = (KA_T)0; } /* * See if we know how to find the root vnode pointer for this file system * type. */ #if defined(ADVFSV) if (aops && (aops == (KA_T)m.m_op)) { /* * Set AdvFS (MSFS) root vnode address. */ if (!m.m_info || kread((KA_T)m.m_info, (char *)&am, sizeof(am))) return(0); rv = (KA_T)am.am_rootvp; } else #endif /* defined(ADVFSV) */ if (cops && (cops == (KA_T)m.m_op)) { /* * Set CDFS root vnode address. */ if (!m.m_info || kread((KA_T)m.m_info, (char *)&cm, sizeof(cm))) return(0); rv = (KA_T)cm.um_rootvp; } else if (dops && (dops == (KA_T)m.m_op)) { /* * Set DVDFS root vnode address. */ if (!m.m_info || kread((KA_T)m.m_info, (char *)&dm, sizeof(dm))) return(0); rv = (KA_T)dm.dm_rootvp; } else if (fops && (fops == (KA_T)m.m_op)) { /* * Set FDFS root vnode address. */ rv = frvp; } else if ((nops && (nops == (KA_T)m.m_op)) || (n3ops && (n3ops == (KA_T)m.m_op))) { /* * Set NFS[3] root vnode address. */ if (!m.m_info || kread((KA_T)m.m_info, (char *)&nm, sizeof(nm))) return(0); rv = (KA_T)nm.mi_rootvp; } else if (uops && (uops == (KA_T)m.m_op)) { /* * Set UFS root vnode address. */ if (!m.m_info || kread((KA_T)m.m_info, (char *)&um, sizeof(um))) return(0); rv = (KA_T)um.um_rootvp; } else return(0); /* * Read the root vnode. */ if (!rv || kread(rv, (char *)&v, sizeof(v))) return(0); if (id != v.v_id) return(0); /* * A new root vnode has been located. Cache it. */ if (niu >= nia) { if (!nia) { len = (MALLOC_S)(10 * sizeof(unsigned long)); ic = (unsigned long *)malloc(len); } else { len = (MALLOC_S)((nia + 10) * sizeof(unsigned long)); ic = (unsigned long *)realloc((MALLOC_P *)ic, len); } if (!ic) { (void) fprintf(stderr, "%s: no space for root node VPID table\n", Pn); Exit(1); } nia += 10; } ic[niu++] = id; return(1); } /* * ncache_isroot() - is head of name cache path a file system root? */ static int ncache_isroot(na, cp) KA_T na; /* vnode address */ char *cp; /* partial path */ { char buf[MAXPATHLEN]; int i; MALLOC_S len; struct mounts *mtp; static KA_T *nc = (KA_T *)NULL; static int nca = 0; static int ncn = 0; struct stat sb; struct vnode v; if (!na) return(0); /* * Search the root capability node address cache. */ for (i = 0; i < ncn; i++) { if (na == nc[i]) return(1); } /* * Read the vnode and see if it's a VDIR node with the VROOT flag set. If * it is, then the path is complete. * * If it isn't, and if the file has an inode number, search the mount table * and see if the file system's inode number is known. If it is, form the * possible full path, safely stat() it, and see if it's inode number matches * the one we have for this file. If it does, then the path is complete. */ if (kread((KA_T)na, (char *)&v, sizeof(v)) || v.v_type != VDIR || !(v.v_flag & VROOT)) { /* * The vnode tests failed. Try the inode tests. */ if (Lf->inp_ty != 1 || !Lf->inode || !Lf->fsdir || (len = strlen(Lf->fsdir)) < 1) return(0); if ((len + 1 + strlen(cp) + 1) > sizeof(buf)) return(0); for (mtp = readmnt(); mtp; mtp = mtp->next) { if (!mtp->dir || !mtp->inode) continue; if (strcmp(Lf->fsdir, mtp->dir) == 0) break; } if (!mtp) return(0); (void) strcpy(buf, Lf->fsdir); if (buf[len - 1] != '/') buf[len++] = '/'; (void) strcpy(&buf[len], cp); if (statsafely(buf, &sb) != 0 || (INODETYPE)sb.st_ino != Lf->inode) return(0); } /* * Add the capability ID to the root capability ID cache. */ if (ncn >= nca) { if (!nca) { len = (MALLOC_S)(10 * sizeof(KA_T)); nc = (KA_T *)malloc(len); } else { len = (MALLOC_S)((nca + 10) * sizeof(KA_T)); nc = (KA_T *)realloc((MALLOC_P *)nc, len); } if (!nc) { (void) fprintf(stderr, "%s: no space for root node address table\n", Pn); Exit(1); } nca += 10; } nc[ncn++] = na; return(1); } /* * ncache_load() - load the kernel's name cache */ void ncache_load() { register int h, i, n; KA_T ka, ncp; int len; register struct l_nch *lp; struct l_nch *lpnxt; static struct namecache *nc = (struct namecache *)NULL; static int ncl = 0; static int nchsz = 0; static int ncpc = 0; static int ncpus = 0; register struct namecache *np; static KA_T *pp = (KA_T *)NULL; if (!Fncache) return; if (Ncfirst) { /* * Do startup (first-time) functions. */ Ncfirst = 0; /* * Get CPU count. */ ka = (KA_T)0; if (get_Nl_value("ncpus", (struct drive_Nl *)NULL, &ka) < 0 || !ka || kread(ka, (char *)&ncpus, sizeof(ncpus))) { if (!Fwarn) (void) fprintf(stderr, "%s: WARNING: can't read processor count: %s\n", Pn, print_kptr(ka, (char *)NULL, 0)); ncl = nchsz = ncpc = ncpus = 0; return; } if (ncpus < 1) { if (!Fwarn) (void) fprintf(stderr, "%s: WARNING: processor count: %d\n", Pn, ncpus); ncl = nchsz = ncpc = ncpus = 0; return; } /* * Get the per-processor table address. */ ka = (KA_T)0; if (get_Nl_value("procptr", (struct drive_Nl *)NULL, &ka) < 0 || !ka || kread(ka, (char *)&ka, sizeof(ka)) || !ka) { if (!Fwarn) (void) fprintf(stderr, "%s: WARNING: per processor table address: %s\n", Pn, print_kptr(ka, (char *)NULL, 0)); ncl = nchsz = ncpc = ncpus = 0; return; } /* * Allocate space for the processor structure addresses and read them. */ len = (int)(ncpus * sizeof(KA_T)); if (!(pp = (KA_T *)malloc((MALLOC_S)len))) { (void) fprintf(stderr, "%s: can't allocate %d bytes for processor addresses\n", Pn, len); Exit(1); } if (kread(ka, (char *)pp, len)) { if (!Fwarn) (void) fprintf(stderr, "%s: WARNING: can't read processor addresses: %s\n", Pn, print_kptr(ka, (char *)NULL, 0)); ncl = nchsz = ncpc = ncpus = 0; return; } for (i = 0; i < ncpus; i++) { if (pp[i]) pp[i] = (KA_T)((char *)pp[i] + offsetof(struct processor, namecache)); } /* * Get the per-processor nchash size. */ ka = (KA_T)0; if (get_Nl_value("nchsz", (struct drive_Nl *)NULL, &ka) < 0 || !ka || kread((KA_T)ka, (char *)&nchsz, sizeof(nchsz))) { if (!Fwarn) (void) fprintf(stderr, "%s: WARNING: processor nchash count address: %s\n", Pn, print_kptr(ka, (char *)NULL, 0)); ncl = nchsz = ncpc = ncpus = 0; return; } if (nchsz < 1) { if (!Fwarn) (void) fprintf(stderr, "%s: WARNING: bad per processor nchash count: %d\n", Pn, nchsz); nchsz = ncpus = 1; return; } /* * Allocate space for nchsz * NCHSIZE * ncpus namecache structures. */ ncpc = (int)(nchsz * NCHSIZE); ncl = (int)(ncpc * sizeof(struct namecache)); len = (int)(ncl * ncpus); if (!(nc = (struct namecache *)malloc((MALLOC_S)len))) { (void) fprintf(stderr, "%s: no space for %d namecache entries (%d bytes)\n", Pn, ncpc * ncpus, len); Exit(1); } } else { /* * Do setup for repeat calls. */ if (Nchash) { for (i = 0; i <= Hmsk; i++) { for (lp = Nchash[i]; lp; lp = lpnxt) { lpnxt = lp->nxt; (void) free((MALLOC_P *)lp); } } (void) free((MALLOC_P *)Nchash); Nchash = (struct l_nch **)NULL; Nc = 0; } } /* * Loop through the processors, reading the processor structure pointer * for the processor, then its name cache. Build a local name cache * table of struct namecache entries for all processors. */ for (i = n = 0; i < ncpus; i++) { if (!pp[i]) continue; if (kread(pp[i], (char *)&ncp, sizeof(ncp)) || !ncp) continue; if (kread(ncp, (char *)&nc[n], ncl)) continue; n += ncpc; } /* * Compute a hash table size and allocate it. */ if (!n) return; for (i = 1; i < n; i <<= 1) ; i += i; Hmsk = i - 1; if (!(Nchash = (struct l_nch **)calloc(i, sizeof(struct l_nch *)))) { if (!Fwarn) (void) fprintf(stderr, "%s: no space for %d byte name cache hash buckets\n", Pn, (int)(i * sizeof(struct l_nch *))); Exit(1); } /* * Assign hash pointers to the accumulated namecache entries. */ for (i = Nc = 0; i < n; i++) { if (!nc[i].nc_vpid) continue; if (((len = nc[i].nc_nlen) < 1) || (len > NCHNAMLEN)) continue; if (len < 3 && nc[i].nc_name[0] == '.') { if ((len == 1) || ((len == 2) && (nc[i].nc_name[1] == '.'))) continue; } h = ncachehash(nc[i].nc_vpid); /* * Look for an existing hash entry. Choose among duplicates the one * with the largest nc_dvpid. */ for (lp = Nchash[h]; lp; lp = lp->nxt) { if ((np = lp->nc) && (np->nc_vpid == nc[i].nc_vpid)) { if (nc[i].nc_dvpid > np->nc_dvpid) lp->nc = &nc[i]; break; } } if (lp) continue; /* * Allocate and fill a new local name cache entry. */ if (!(lp = (struct l_nch *)malloc(sizeof(struct l_nch)))) { (void) fprintf(stderr, "%s: can't allocate l_nch entry\n", Pn); Exit(1); } lp->nc = &nc[i]; lp->nxt = Nchash[h]; Nchash[h] = lp; Nc++; } } /* * ncache_lookup() - look up a node's name in the kernel's name cache */ char * ncache_lookup(buf, blen, fp) char *buf; /* receiving name buffer */ int blen; /* receiving buffer length */ int *fp; /* full path reply */ { char *cp = buf; struct l_nch *lc; struct mounts *mtp; struct namecache *nc; int nl, rlen; *cp = '\0'; *fp = 0; # if defined(HASFSINO) /* * If the entry has an inode number that matches the inode number of the * file system mount point, return an empty path reply. That tells the * caller to print the file system mount point name only. */ if (Lf->inp_ty == 1 && Lf->fs_ino && Lf->inode == Lf->fs_ino) return(cp); # endif /* defined(HASFSINO) */ /* * Look up the name cache entry for the node address. */ if (Nc == 0 || !(lc = ncache_addr(Lf->id)) || !(nc = lc->nc)) { /* * If the node has no cache entry, see if it's the mount * point of a known file system. */ if (!Lf->fsdir || !Lf->dev_def || Lf->inp_ty != 1) return((char *)NULL); for (mtp = readmnt(); mtp; mtp = mtp->next) { if (!mtp->dir || !mtp->inode) continue; if ((Lf->dev == mtp->dev) && (mtp->inode == Lf->inode) && (strcmp(mtp->dir, Lf->fsdir) == 0)) return(cp); } return((char *)NULL); } /* * Start the path assembly. */ if ((nl = nc->nc_nlen) > (blen - 1)) return((char *)NULL); cp = buf + blen - nl - 1; rlen = blen - nl - 1; (void) strncpy(cp, nc->nc_name, nl); cp[nl] = '\0'; /* * Look up the name cache entries that are parents of the node address. * Quit when: * * there's no parent; * the name length is too large to fit in the receiving buffer. */ for (;;) { if (!nc->nc_dvpid) { if (ncache_isroot((KA_T)nc->nc_vp, cp)) *fp = 1; break; } if (!(lc = ncache_addr(nc->nc_dvpid))) { if (ncache_ckrootid((KA_T)nc->nc_vp, nc->nc_dvpid)) *fp = 1; break; } if (!(nc = lc->nc)) break; if (((nl = nc->nc_nlen) + 1) > rlen) break; *(cp - 1) = '/'; cp--; rlen--; (void) strncpy(cp - nl, nc->nc_name, nl); cp -= nl; rlen -= nl; } return(cp); } #endif /* DUV>=50100 && defined(HASNCACHE) */