Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/miscfs/kernfs/kernfs_vnops.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/miscfs/kernfs/kernfs_vnops.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.97 retrieving revision 1.137 diff -u -p -r1.97 -r1.137 --- src/sys/miscfs/kernfs/kernfs_vnops.c 2003/09/26 03:08:18 1.97 +++ src/sys/miscfs/kernfs/kernfs_vnops.c 2009/06/23 19:36:40 1.137 @@ -1,4 +1,4 @@ -/* $NetBSD: kernfs_vnops.c,v 1.97 2003/09/26 03:08:18 atatat Exp $ */ +/* $NetBSD: kernfs_vnops.c,v 1.137 2009/06/23 19:36:40 elad Exp $ */ /* * Copyright (c) 1992, 1993 @@ -39,7 +39,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: kernfs_vnops.c,v 1.97 2003/09/26 03:08:18 atatat Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kernfs_vnops.c,v 1.137 2009/06/23 19:36:40 elad Exp $"); #ifdef _KERNEL_OPT #include "opt_ipsec.h" @@ -79,108 +79,145 @@ __KERNEL_RCSID(0, "$NetBSD: kernfs_vnops #define READ_MODE (S_IRUSR|S_IRGRP|S_IROTH) #define WRITE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH) -#define DIR_MODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) -#define UDIR_MODE (S_IRUSR|S_IXUSR) +#define UREAD_MODE (S_IRUSR) +#define DIR_MODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) +#define UDIR_MODE (S_IRUSR|S_IXUSR) #define N(s) sizeof(s)-1, s const struct kern_target kern_targets[] = { /* NOTE: The name must be less than UIO_MX-16 chars in length */ /* name data tag type ro/rw */ - { DT_DIR, N("."), 0, Kern, VDIR, DIR_MODE }, - { DT_DIR, N(".."), 0, Kroot, VDIR, DIR_MODE }, - { DT_REG, N("boottime"), &boottime.tv_sec, Kint, VREG, READ_MODE }, - /* XXX cast away const */ - { DT_REG, N("copyright"), (void *)copyright, - Kstring, VREG, READ_MODE }, - { DT_REG, N("hostname"), 0, Khostname, VREG, WRITE_MODE }, - { DT_REG, N("hz"), &hz, Kint, VREG, READ_MODE }, + { DT_DIR, N("."), 0, KFSkern, VDIR, DIR_MODE }, + { DT_DIR, N(".."), 0, KFSroot, VDIR, DIR_MODE }, + { DT_REG, N("boottime"), &boottime.tv_sec, KFSint, VREG, READ_MODE }, + /* XXXUNCONST */ + { DT_REG, N("copyright"), __UNCONST(copyright), + KFSstring, VREG, READ_MODE }, + { DT_REG, N("hostname"), 0, KFShostname, VREG, WRITE_MODE }, + { DT_REG, N("hz"), &hz, KFSint, VREG, READ_MODE }, #ifdef IPSEC - { DT_DIR, N("ipsecsa"), 0, Kipsecsadir, VDIR, UDIR_MODE }, - { DT_DIR, N("ipsecsp"), 0, Kipsecspdir, VDIR, UDIR_MODE }, + { DT_DIR, N("ipsecsa"), 0, KFSipsecsadir, VDIR, UDIR_MODE }, + { DT_DIR, N("ipsecsp"), 0, KFSipsecspdir, VDIR, UDIR_MODE }, #endif - { DT_REG, N("loadavg"), 0, Kavenrun, VREG, READ_MODE }, - { DT_REG, N("msgbuf"), 0, Kmsgbuf, VREG, READ_MODE }, - { DT_REG, N("pagesize"), &uvmexp.pagesize, Kint, VREG, READ_MODE }, - { DT_REG, N("physmem"), &physmem, Kint, VREG, READ_MODE }, + { DT_REG, N("loadavg"), 0, KFSavenrun, VREG, READ_MODE }, + { DT_REG, N("msgbuf"), 0, KFSmsgbuf, VREG, READ_MODE }, + { DT_REG, N("pagesize"), &uvmexp.pagesize, KFSint, VREG, READ_MODE }, + { DT_REG, N("physmem"), &physmem, KFSint, VREG, READ_MODE }, #if 0 - { DT_DIR, N("root"), 0, Knull, VDIR, DIR_MODE }, + { DT_DIR, N("root"), 0, KFSnull, VDIR, DIR_MODE }, #endif - { DT_BLK, N("rootdev"), &rootdev, Kdevice, VBLK, READ_MODE }, - { DT_CHR, N("rrootdev"), &rrootdev, Kdevice, VCHR, READ_MODE }, - { DT_REG, N("time"), 0, Ktime, VREG, READ_MODE }, - /* XXX cast away const */ - { DT_REG, N("version"), (void *)version, - Kstring, VREG, READ_MODE }, + { DT_BLK, N("rootdev"), &rootdev, KFSdevice, VBLK, READ_MODE }, + { DT_CHR, N("rrootdev"), &rrootdev, KFSdevice, VCHR, READ_MODE }, + { DT_REG, N("time"), 0, KFStime, VREG, READ_MODE }, + /* XXXUNCONST */ + { DT_REG, N("version"), __UNCONST(version), + KFSstring, VREG, READ_MODE }, +}; +const struct kern_target subdir_targets[] = { +/* NOTE: The name must be less than UIO_MX-16 chars in length */ + /* name data tag type ro/rw */ + { DT_DIR, N("."), 0, KFSsubdir, VDIR, DIR_MODE }, + { DT_DIR, N(".."), 0, KFSkern, VDIR, DIR_MODE }, }; #ifdef IPSEC const struct kern_target ipsecsa_targets[] = { /* NOTE: The name must be less than UIO_MX-16 chars in length */ /* name data tag type ro/rw */ - { DT_DIR, N("."), 0, Kipsecsadir, VDIR, DIR_MODE }, - { DT_DIR, N(".."), 0, Kern, VDIR, DIR_MODE }, + { DT_DIR, N("."), 0, KFSipsecsadir, VDIR, DIR_MODE }, + { DT_DIR, N(".."), 0, KFSkern, VDIR, DIR_MODE }, }; const struct kern_target ipsecsp_targets[] = { /* NOTE: The name must be less than UIO_MX-16 chars in length */ /* name data tag type ro/rw */ - { DT_DIR, N("."), 0, Kipsecspdir, VDIR, DIR_MODE }, - { DT_DIR, N(".."), 0, Kern, VDIR, DIR_MODE }, + { DT_DIR, N("."), 0, KFSipsecspdir, VDIR, DIR_MODE }, + { DT_DIR, N(".."), 0, KFSkern, VDIR, DIR_MODE }, }; +const struct kern_target ipsecsa_kt = + { DT_DIR, N(""), 0, KFSipsecsa, VREG, UREAD_MODE }; +const struct kern_target ipsecsp_kt = + { DT_DIR, N(""), 0, KFSipsecsp, VREG, UREAD_MODE }; #endif #undef N +SIMPLEQ_HEAD(,dyn_kern_target) dyn_kern_targets = + SIMPLEQ_HEAD_INITIALIZER(dyn_kern_targets); int nkern_targets = sizeof(kern_targets) / sizeof(kern_targets[0]); +const int static_nkern_targets = sizeof(kern_targets) / sizeof(kern_targets[0]); #ifdef IPSEC int nipsecsa_targets = sizeof(ipsecsa_targets) / sizeof(ipsecsa_targets[0]); int nipsecsp_targets = sizeof(ipsecsp_targets) / sizeof(ipsecsp_targets[0]); +int nkern_dirs = 4; /* 2 extra subdirs */ +#else +int nkern_dirs = 2; #endif +int kernfs_try_fileop(kfstype, kfsfileop, void *, int); +int kernfs_try_xread(kfstype, const struct kernfs_node *, char **, + size_t, int); +int kernfs_try_xwrite(kfstype, const struct kernfs_node *, char *, + size_t, int); + +static int kernfs_default_xread(void *v); +static int kernfs_default_xwrite(void *v); +static int kernfs_default_fileop_getattr(void *); + +/* must include all fileop's */ +const struct kernfs_fileop kernfs_default_fileops[] = { + { .kf_fileop = KERNFS_XREAD }, + { .kf_fileop = KERNFS_XWRITE }, + { .kf_fileop = KERNFS_FILEOP_OPEN }, + { .kf_fileop = KERNFS_FILEOP_GETATTR, + .kf_vop = kernfs_default_fileop_getattr }, + { .kf_fileop = KERNFS_FILEOP_IOCTL }, + { .kf_fileop = KERNFS_FILEOP_CLOSE }, + { .kf_fileop = KERNFS_FILEOP_READ, + .kf_vop = kernfs_default_xread }, + { .kf_fileop = KERNFS_FILEOP_WRITE, + .kf_vop = kernfs_default_xwrite }, +}; -int kernfs_lookup __P((void *)); +int kernfs_lookup(void *); #define kernfs_create genfs_eopnotsupp #define kernfs_mknod genfs_eopnotsupp -int kernfs_open __P((void *)); -int kernfs_close __P((void *)); -int kernfs_access __P((void *)); -int kernfs_getattr __P((void *)); -int kernfs_setattr __P((void *)); -int kernfs_read __P((void *)); -int kernfs_write __P((void *)); +int kernfs_open(void *); +int kernfs_close(void *); +int kernfs_access(void *); +int kernfs_getattr(void *); +int kernfs_setattr(void *); +int kernfs_read(void *); +int kernfs_write(void *); #define kernfs_fcntl genfs_fcntl -#define kernfs_ioctl genfs_enoioctl +int kernfs_ioctl(void *); #define kernfs_poll genfs_poll #define kernfs_revoke genfs_revoke #define kernfs_fsync genfs_nullop #define kernfs_seek genfs_nullop #define kernfs_remove genfs_eopnotsupp -int kernfs_link __P((void *)); +int kernfs_link(void *); #define kernfs_rename genfs_eopnotsupp #define kernfs_mkdir genfs_eopnotsupp #define kernfs_rmdir genfs_eopnotsupp -int kernfs_symlink __P((void *)); -int kernfs_readdir __P((void *)); +int kernfs_symlink(void *); +int kernfs_readdir(void *); #define kernfs_readlink genfs_eopnotsupp #define kernfs_abortop genfs_abortop -int kernfs_inactive __P((void *)); -int kernfs_reclaim __P((void *)); +int kernfs_inactive(void *); +int kernfs_reclaim(void *); #define kernfs_lock genfs_lock #define kernfs_unlock genfs_unlock #define kernfs_bmap genfs_badop #define kernfs_strategy genfs_badop -int kernfs_print __P((void *)); +int kernfs_print(void *); #define kernfs_islocked genfs_islocked -int kernfs_pathconf __P((void *)); +int kernfs_pathconf(void *); #define kernfs_advlock genfs_einval -#define kernfs_blkatoff genfs_eopnotsupp -#define kernfs_valloc genfs_eopnotsupp -#define kernfs_vfree genfs_nullop -#define kernfs_truncate genfs_eopnotsupp -#define kernfs_update genfs_nullop #define kernfs_bwrite genfs_eopnotsupp #define kernfs_putpages genfs_putpages -static int kernfs_xread __P((struct kernfs_node *, int, char **, size_t, size_t *)); -static int kernfs_xwrite __P((const struct kernfs_node *, char *, size_t)); +static int kernfs_xread(struct kernfs_node *, int, char **, + size_t, size_t *); +static int kernfs_xwrite(const struct kernfs_node *, char *, size_t); -int (**kernfs_vnodeop_p) __P((void *)); +int (**kernfs_vnodeop_p)(void *); const struct vnodeopv_entry_desc kernfs_vnodeop_entries[] = { { &vop_default_desc, vn_default_error }, { &vop_lookup_desc, kernfs_lookup }, /* lookup */ @@ -218,11 +255,6 @@ const struct vnodeopv_entry_desc kernfs_ { &vop_islocked_desc, kernfs_islocked }, /* islocked */ { &vop_pathconf_desc, kernfs_pathconf }, /* pathconf */ { &vop_advlock_desc, kernfs_advlock }, /* advlock */ - { &vop_blkatoff_desc, kernfs_blkatoff }, /* blkatoff */ - { &vop_valloc_desc, kernfs_valloc }, /* valloc */ - { &vop_vfree_desc, kernfs_vfree }, /* vfree */ - { &vop_truncate_desc, kernfs_truncate }, /* truncate */ - { &vop_update_desc, kernfs_update }, /* update */ { &vop_bwrite_desc, kernfs_bwrite }, /* bwrite */ { &vop_putpages_desc, kernfs_putpages }, /* putpages */ { NULL, NULL } @@ -230,45 +262,159 @@ const struct vnodeopv_entry_desc kernfs_ const struct vnodeopv_desc kernfs_vnodeop_opv_desc = { &kernfs_vnodeop_p, kernfs_vnodeop_entries }; +static inline int +kernfs_fileop_compare(struct kernfs_fileop *a, struct kernfs_fileop *b) +{ + if (a->kf_type < b->kf_type) + return -1; + if (a->kf_type > b->kf_type) + return 1; + if (a->kf_fileop < b->kf_fileop) + return -1; + if (a->kf_fileop > b->kf_fileop) + return 1; + return (0); +} + +SPLAY_HEAD(kfsfileoptree, kernfs_fileop) kfsfileoptree = + SPLAY_INITIALIZER(kfsfileoptree); +SPLAY_PROTOTYPE(kfsfileoptree, kernfs_fileop, kf_node, kernfs_fileop_compare); +SPLAY_GENERATE(kfsfileoptree, kernfs_fileop, kf_node, kernfs_fileop_compare); + +kfstype +kernfs_alloctype(int nkf, const struct kernfs_fileop *kf) +{ + static u_char nextfreetype = KFSlasttype; + struct kernfs_fileop *dkf, *fkf, skf; + int i; + + /* XXX need to keep track of dkf's memory if we support + deallocating types */ + dkf = malloc(sizeof(kernfs_default_fileops), M_TEMP, M_WAITOK); + memcpy(dkf, kernfs_default_fileops, sizeof(kernfs_default_fileops)); + + for (i = 0; i < sizeof(kernfs_default_fileops) / + sizeof(kernfs_default_fileops[0]); i++) { + dkf[i].kf_type = nextfreetype; + SPLAY_INSERT(kfsfileoptree, &kfsfileoptree, &dkf[i]); + } + + for (i = 0; i < nkf; i++) { + skf.kf_type = nextfreetype; + skf.kf_fileop = kf[i].kf_fileop; + if ((fkf = SPLAY_FIND(kfsfileoptree, &kfsfileoptree, &skf))) + fkf->kf_vop = kf[i].kf_vop; + } + + return nextfreetype++; +} + +int +kernfs_try_fileop(kfstype type, kfsfileop fileop, void *v, int error) +{ + struct kernfs_fileop *kf, skf; + + skf.kf_type = type; + skf.kf_fileop = fileop; + if ((kf = SPLAY_FIND(kfsfileoptree, &kfsfileoptree, &skf))) + if (kf->kf_vop) + return kf->kf_vop(v); + return error; +} + +int +kernfs_try_xread(kfstype type, const struct kernfs_node *kfs, char **bfp, + size_t len, int error) +{ + struct kernfs_fileop *kf, skf; + + skf.kf_type = type; + skf.kf_fileop = KERNFS_XREAD; + if ((kf = SPLAY_FIND(kfsfileoptree, &kfsfileoptree, &skf))) + if (kf->kf_xread) + return kf->kf_xread(kfs, bfp, len); + return error; +} + +int +kernfs_try_xwrite(kfstype type, const struct kernfs_node *kfs, char *bf, + size_t len, int error) +{ + struct kernfs_fileop *kf, skf; + + skf.kf_type = type; + skf.kf_fileop = KERNFS_XWRITE; + if ((kf = SPLAY_FIND(kfsfileoptree, &kfsfileoptree, &skf))) + if (kf->kf_xwrite) + return kf->kf_xwrite(kfs, bf, len); + return error; +} + +int +kernfs_addentry(kernfs_parentdir_t *pkt, kernfs_entry_t *dkt) +{ + struct kernfs_subdir *ks, *parent; + + if (pkt == NULL) { + SIMPLEQ_INSERT_TAIL(&dyn_kern_targets, dkt, dkt_queue); + nkern_targets++; + if (dkt->dkt_kt.kt_vtype == VDIR) + nkern_dirs++; + } else { + parent = (struct kernfs_subdir *)pkt->kt_data; + SIMPLEQ_INSERT_TAIL(&parent->ks_entries, dkt, dkt_queue); + parent->ks_nentries++; + if (dkt->dkt_kt.kt_vtype == VDIR) + parent->ks_dirs++; + } + if (dkt->dkt_kt.kt_vtype == VDIR && dkt->dkt_kt.kt_data == NULL) { + ks = malloc(sizeof(struct kernfs_subdir), + M_TEMP, M_WAITOK); + SIMPLEQ_INIT(&ks->ks_entries); + ks->ks_nentries = 2; /* . and .. */ + ks->ks_dirs = 2; + ks->ks_parent = pkt ? pkt : &kern_targets[0]; + dkt->dkt_kt.kt_data = ks; + } + return 0; +} + static int -kernfs_xread(kfs, off, bufp, len, wrlen) - struct kernfs_node *kfs; - int off; - char **bufp; - size_t len; - size_t *wrlen; +kernfs_xread(struct kernfs_node *kfs, int off, char **bufp, size_t len, size_t *wrlen) { const struct kern_target *kt; #ifdef IPSEC struct mbuf *m; #endif + int err; kt = kfs->kfs_kt; switch (kfs->kfs_type) { - case Ktime: { + case KFStime: { struct timeval tv; microtime(&tv); - snprintf(*bufp, len, "%ld %ld\n", tv.tv_sec, tv.tv_usec); + snprintf(*bufp, len, "%lld %ld\n", (long long)tv.tv_sec, + (long)tv.tv_usec); break; } - case Kint: { + case KFSint: { int *ip = kt->kt_data; snprintf(*bufp, len, "%d\n", *ip); break; } - case Kstring: { + case KFSstring: { char *cp = kt->kt_data; *bufp = cp; break; } - case Kmsgbuf: { + case KFSmsgbuf: { long n; /* @@ -302,9 +448,9 @@ kernfs_xread(kfs, off, bufp, len, wrlen) return (0); } - case Khostname: { + case KFShostname: { char *cp = hostname; - int xlen = hostnamelen; + size_t xlen = hostnamelen; if (xlen >= (len - 2)) return (EINVAL); @@ -316,7 +462,7 @@ kernfs_xread(kfs, off, bufp, len, wrlen) break; } - case Kavenrun: + case KFSavenrun: averunnable.fscale = FSCALE; snprintf(*bufp, len, "%d %d %d %ld\n", averunnable.ldavg[0], averunnable.ldavg[1], @@ -324,7 +470,9 @@ kernfs_xread(kfs, off, bufp, len, wrlen) break; #ifdef IPSEC - case Kipsecsa: + case KFSipsecsa: + if (key_setdumpsa_spi == NULL) + return 0; /* * Note that SA configuration could be changed during the * read operation, resulting in garbled output. @@ -344,11 +492,13 @@ kernfs_xread(kfs, off, bufp, len, wrlen) m_freem(m); return (0); - case Kipsecsp: + case KFSipsecsp: /* * Note that SP configuration could be changed during the * read operation, resulting in garbled output. */ + if (key_getspbyid == NULL) + return 0; if (!kfs->kfs_v) { struct secpolicy *sp; @@ -376,8 +526,10 @@ kernfs_xread(kfs, off, bufp, len, wrlen) #endif default: - *wrlen = 0; - return (0); + err = kernfs_try_xread(kfs->kfs_type, kfs, bufp, len, + EOPNOTSUPP); + if (err) + return err; } len = strlen(*bufp); @@ -391,23 +543,20 @@ kernfs_xread(kfs, off, bufp, len, wrlen) } static int -kernfs_xwrite(kfs, buf, len) - const struct kernfs_node *kfs; - char *buf; - size_t len; +kernfs_xwrite(const struct kernfs_node *kfs, char *bf, size_t len) { switch (kfs->kfs_type) { - case Khostname: - if (buf[len-1] == '\n') + case KFShostname: + if (bf[len-1] == '\n') --len; - memcpy(hostname, buf, len); + memcpy(hostname, bf, len); hostname[len] = '\0'; hostnamelen = (size_t) len; return (0); default: - return (EIO); + return kernfs_try_xwrite(kfs->kfs_type, kfs, bf, len, EIO); } } @@ -417,8 +566,7 @@ kernfs_xwrite(kfs, buf, len) * ndp is the name to locate in that directory... */ int -kernfs_lookup(v) - void *v; +kernfs_lookup(void *v) { struct vop_lookup_args /* { struct vnode * a_dvp; @@ -431,14 +579,15 @@ kernfs_lookup(v) const char *pname = cnp->cn_nameptr; const struct kernfs_node *kfs; const struct kern_target *kt; - int error, i, wantpunlock; + const struct dyn_kern_target *dkt; + const struct kernfs_subdir *ks; + int error, i; #ifdef IPSEC char *ep; u_int32_t id; #endif *vpp = NULLVP; - cnp->cn_flags &= ~PDIRUNLOCK; if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME) return (EROFS); @@ -449,46 +598,62 @@ kernfs_lookup(v) return (0); } - wantpunlock = (~cnp->cn_flags & (LOCKPARENT | ISLASTCN)); kfs = VTOKERN(dvp); switch (kfs->kfs_type) { - case Kern: + case KFSkern: /* * Shouldn't get here with .. in the root node. */ if (cnp->cn_flags & ISDOTDOT) return (EIO); - for (i = 0; i < nkern_targets; i++) { + for (i = 0; i < static_nkern_targets; i++) { kt = &kern_targets[i]; if (cnp->cn_namelen == kt->kt_namlen && memcmp(kt->kt_name, pname, cnp->cn_namelen) == 0) goto found; } + SIMPLEQ_FOREACH(dkt, &dyn_kern_targets, dkt_queue) { + if (cnp->cn_namelen == dkt->dkt_kt.kt_namlen && + memcmp(dkt->dkt_kt.kt_name, pname, cnp->cn_namelen) == 0) { + kt = &dkt->dkt_kt; + goto found; + } + } break; found: error = kernfs_allocvp(dvp->v_mount, vpp, kt->kt_tag, kt, 0); - if ((error == 0) && wantpunlock) { - VOP_UNLOCK(dvp, 0); - cnp->cn_flags |= PDIRUNLOCK; - } return (error); + case KFSsubdir: + ks = (struct kernfs_subdir *)kfs->kfs_kt->kt_data; + if (cnp->cn_flags & ISDOTDOT) { + kt = ks->ks_parent; + goto found; + } + + SIMPLEQ_FOREACH(dkt, &ks->ks_entries, dkt_queue) { + if (cnp->cn_namelen == dkt->dkt_kt.kt_namlen && + memcmp(dkt->dkt_kt.kt_name, pname, cnp->cn_namelen) == 0) { + kt = &dkt->dkt_kt; + goto found; + } + } + break; + #ifdef IPSEC - case Kipsecsadir: - for (i = 0; i < nipsecsa_targets; i++) { + case KFSipsecsadir: + if (cnp->cn_flags & ISDOTDOT) { + kt = &kern_targets[0]; + goto found; + } + + for (i = 2; i < nipsecsa_targets; i++) { kt = &ipsecsa_targets[i]; if (cnp->cn_namelen == kt->kt_namlen && - memcmp(kt->kt_name, pname, cnp->cn_namelen) == 0) { - error = kernfs_allocvp(dvp->v_mount, vpp, - kt->kt_tag, kt, 0); - if ((error == 0) && wantpunlock) { - VOP_UNLOCK(dvp, 0); - cnp->cn_flags |= PDIRUNLOCK; - } - return (error); - } + memcmp(kt->kt_name, pname, cnp->cn_namelen) == 0) + goto found; } ep = NULL; @@ -496,26 +661,20 @@ kernfs_lookup(v) if (!ep || *ep || ep == pname) break; - error = kernfs_allocvp(dvp->v_mount, vpp, Kipsecsa, NULL, id); - if ((error == 0) && wantpunlock) { - VOP_UNLOCK(dvp, 0); - cnp->cn_flags |= PDIRUNLOCK; - } + error = kernfs_allocvp(dvp->v_mount, vpp, KFSipsecsa, &ipsecsa_kt, id); return (error); - case Kipsecspdir: - for (i = 0; i < nipsecsp_targets; i++) { + case KFSipsecspdir: + if (cnp->cn_flags & ISDOTDOT) { + kt = &kern_targets[0]; + goto found; + } + + for (i = 2; i < nipsecsp_targets; i++) { kt = &ipsecsp_targets[i]; if (cnp->cn_namelen == kt->kt_namlen && - memcmp(kt->kt_name, pname, cnp->cn_namelen) == 0) { - error = kernfs_allocvp(dvp->v_mount, vpp, - kt->kt_tag, kt, 0); - if ((error == 0) && wantpunlock) { - VOP_UNLOCK(dvp, 0); - cnp->cn_flags |= PDIRUNLOCK; - } - return (error); - } + memcmp(kt->kt_name, pname, cnp->cn_namelen) == 0) + goto found; } ep = NULL; @@ -523,11 +682,7 @@ kernfs_lookup(v) if (!ep || *ep || ep == pname) break; - error = kernfs_allocvp(dvp->v_mount, vpp, Kipsecsp, NULL, id); - if ((error == 0) && wantpunlock) { - VOP_UNLOCK(dvp, 0); - cnp->cn_flags |= PDIRUNLOCK; - } + error = kernfs_allocvp(dvp->v_mount, vpp, KFSipsecsp, &ipsecsp_kt, id); return (error); #endif @@ -539,14 +694,12 @@ kernfs_lookup(v) } int -kernfs_open(v) - void *v; +kernfs_open(void *v) { struct vop_open_args /* { struct vnode *a_vp; int a_mode; - struct ucred *a_cred; - struct proc *a_p; + kauth_cred_t a_cred; } */ *ap = v; struct kernfs_node *kfs = VTOKERN(ap->a_vp); #ifdef IPSEC @@ -556,7 +709,9 @@ kernfs_open(v) switch (kfs->kfs_type) { #ifdef IPSEC - case Kipsecsa: + case KFSipsecsa: + if (key_setdumpsa_spi == NULL) + return 0; m = key_setdumpsa_spi(htonl(kfs->kfs_value)); if (m) { m_freem(m); @@ -564,7 +719,9 @@ kernfs_open(v) } else return (ENOENT); - case Kipsecsp: + case KFSipsecsp: + if (key_getspbyid == NULL) + return 0; sp = key_getspbyid(kfs->kfs_value); if (sp) { kfs->kfs_v = sp; @@ -574,70 +731,85 @@ kernfs_open(v) #endif default: - return (0); + return kernfs_try_fileop(kfs->kfs_type, KERNFS_FILEOP_OPEN, + v, 0); } } int -kernfs_close(v) - void *v; +kernfs_close(void *v) { struct vop_close_args /* { struct vnode *a_vp; int a_fflag; - struct ucred *a_cred; - struct proc *a_p; + kauth_cred_t a_cred; } */ *ap = v; struct kernfs_node *kfs = VTOKERN(ap->a_vp); switch (kfs->kfs_type) { #ifdef IPSEC - case Kipsecsp: + case KFSipsecsp: + if (key_freesp == NULL) + return 0; key_freesp((struct secpolicy *)kfs->kfs_v); break; #endif default: - break; + return kernfs_try_fileop(kfs->kfs_type, KERNFS_FILEOP_CLOSE, + v, 0); } return (0); } int -kernfs_access(v) - void *v; +kernfs_access(void *v) { struct vop_access_args /* { struct vnode *a_vp; int a_mode; - struct ucred *a_cred; - struct proc *a_p; + kauth_cred_t a_cred; } */ *ap = v; struct vattr va; int error; - if ((error = VOP_GETATTR(ap->a_vp, &va, ap->a_cred, ap->a_p)) != 0) + if ((error = VOP_GETATTR(ap->a_vp, &va, ap->a_cred)) != 0) return (error); - return (vaccess(va.va_type, va.va_mode, va.va_uid, va.va_gid, + return (genfs_can_access(va.va_type, va.va_mode, va.va_uid, va.va_gid, ap->a_mode, ap->a_cred)); } +static int +kernfs_default_fileop_getattr(void *v) +{ + struct vop_getattr_args /* { + struct vnode *a_vp; + struct vattr *a_vap; + kauth_cred_t a_cred; + } */ *ap = v; + struct vattr *vap = ap->a_vap; + + vap->va_nlink = 1; + vap->va_bytes = vap->va_size = 0; + + return 0; +} + int -kernfs_getattr(v) - void *v; +kernfs_getattr(void *v) { struct vop_getattr_args /* { struct vnode *a_vp; struct vattr *a_vap; - struct ucred *a_cred; - struct proc *a_p; + kauth_cred_t a_cred; } */ *ap = v; struct kernfs_node *kfs = VTOKERN(ap->a_vp); + struct kernfs_subdir *ks; struct vattr *vap = ap->a_vap; int error = 0; - char strbuf[KSTRING], *buf; + char strbuf[KSTRING], *bf; size_t nread, total; VATTR_NULL(vap); @@ -649,17 +821,12 @@ kernfs_getattr(v) vap->va_flags = 0; vap->va_size = 0; vap->va_blocksize = DEV_BSIZE; - /* - * Make all times be current TOD, except for the "boottime" node. - * Avoid microtime(9), it's slow. - * We don't guard the read from time(9) with splclock(9) since we - * don't actually need to be THAT sure the access is atomic. - */ - if (kfs->kfs_kt && kfs->kfs_kt->kt_namlen == 8 && + /* Make all times be current TOD, except for the "boottime" node. */ + if (kfs->kfs_kt->kt_namlen == 8 && !memcmp(kfs->kfs_kt->kt_name, "boottime", 8)) { - TIMEVAL_TO_TIMESPEC(&boottime, &vap->va_ctime); + vap->va_ctime = boottime; } else { - TIMEVAL_TO_TIMESPEC(&time, &vap->va_ctime); + getnanotime(&vap->va_ctime); } vap->va_atime = vap->va_mtime = vap->va_ctime; vap->va_gen = 0; @@ -668,37 +835,39 @@ kernfs_getattr(v) vap->va_bytes = 0; switch (kfs->kfs_type) { - case Kern: -#ifdef IPSEC - vap->va_nlink = 4; /* 2 extra subdirs */ -#else - vap->va_nlink = 2; -#endif + case KFSkern: + vap->va_nlink = nkern_dirs; vap->va_bytes = vap->va_size = DEV_BSIZE; break; - case Kroot: + case KFSroot: vap->va_nlink = 1; vap->va_bytes = vap->va_size = DEV_BSIZE; break; - case Knull: - case Ktime: - case Kint: - case Kstring: - case Khostname: - case Kavenrun: - case Kdevice: - case Kmsgbuf: + case KFSsubdir: + ks = (struct kernfs_subdir *)kfs->kfs_kt->kt_data; + vap->va_nlink = ks->ks_dirs; + vap->va_bytes = vap->va_size = DEV_BSIZE; + break; + + case KFSnull: + case KFStime: + case KFSint: + case KFSstring: + case KFShostname: + case KFSavenrun: + case KFSdevice: + case KFSmsgbuf: #ifdef IPSEC - case Kipsecsa: - case Kipsecsp: + case KFSipsecsa: + case KFSipsecsp: #endif vap->va_nlink = 1; total = 0; do { - buf = strbuf; - error = kernfs_xread(kfs, total, &buf, + bf = strbuf; + error = kernfs_xread(kfs, total, &bf, sizeof(strbuf), &nread); total += nread; } while (error == 0 && nread != 0); @@ -706,15 +875,16 @@ kernfs_getattr(v) break; #ifdef IPSEC - case Kipsecsadir: - case Kipsecspdir: + case KFSipsecsadir: + case KFSipsecspdir: vap->va_nlink = 2; vap->va_bytes = vap->va_size = DEV_BSIZE; break; #endif default: - error = EINVAL; + error = kernfs_try_fileop(kfs->kfs_type, + KERNFS_FILEOP_GETATTR, v, EINVAL); break; } @@ -723,8 +893,7 @@ kernfs_getattr(v) /*ARGSUSED*/ int -kernfs_setattr(v) - void *v; +kernfs_setattr(void *v) { /* @@ -737,45 +906,67 @@ kernfs_setattr(v) } int -kernfs_read(v) - void *v; +kernfs_default_xread(void *v) { struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; - struct ucred *a_cred; + kauth_cred_t a_cred; } */ *ap = v; struct uio *uio = ap->a_uio; struct kernfs_node *kfs = VTOKERN(ap->a_vp); - char strbuf[KSTRING], *buf; - off_t off; + char strbuf[KSTRING], *bf; + int off; size_t len; int error; if (ap->a_vp->v_type == VDIR) return (EOPNOTSUPP); - off = uio->uio_offset; - buf = strbuf; - if ((error = kernfs_xread(kfs, off, &buf, sizeof(strbuf), &len)) == 0) - error = uiomove(buf, len, uio); + off = (int)uio->uio_offset; + /* Don't allow negative offsets */ + if (off < 0) + return EINVAL; + + bf = strbuf; + if ((error = kernfs_xread(kfs, off, &bf, sizeof(strbuf), &len)) == 0) + error = uiomove(bf, len, uio); return (error); } int -kernfs_write(v) - void *v; +kernfs_read(void *v) { - struct vop_write_args /* { + struct vop_read_args /* { struct vnode *a_vp; struct uio *a_uio; int a_ioflag; struct ucred *a_cred; } */ *ap = v; struct kernfs_node *kfs = VTOKERN(ap->a_vp); + + if (kfs->kfs_type < KFSlasttype) { + /* use default function */ + return kernfs_default_xread(v); + } + return kernfs_try_fileop(kfs->kfs_type, KERNFS_FILEOP_READ, v, + EOPNOTSUPP); +} + +static int +kernfs_default_xwrite(void *v) +{ + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + kauth_cred_t a_cred; + } */ *ap = v; + struct kernfs_node *kfs = VTOKERN(ap->a_vp); struct uio *uio = ap->a_uio; - int error, xlen; + int error; + size_t xlen; char strbuf[KSTRING]; if (uio->uio_offset != 0) @@ -794,13 +985,102 @@ kernfs_write(v) } int -kernfs_readdir(v) - void *v; +kernfs_write(void *v) +{ + struct vop_write_args /* { + struct vnode *a_vp; + struct uio *a_uio; + int a_ioflag; + kauth_cred_t a_cred; + } */ *ap = v; + struct kernfs_node *kfs = VTOKERN(ap->a_vp); + + if (kfs->kfs_type < KFSlasttype) { + /* use default function */ + return kernfs_default_xwrite(v); + } + return kernfs_try_fileop(kfs->kfs_type, KERNFS_FILEOP_WRITE, v, + EOPNOTSUPP); +} + +int +kernfs_ioctl(void *v) +{ + struct vop_ioctl_args /* { + const struct vnodeop_desc *a_desc; + struct vnode *a_vp; + u_long a_command; + void *a_data; + int a_fflag; + kauth_cred_t a_cred; + } */ *ap = v; + struct kernfs_node *kfs = VTOKERN(ap->a_vp); + + return kernfs_try_fileop(kfs->kfs_type, KERNFS_FILEOP_IOCTL, v, + EPASSTHROUGH); +} + +static int +kernfs_setdirentfileno_kt(struct dirent *d, const struct kern_target *kt, + u_int32_t value, struct vop_readdir_args *ap) +{ + struct kernfs_node *kfs; + struct vnode *vp; + int error; + + if ((error = kernfs_allocvp(ap->a_vp->v_mount, &vp, kt->kt_tag, kt, + value)) != 0) + return error; + if (kt->kt_tag == KFSdevice) { + struct vattr va; + + error = VOP_GETATTR(vp, &va, ap->a_cred); + if (error != 0) { + return error; + } + d->d_fileno = va.va_fileid; + } else { + kfs = VTOKERN(vp); + d->d_fileno = kfs->kfs_fileno; + } + vput(vp); + return 0; +} + +static int +kernfs_setdirentfileno(struct dirent *d, off_t entry, + struct kernfs_node *thisdir_kfs, const struct kern_target *parent_kt, + const struct kern_target *kt, struct vop_readdir_args *ap) +{ + const struct kern_target *ikt; + int error; + + switch (entry) { + case 0: + d->d_fileno = thisdir_kfs->kfs_fileno; + return 0; + case 1: + ikt = parent_kt; + break; + default: + ikt = kt; + break; + } + if (ikt != thisdir_kfs->kfs_kt) { + if ((error = kernfs_setdirentfileno_kt(d, ikt, 0, ap)) != 0) + return error; + } else + d->d_fileno = thisdir_kfs->kfs_fileno; + return 0; +} + +int +kernfs_readdir(void *v) { struct vop_readdir_args /* { struct vnode *a_vp; struct uio *a_uio; - struct ucred *a_cred; + kauth_cred_t a_cred; int *a_eofflag; off_t **a_cookies; int a_*ncookies; @@ -809,7 +1089,9 @@ kernfs_readdir(v) struct dirent d; struct kernfs_node *kfs = VTOKERN(ap->a_vp); const struct kern_target *kt; - off_t i; + const struct dyn_kern_target *dkt = NULL; + const struct kernfs_subdir *ks; + off_t i, j; int error; off_t *cookies = NULL; int ncookies = 0, n; @@ -830,7 +1112,7 @@ kernfs_readdir(v) ncookies = uio->uio_resid / UIO_MX; switch (kfs->kfs_type) { - case Kern: + case KFSkern: if (i >= nkern_targets) return (0); @@ -843,8 +1125,24 @@ kernfs_readdir(v) n = 0; for (; i < nkern_targets && uio->uio_resid >= UIO_MX; i++) { - kt = &kern_targets[i]; - if (kt->kt_tag == Kdevice) { + if (i < static_nkern_targets) + kt = &kern_targets[i]; + else { + if (dkt == NULL) { + dkt = SIMPLEQ_FIRST(&dyn_kern_targets); + for (j = static_nkern_targets; j < i && + dkt != NULL; j++) + dkt = SIMPLEQ_NEXT(dkt, dkt_queue); + if (j != i) + break; + } else { + dkt = SIMPLEQ_NEXT(dkt, dkt_queue); + } + if (dkt == NULL) + break; + kt = &dkt->dkt_kt; + } + if (kt->kt_tag == KFSdevice) { dev_t *dp = kt->kt_data; struct vnode *fvp; @@ -853,14 +1151,12 @@ kernfs_readdir(v) continue; } d.d_namlen = kt->kt_namlen; - if (i < 2) - d.d_fileno = KERNFS_FILENO(&kern_targets[0], - kern_targets[0].kt_tag, 0); - else - d.d_fileno = KERNFS_FILENO(kt, kt->kt_tag, 0); + if ((error = kernfs_setdirentfileno(&d, i, kfs, + &kern_targets[0], kt, ap)) != 0) + break; memcpy(d.d_name, kt->kt_name, kt->kt_namlen + 1); d.d_type = kt->kt_type; - if ((error = uiomove((caddr_t)&d, UIO_MX, uio)) != 0) + if ((error = uiomove(&d, UIO_MX, uio)) != 0) break; if (cookies) *cookies++ = i + 1; @@ -869,7 +1165,7 @@ kernfs_readdir(v) ncookies = n; break; - case Kroot: + case KFSroot: if (i >= 2) return 0; @@ -887,7 +1183,56 @@ kernfs_readdir(v) d.d_fileno = KERNFS_FILENO(kt, kt->kt_tag, 0); memcpy(d.d_name, kt->kt_name, kt->kt_namlen + 1); d.d_type = kt->kt_type; - if ((error = uiomove((caddr_t)&d, UIO_MX, uio)) != 0) + if ((error = uiomove(&d, UIO_MX, uio)) != 0) + break; + if (cookies) + *cookies++ = i + 1; + n++; + } + ncookies = n; + break; + + case KFSsubdir: + ks = (struct kernfs_subdir *)kfs->kfs_kt->kt_data; + if (i >= ks->ks_nentries) + return (0); + + if (ap->a_ncookies) { + ncookies = min(ncookies, (ks->ks_nentries - i)); + cookies = malloc(ncookies * sizeof(off_t), M_TEMP, + M_WAITOK); + *ap->a_cookies = cookies; + } + + dkt = SIMPLEQ_FIRST(&ks->ks_entries); + for (j = 0; j < i && dkt != NULL; j++) + dkt = SIMPLEQ_NEXT(dkt, dkt_queue); + n = 0; + for (; i < ks->ks_nentries && uio->uio_resid >= UIO_MX; i++) { + if (i < 2) + kt = &subdir_targets[i]; + else { + /* check if ks_nentries lied to us */ + if (dkt == NULL) + break; + kt = &dkt->dkt_kt; + dkt = SIMPLEQ_NEXT(dkt, dkt_queue); + } + if (kt->kt_tag == KFSdevice) { + dev_t *dp = kt->kt_data; + struct vnode *fvp; + + if (*dp == NODEV || + !vfinddev(*dp, kt->kt_vtype, &fvp)) + continue; + } + d.d_namlen = kt->kt_namlen; + if ((error = kernfs_setdirentfileno(&d, i, kfs, + ks->ks_parent, kt, ap)) != 0) + break; + memcpy(d.d_name, kt->kt_name, kt->kt_namlen + 1); + d.d_type = kt->kt_type; + if ((error = uiomove(&d, UIO_MX, uio)) != 0) break; if (cookies) *cookies++ = i + 1; @@ -897,9 +1242,11 @@ kernfs_readdir(v) break; #ifdef IPSEC - case Kipsecsadir: + case KFSipsecsadir: /* count SA in the system */ n = 0; + if (&satailq == NULL) + return 0; TAILQ_FOREACH(sav, &satailq, tailq) { for (sav2 = TAILQ_FIRST(&satailq); sav2 != sav; @@ -927,10 +1274,12 @@ kernfs_readdir(v) for (; i < nipsecsa_targets && uio->uio_resid >= UIO_MX; i++) { kt = &ipsecsa_targets[i]; d.d_namlen = kt->kt_namlen; - d.d_fileno = KERNFS_FILENO(kt, kt->kt_tag, 0); + if ((error = kernfs_setdirentfileno(&d, i, kfs, + &kern_targets[0], kt, ap)) != 0) + break; memcpy(d.d_name, kt->kt_name, kt->kt_namlen + 1); d.d_type = kt->kt_type; - if ((error = uiomove((caddr_t)&d, UIO_MX, uio)) != 0) + if ((error = uiomove(&d, UIO_MX, uio)) != 0) break; if (cookies) *cookies++ = i + 1; @@ -954,12 +1303,13 @@ kernfs_readdir(v) continue; if (uio->uio_resid < UIO_MX) break; - d.d_fileno = KERNFS_FILENO(kfs->kfs_kt, kfs->kfs_type, - kfs->kfs_cookie); + if ((error = kernfs_setdirentfileno_kt(&d, &ipsecsa_kt, + sav->spi, ap)) != 0) + break; d.d_namlen = snprintf(d.d_name, sizeof(d.d_name), "%u", ntohl(sav->spi)); d.d_type = DT_REG; - if ((error = uiomove((caddr_t)&d, UIO_MX, uio)) != 0) + if ((error = uiomove(&d, UIO_MX, uio)) != 0) break; if (cookies) *cookies++ = i + 1; @@ -969,13 +1319,16 @@ kernfs_readdir(v) ncookies = n; break; - case Kipsecspdir: + case KFSipsecspdir: /* count SP in the system */ + if (&sptailq == NULL) + return 0; + n = 0; TAILQ_FOREACH(sp, &sptailq, tailq) n++; - if (i >= 2 + n) + if (i >= nipsecsp_targets + n) return (0); if (ap->a_ncookies) { @@ -989,10 +1342,12 @@ kernfs_readdir(v) for (; i < nipsecsp_targets && uio->uio_resid >= UIO_MX; i++) { kt = &ipsecsp_targets[i]; d.d_namlen = kt->kt_namlen; - d.d_fileno = KERNFS_FILENO(kt, kt->kt_tag, 0); + if ((error = kernfs_setdirentfileno(&d, i, kfs, + &kern_targets[0], kt, ap)) != 0) + break; memcpy(d.d_name, kt->kt_name, kt->kt_namlen + 1); d.d_type = kt->kt_type; - if ((error = uiomove((caddr_t)&d, UIO_MX, uio)) != 0) + if ((error = uiomove(&d, UIO_MX, uio)) != 0) break; if (cookies) *cookies++ = i + 1; @@ -1006,12 +1361,13 @@ kernfs_readdir(v) TAILQ_FOREACH(sp, &sptailq, tailq) { if (uio->uio_resid < UIO_MX) break; - d.d_fileno = KERNFS_FILENO(kfs->kfs_kt, kfs->kfs_type, - kfs->kfs_cookie); + if ((error = kernfs_setdirentfileno_kt(&d, &ipsecsp_kt, + sp->id, ap)) != 0) + break; d.d_namlen = snprintf(d.d_name, sizeof(d.d_name), "%u", sp->id); d.d_type = DT_REG; - if ((error = uiomove((caddr_t)&d, UIO_MX, uio)) != 0) + if ((error = uiomove(&d, UIO_MX, uio)) != 0) break; if (cookies) *cookies++ = i + 1; @@ -1042,12 +1398,11 @@ kernfs_readdir(v) } int -kernfs_inactive(v) - void *v; +kernfs_inactive(void *v) { struct vop_inactive_args /* { struct vnode *a_vp; - struct proc *a_p; + bool *a_recycle; } */ *ap = v; struct vnode *vp = ap->a_vp; const struct kernfs_node *kfs = VTOKERN(ap->a_vp); @@ -1056,35 +1411,38 @@ kernfs_inactive(v) struct secpolicy *sp; #endif - VOP_UNLOCK(vp, 0); + *ap->a_recycle = false; switch (kfs->kfs_type) { #ifdef IPSEC - case Kipsecsa: + case KFSipsecsa: + if (key_setdumpsa_spi == NULL) + return 0; m = key_setdumpsa_spi(htonl(kfs->kfs_value)); if (m) m_freem(m); else - vgone(vp); + *ap->a_recycle = true; break; - case Kipsecsp: + case KFSipsecsp: + if (key_getspbyid == NULL) + return 0; sp = key_getspbyid(kfs->kfs_value); if (sp) key_freesp(sp); else { - /* should never happen as we hold a refcnt */ - vgone(vp); + *ap->a_recycle = true; } break; #endif default: break; } + VOP_UNLOCK(vp, 0); return (0); } int -kernfs_reclaim(v) - void *v; +kernfs_reclaim(void *v) { struct vop_reclaim_args /* { struct vnode *a_vp; @@ -1097,8 +1455,7 @@ kernfs_reclaim(v) * Return POSIX pathconf information applicable to special devices. */ int -kernfs_pathconf(v) - void *v; +kernfs_pathconf(void *v) { struct vop_pathconf_args /* { struct vnode *a_vp; @@ -1139,8 +1496,7 @@ kernfs_pathconf(v) */ /* ARGSUSED */ int -kernfs_print(v) - void *v; +kernfs_print(void *v) { printf("tag VT_KERNFS, kernfs vnode\n"); @@ -1148,23 +1504,21 @@ kernfs_print(v) } int -kernfs_link(v) - void *v; +kernfs_link(void *v) { struct vop_link_args /* { struct vnode *a_dvp; - struct vnode *a_vp; + struct vnode *a_vp; struct componentname *a_cnp; } */ *ap = v; - + VOP_ABORTOP(ap->a_dvp, ap->a_cnp); vput(ap->a_dvp); return (EROFS); } int -kernfs_symlink(v) - void *v; +kernfs_symlink(void *v) { struct vop_symlink_args /* { struct vnode *a_dvp; @@ -1173,7 +1527,7 @@ kernfs_symlink(v) struct vattr *a_vap; char *a_target; } */ *ap = v; - + VOP_ABORTOP(ap->a_dvp, ap->a_cnp); vput(ap->a_dvp); return (EROFS);