#!/bin/sh - copyright="\ /* * Copyright (c) 1992, 1993, 1994, 1995 * The Regents of the University of California. 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. * 3. 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. */ " SCRIPT_ID='$NetBSD: vnode_if.sh,v 1.77 2022/10/26 23:39:43 riastradh Exp $' # Script to produce VFS front-end sugar. # # usage: vnode_if.sh srcfile # (where srcfile is currently /sys/kern/vnode_if.src) # if [ $# -ne 1 ] ; then echo 'usage: vnode_if.sh srcfile' exit 1 fi # Name and revision of the source file. src=$1 SRC_ID=`head -1 $src | sed -e 's/.*\$\(.*\)\$.*/\1/'` # Names of the created files. out_c=vnode_if.c out_rumpc=../rump/librump/rumpvfs/rumpvnode_if.c out_h=../sys/vnode_if.h out_rumph=../rump/include/rump/rumpvnode_if.h # generate VNODE_LOCKDEBUG checks (not fully functional) lockdebug=1 # Awk program (must support nawk extensions) # Use "awk" at Berkeley, "nawk" or "gawk" elsewhere. awk=${AWK:-awk} # Does this awk have a "toupper" function? (i.e. is it GNU awk) isgawk=`$awk 'BEGIN { print toupper("true"); exit; }' 2>/dev/null` # If this awk does not define "toupper" then define our own. if [ "$isgawk" = TRUE ] ; then # GNU awk provides it. toupper= else # Provide our own toupper() toupper=' function toupper(str) { _toupper_cmd = "echo "str" |tr a-z A-Z" _toupper_cmd | getline _toupper_str; close(_toupper_cmd); return _toupper_str; }' fi # # This is the common part of all awk programs that read $src # This parses the input for one function into the arrays: # argdir, argtype, argname, willrele # and calls "doit()" to generate output for the function. # # Input to this parser is pre-processed slightly by sed # so this awk parser doesn't have to work so hard. The # changes done by the sed pre-processing step are: # insert a space between * and pointer name # replace semicolons with spaces # sed_prep='s:\*\([^\*/]\):\* \1:g s/;/ /' awk_parser=' # Comment line /^#/ { next; } # First line of description /^vop_/ { name=$1; args_name=$1; argc=0; have_context=0; is_context=0; ncontext=0; willmake=-1; fstrans=""; do_pre=""; do_post=""; next; } # Last line of description /^}/ { doit(); next; } # Middle lines of description { if ($1 == "VERSION") { args_name=args_name "_v" $2; next; } else if ($1 ~ "^FSTRANS=") { fstrans = $1; sub("FSTRANS=", "", fstrans); next; } else if ($1 ~ "^PRE=") { do_pre = $1; sub("PRE=", "", do_pre); next; } else if ($1 ~ "^POST=") { do_post = $1; sub("POST=", "", do_post); next; } if ($1 == "CONTEXT") { # CONTEXT require PRE and POST handlers. if (do_pre == "" || do_post == "") next; is_context=1; have_context=1; } else { if (have_context) { # CONTEXT members must come at the end of # the args structure, so everything else # is ignored. next; } argdir[argc] = $1; } i=2; if (is_context == 0) { if ($2 == "LOCKED=EXCL") { lockstate[argc] = "elocked"; i++; } else if ($2 == "LOCKED=YES") { lockstate[argc] = "locked"; i++; } else if ($2 == "LOCKED=NO") { lockstate[argc] = "unlocked"; i++; } else lockstate[argc] = ""; if ($2 == "WILLRELE" || $3 == "WILLRELE") { willrele[argc] = 1; i++; } else if ($2 == "WILLPUT" || $3 == "WILLPUT") { willrele[argc] = 3; i++; } else willrele[argc] = 0; if ($2 == "WILLMAKE") { willmake=argc; i++; } if (argc == 0 && fstrans == "") { if (lockstate[0] == "locked" || lockstate[0] == "elocked") fstrans = "NO"; else fstrans = "YES"; } } # XXX: replace non-portable types for rump. We should really # nuke the types from the kernel, but that is a battle for # another day. at = $i; if (rump) { if (at == "vm_prot_t") at = "int"; if (at == "voff_t") at = "off_t"; if (at == "kauth_cred_t") at = "struct kauth_cred *" if (at == "daddr_t") at = "int64_t" } argtype[argc + ncontext] = at; i++; while (i < NF) { argtype[argc + ncontext] = argtype[argc + ncontext]" "$i; i++; } argname[argc + ncontext] = $i; if (is_context) ncontext++; else argc++; next; } ' # This is put before the copyright on each generated file. warning="\ /* @NetBSD@ */ /* * Warning: DO NOT EDIT! This file is automatically generated! * (Modifications made here may easily be lost!) * * Created from the file: * ${SRC_ID} * by the script: * ${SCRIPT_ID} */ " # This is to satisfy McKusick (get rid of evil spaces 8^) anal_retentive='s:\([^/]\*\) :\1:g' do_hfile () { # # Redirect stdout to the H file. # echo "$0: Creating $1" 1>&2 exec > $1 rump=$2 # Begin stuff if [ -z "${rump}" ]; then SYS='SYS_' else SYS='RUMP_RUMP' fi echo -n "$warning" | sed -e 's/\$//g;s/@/\$/g;s/ $//' echo "" echo -n "$copyright" echo '' echo "#ifndef _${SYS}VNODE_IF_H_" echo "#define _${SYS}VNODE_IF_H_" [ -z "${rump}" ] && echo " extern const struct vnodeop_desc ${rump}vop_default_desc;" echo # Body stuff # This awk program needs toupper() so define it if necessary. sed -e "$sed_prep" $src | $awk -v rump=${rump} "$toupper"' function doit() { name = rump name # Declare arg struct, descriptor. if (!rump) { printf("\n#define %s_DESCOFFSET %d\n", toupper(name), vop_offset++); printf("struct %s_args {\n", args_name); printf("\tconst struct vnodeop_desc * a_desc;\n"); for (i=0; i 77) { protoarg = ("\n " protoarg); arglen += 4; protolen = 0; } printf("%s", protoarg); protolen += arglen; } printf(");\n"); } BEGIN { vop_offset = 1; # start at 1, to count the 'default' op printf("struct buf;\n"); if (rump) { printf("struct flock;\n"); printf("struct knote;\n"); printf("struct vm_page;\n"); printf("struct acl;\n"); printf("\n#include \n"); } printf("\n#ifndef _KERNEL\n#include \n#endif\n"); if (rump) printf("\n"); } END { if (!rump) { printf("\n#define VNODE_OPS_COUNT\t%d\n", vop_offset); } } '"$awk_parser" | sed -e "$anal_retentive" # End stuff echo '' echo "#endif /* !_${SYS}VNODE_IF_H_ */" } do_hfile $out_h '' do_hfile $out_rumph 'rump_' do_cfile () { # # Redirect stdout to the C file. # echo "$0: Creating $1" 1>&2 exec > $1 rump=$2 # Begin stuff echo -n "$warning" | sed -e 's/\$//g;s/@/\$/g;s/ $//' echo "" echo -n "$copyright" echo " #include __KERNEL_RCSID(0, \"\$NetBSD\$\");" if [ -z "${rump}" -a ${lockdebug} -ne 0 ] ; then echo '' echo '#ifdef _KERNEL_OPT' echo '#include "opt_vnode_lockdebug.h"' echo '#endif /* _KERNEL_OPT */' fi echo ' #include #include #include #include #include #include ' [ -z "${rump}" ] && echo '#include ' [ ! -z "${rump}" ] && echo '#include ' \ && echo '#include ' if [ -z "${rump}" ] ; then echo " #include enum fst_op { FST_NO, FST_YES, FST_LAZY, FST_TRY }; static inline int vop_pre(vnode_t *vp, struct mount **mp, bool *mpsafe, enum fst_op op) { int error; *mpsafe = (vp->v_vflag & VV_MPSAFE); if (!*mpsafe) { KERNEL_LOCK(1, curlwp); } if (op == FST_YES || op == FST_LAZY || op == FST_TRY) { for (;;) { *mp = vp->v_mount; if (op == FST_TRY) { error = fstrans_start_nowait(*mp); if (error) { if (!*mpsafe) { KERNEL_UNLOCK_ONE(curlwp); } return error; } } else if (op == FST_LAZY) { fstrans_start_lazy(*mp); } else { fstrans_start(*mp); } if (__predict_true(*mp == vp->v_mount)) break; fstrans_done(*mp); } } else { *mp = vp->v_mount; } return 0; } static inline u_quad_t vop_pre_get_size(struct vnode *vp) { mutex_enter(vp->v_interlock); KASSERT(vp->v_size != VSIZENOTSET); u_quad_t rv = (u_quad_t)vp->v_size; mutex_exit(vp->v_interlock); return rv; } /* * VOP_RMDIR(), VOP_REMOVE(), and VOP_RENAME() need special handling * because they each drop the caller's references on one or more of * their arguments. While there must be an open file descriptor in * associated with a vnode in order for knotes to be attached to it, * that status could change during the course of the operation. So, * for the vnode arguments that are WILLRELE or WILLPUT, we check * pre-op if there are registered knotes, take a hold count if so, * and post-op release the hold after activating any knotes still * associated with the vnode. */ #define VOP_POST_KNOTE(thisvp, e, n) \\ do { \\ if (__predict_true((e) == 0)) { \\ /* \\ * VN_KNOTE() does the VN_KEVENT_INTEREST() \\ * check for us. \\ */ \\ VN_KNOTE((thisvp), (n)); \\ } \\ } while (/*CONSTCOND*/0) #define VOP_POST_KNOTE_HELD(thisvp, e, n) \\ do { \\ /* \\ * We don't perform a VN_KEVENT_INTEREST() check here; it \\ * was already performed when we did the pre-op work that \\ * caused the vnode to be held in the first place. \\ */ \\ mutex_enter((thisvp)->v_interlock); \\ if (__predict_true((e) == 0)) { \\ knote(&(thisvp)->v_klist->vk_klist, (n)); \\ } \\ holdrelel((thisvp)); \\ mutex_exit((thisvp)->v_interlock); \\ /* \\ * thisvp might be gone now! Don't touch! \\ */ \\ } while (/*CONSTCOND*/0) #define vop_create_post(ap, e) \\ VOP_POST_KNOTE((ap)->a_dvp, (e), NOTE_WRITE) #define vop_mknod_post(ap, e) \\ VOP_POST_KNOTE((ap)->a_dvp, (e), NOTE_WRITE) #define vop_setattr_pre(ap) \\ u_quad_t osize = 0; \\ long vp_events = \\ VN_KEVENT_INTEREST((ap)->a_vp, NOTE_ATTRIB | NOTE_EXTEND) \\ ? NOTE_ATTRIB : 0; \\ bool check_extend = false; \\ if (__predict_false(vp_events != 0 && \\ (ap)->a_vap->va_size != VNOVALSIZE)) { \\ check_extend = true; \\ osize = vop_pre_get_size((ap)->a_vp); \\ } #define vop_setattr_post(ap, e) \\ do { \\ if (__predict_false(vp_events != 0)) { \\ if (__predict_false(check_extend && \\ (ap)->a_vap->va_size > osize)) { \\ vp_events |= NOTE_EXTEND; \\ } \\ VOP_POST_KNOTE((ap)->a_vp, (e), vp_events); \\ } \\ } while (/*CONSTCOND*/0) #define vop_setacl_post(ap, e) \\ VOP_POST_KNOTE((ap)->a_vp, (e), NOTE_ATTRIB) #define vop_link_post(ap, e) \\ do { \\ VOP_POST_KNOTE((ap)->a_dvp, (e), NOTE_WRITE); \\ VOP_POST_KNOTE((ap)->a_vp, (e), NOTE_LINK); \\ } while (/*CONSTCOND*/0) #define vop_mkdir_post(ap, e) \\ VOP_POST_KNOTE((ap)->a_dvp, (e), NOTE_WRITE | NOTE_LINK) #define vop_remove_pre_common(ap) \\ bool post_event_vp = \\ VN_KEVENT_INTEREST((ap)->a_vp, NOTE_DELETE | NOTE_LINK); \\ if (__predict_false(post_event_vp)) { \\ vhold((ap)->a_vp); \\ } #define vop_remove_post_common(ap, e, dn, lc) \\ do { \\ VOP_POST_KNOTE((ap)->a_dvp, (e), (dn)); \\ if (__predict_false(post_event_vp)) { \\ VOP_POST_KNOTE_HELD((ap)->a_vp, (e), \\ (lc) ? NOTE_LINK : NOTE_DELETE); \\ } \\ } while (/*CONSTCOND*/0) /* * One could make the argument that VOP_REMOVE() should send NOTE_LINK * on vp if the resulting link count is not zero, but that's not what * the documentation says. * * We could change this easily by passing ap->ctx_vp_new_nlink to * vop_remove_post_common(). */ #define vop_remove_pre(ap) \\ vop_remove_pre_common((ap)); \\ /* \\ * We will assume that the file being removed is deleted unless \\ * the file system tells us otherwise by updating vp_new_nlink. \\ */ \\ (ap)->ctx_vp_new_nlink = 0; #define vop_remove_post(ap, e) \\ vop_remove_post_common((ap), (e), NOTE_WRITE, 0) #define vop_rmdir_pre(ap) \\ vop_remove_pre_common(ap) #define vop_rmdir_post(ap, e) \\ vop_remove_post_common((ap), (e), NOTE_WRITE | NOTE_LINK, 0) #define vop_symlink_post(ap, e) \\ VOP_POST_KNOTE((ap)->a_dvp, (e), NOTE_WRITE) #define vop_open_post(ap, e) \\ VOP_POST_KNOTE((ap)->a_vp, (e), NOTE_OPEN) #define vop_close_post(ap, e) \\ do { \\ /* See the definition of VN_KNOTE() in . */ \\ if (__predict_false(VN_KEVENT_INTEREST((ap)->a_vp, \\ NOTE_CLOSE_WRITE | NOTE_CLOSE) && (e) == 0)) { \\ struct vnode *thisvp = (ap)->a_vp; \\ mutex_enter(thisvp->v_interlock); \\ /* \\ * Don't send NOTE_CLOSE when closing a vnode that's \\ * been reclaimed or otherwise revoked; a NOTE_REVOKE \\ * has already been sent, and this close is effectively \\ * meaningless from the watcher's perspective. \\ */ \\ if (__predict_true(thisvp->v_op != dead_vnodeop_p)) { \\ knote(&thisvp->v_klist->vk_klist, \\ ((ap)->a_fflag & FWRITE) \\ ? NOTE_CLOSE_WRITE : NOTE_CLOSE); \\ } \\ mutex_exit(thisvp->v_interlock); \\ } \\ } while (/*CONSTCOND*/0) #define vop_read_post(ap, e) \\ VOP_POST_KNOTE((ap)->a_vp, (e), NOTE_READ) #define vop_write_pre(ap) \\ off_t ooffset = 0, noffset = 0; \\ u_quad_t osize = 0; \\ long vp_events = \\ VN_KEVENT_INTEREST((ap)->a_vp, NOTE_WRITE | NOTE_EXTEND) \\ ? NOTE_WRITE : 0; \\ if (__predict_false(vp_events != 0)) { \\ ooffset = (ap)->a_uio->uio_offset; \\ osize = vop_pre_get_size((ap)->a_vp); \\ } #define vop_write_post(ap, e) \\ do { \\ /* \\ * If any data was written, we'll post an event, even if \\ * there was an error. \\ */ \\ noffset = (ap)->a_uio->uio_offset; \\ if (__predict_false(vp_events != 0 && noffset > ooffset)) { \\ if (noffset > osize) { \\ vp_events |= NOTE_EXTEND; \\ } \\ VN_KNOTE((ap)->a_vp, vp_events); \\ } \\ } while (/*CONSTCOND*/0) static inline void vop_post(vnode_t *vp, struct mount *mp, bool mpsafe, enum fst_op op) { if (op == FST_YES || op == FST_LAZY) { fstrans_done(mp); } if (!mpsafe) { KERNEL_UNLOCK_ONE(curlwp); } } static inline void assert_vop_unlocked(vnode_t *vp, const char *str) { #if defined(VNODE_LOCKDEBUG) if (VOP_ISLOCKED(vp) == LK_EXCLUSIVE) panic(\"%s: %p %d/%d is locked but should not be\", str, vp, vp->v_tag, vp->v_type); #endif } static inline void assert_vop_locked(vnode_t *vp, const char *str) { #if defined(VNODE_LOCKDEBUG) if (VOP_ISLOCKED(vp) == LK_NONE) panic(\"%s: %p %d/%d is not locked but should be\", str, vp, vp->v_tag, vp->v_type); #endif } static inline void assert_vop_elocked(vnode_t *vp, const char *str) { #if defined(VNODE_LOCKDEBUG) if (VOP_ISLOCKED(vp) != LK_EXCLUSIVE) panic(\"%s: %p %d/%d is not exclusive locked but should be\", str, vp, vp->v_tag, vp->v_type); #endif } const struct vnodeop_desc vop_default_desc = {" echo ' 0, "default", 0, NULL, VDESC_NO_OFFSET, VDESC_NO_OFFSET, VDESC_NO_OFFSET, }; ' fi # Body stuff sed -e "$sed_prep" $src | $awk -v rump=${rump} -v lockdebug=${lockdebug} ' function do_offset(typematch) { for (i=0; iv_size != VSIZENOTSET\n" \ "\t\t && (*%s)->v_writesize != VSIZENOTSET);\n", argname[willmake], argname[willmake]); printf("#endif /* DIAGNOSTIC */\n"); } printf("\treturn error;\n}\n"); } function doit() { printf("\n"); if (!rump) offsets(); if (rump) extname = "RUMP_" toupper(name); else extname = toupper(name); # Define function. printf("int\n%s(", extname); for (i=0; i