1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 
  27 
  28 #include <sys/types.h>
  29 #include <sys/param.h>
  30 #include <sys/time.h>
  31 #include <sys/cred.h>
  32 #include <sys/vfs.h>
  33 #include <sys/vfs_opreg.h>
  34 #include <sys/gfs.h>
  35 #include <sys/vnode.h>
  36 #include <sys/systm.h>
  37 #include <sys/errno.h>
  38 #include <sys/sysmacros.h>
  39 #include <fs/fs_subr.h>
  40 #include <sys/contract.h>
  41 #include <sys/contract_impl.h>
  42 #include <sys/ctfs.h>
  43 #include <sys/ctfs_impl.h>
  44 #include <sys/file.h>
  45 
  46 /*
  47  * CTFS routines for the /system/contract/<type>/<ctid>/ctl vnode.
  48  * CTFS routines for the /system/contract/<type>/<ctid>/status vnode.
  49  */
  50 
  51 /*
  52  * ctfs_create_ctlnode
  53  *
  54  * If necessary, creates a ctlnode for a ctl file and inserts it into
  55  * the specified cdirnode's gfs_dir_t.  Returns either the existing
  56  * vnode or the new one.
  57  */
  58 vnode_t *
  59 ctfs_create_ctlnode(vnode_t *pvp)
  60 {
  61         ctfs_ctlnode_t *ctlnode;
  62         ctfs_cdirnode_t *cdirnode = pvp->v_data;
  63         vnode_t *vp;
  64 
  65         vp = gfs_file_create(sizeof (ctfs_ctlnode_t), pvp, ctfs_ops_ctl);
  66         ctlnode = vp->v_data;
  67         /*
  68          * We transitively have a hold on the contract through our
  69          * parent directory.
  70          */
  71         ctlnode->ctfs_ctl_contract = cdirnode->ctfs_cn_contract;
  72 
  73         return (vp);
  74 }
  75 
  76 /*
  77  * ctfs_ctl_access - VOP_ACCESS entry point
  78  *
  79  * You only get to access ctl files for contracts you own or were
  80  * abandoned and inherited by your containing process contract.
  81  */
  82 /* ARGSUSED */
  83 static int
  84 ctfs_ctl_access(
  85         vnode_t *vp,
  86         int mode,
  87         int flags,
  88         cred_t *cr,
  89         caller_context_t *cct)
  90 {
  91         ctfs_ctlnode_t *ctlnode = vp->v_data;
  92         contract_t *ct = ctlnode->ctfs_ctl_contract;
  93 
  94         if (mode & (VEXEC | VREAD))
  95                 return (EACCES);
  96 
  97         mutex_enter(&ct->ct_lock);
  98         if ((curproc == ct->ct_owner) ||
  99             (ct->ct_owner == NULL && ct->ct_regent != NULL &&
 100             ct->ct_regent->ct_data == curproc->p_ct_process)) {
 101                 mutex_exit(&ct->ct_lock);
 102                 return (0);
 103         }
 104 
 105         mutex_exit(&ct->ct_lock);
 106         return (EACCES);
 107 }
 108 
 109 /*
 110  * ctfs_ctl_open - VOP_OPEN entry point
 111  *
 112  * Just checks to make sure the mode bits are set, and that the
 113  * constraints imposed by ctfs_ctl_access are met.
 114  */
 115 static int
 116 ctfs_ctl_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
 117 {
 118         if (flag != (FWRITE | FOFFMAX))
 119                 return (EINVAL);
 120 
 121         return (ctfs_ctl_access(*vpp, VWRITE, 0, cr, ct));
 122 }
 123 
 124 /*
 125  * ctfs_ctl_common_getattr
 126  * Implements functionality common to ctl and status ctfs VOP_GETATTR
 127  * entry points. It assumes vp->v_data is set
 128  */
 129 static int
 130 ctfs_ctl_common_getattr(vnode_t *vp, vattr_t *vap)
 131 {
 132         ctfs_ctlnode_t *ctlnode = vp->v_data;
 133 
 134         vap->va_type = VREG;
 135         vap->va_nlink = 1;
 136         vap->va_size = 0;
 137         vap->va_ctime = ctlnode->ctfs_ctl_contract->ct_ctime;
 138         mutex_enter(&ctlnode->ctfs_ctl_contract->ct_events.ctq_lock);
 139         vap->va_atime = vap->va_mtime =
 140             ctlnode->ctfs_ctl_contract->ct_events.ctq_atime;
 141         mutex_exit(&ctlnode->ctfs_ctl_contract->ct_events.ctq_lock);
 142         ctfs_common_getattr(vp, vap);
 143 
 144         return (0);
 145 }
 146 
 147 /*
 148  * ctfs_ctl_getattr - VOP_GETATTR entry point
 149  */
 150 /* ARGSUSED */
 151 static int
 152 ctfs_ctl_getattr(vnode_t *vp, vattr_t *vap, int flags,
 153     cred_t *cr, caller_context_t *ct)
 154 {
 155         vap->va_mode = 0222;
 156 
 157         return (ctfs_ctl_common_getattr(vp, vap));
 158 }
 159 
 160 /*
 161  * ctfs_stat_getattr - VOP_GETATTR entry point
 162  */
 163 /* ARGSUSED */
 164 static int
 165 ctfs_stat_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
 166     caller_context_t *ct)
 167 {
 168         vap->va_mode = 0444;
 169 
 170         return (ctfs_ctl_common_getattr(vp, vap));
 171 }
 172 
 173 /*
 174  * ctfs_ctl_ioctl - VOP_IOCTL entry point
 175  *
 176  * All the ct_ctl_*(3contract) interfaces point here.
 177  */
 178 /* ARGSUSED */
 179 static int
 180 ctfs_ctl_ioctl(
 181         vnode_t *vp,
 182         int cmd,
 183         intptr_t arg,
 184         int flag,
 185         cred_t *cr,
 186         int *rvalp,
 187         caller_context_t *cct)
 188 {
 189         ctfs_ctlnode_t  *ctlnode = vp->v_data;
 190         contract_t      *ct = ctlnode->ctfs_ctl_contract;
 191         int             error = 0;
 192         uint64_t        event;
 193         int             ack;
 194 
 195         switch (cmd) {
 196         case CT_CABANDON:
 197                 error = contract_abandon(ct, curproc, 1);
 198                 break;
 199 
 200         case CT_CACK:
 201         case CT_CNACK:
 202                 if (copyin((void *)arg, &event, sizeof (uint64_t)))
 203                         return (EFAULT);
 204                 ack = (cmd == CT_CACK) ? CT_ACK : CT_NACK;
 205                 error = contract_ack(ct, event, ack);
 206                 break;
 207 
 208         case CT_CNEWCT:
 209                 error = contract_newct(ct);
 210                 break;
 211 
 212         case CT_CQREQ:
 213                 if (copyin((void *)arg, &event, sizeof (uint64_t)))
 214                         return (EFAULT);
 215                 error = contract_qack(ct, event);
 216                 break;
 217 
 218         case CT_CADOPT:
 219                 error = contract_adopt(ct, curproc);
 220                 break;
 221 
 222         default:
 223                 return (EINVAL);
 224         }
 225 
 226         return (error);
 227 }
 228 
 229 const fs_operation_def_t ctfs_tops_ctl[] = {
 230         { VOPNAME_OPEN,         { .vop_open = ctfs_ctl_open } },
 231         { VOPNAME_CLOSE,        { .vop_close = ctfs_close } },
 232         { VOPNAME_IOCTL,        { .vop_ioctl = ctfs_ctl_ioctl } },
 233         { VOPNAME_GETATTR,      { .vop_getattr = ctfs_ctl_getattr } },
 234         { VOPNAME_ACCESS,       { .vop_access = ctfs_ctl_access } },
 235         { VOPNAME_READDIR,      { .error = fs_notdir } },
 236         { VOPNAME_LOOKUP,       { .error = fs_notdir } },
 237         { VOPNAME_INACTIVE,     { .vop_inactive = gfs_vop_inactive } },
 238         { NULL,                 { NULL } }
 239 };
 240 
 241 /*
 242  * ctfs_create_statnode
 243  *
 244  * If necessary, creates a ctlnode for a status file and inserts it
 245  * into the specified cdirnode's gfs_dir_t.  Returns either the
 246  * existing vnode or the new one.
 247  */
 248 vnode_t *
 249 ctfs_create_statnode(vnode_t *pvp)
 250 {
 251         vnode_t *vp;
 252         ctfs_cdirnode_t *cdirnode = pvp->v_data;
 253         ctfs_ctlnode_t *ctlnode;
 254 
 255         vp = gfs_file_create(sizeof (ctfs_ctlnode_t), pvp, ctfs_ops_stat);
 256         ctlnode = vp->v_data;
 257         /*
 258          * We transitively have a hold on the contract through our
 259          * parent directory.
 260          */
 261         ctlnode->ctfs_ctl_contract = cdirnode->ctfs_cn_contract;
 262 
 263         return (vp);
 264 }
 265 
 266 /*
 267  * ctfs_stat_ioctl - VOP_IOCTL entry point
 268  *
 269  * The kernel half of ct_status_read(3contract).
 270  */
 271 /* ARGSUSED */
 272 static int
 273 ctfs_stat_ioctl(
 274         vnode_t *vp,
 275         int cmd,
 276         intptr_t arg,
 277         int flag,
 278         cred_t *cr,
 279         int *rvalp,
 280         caller_context_t *cct)
 281 {
 282         ctfs_ctlnode_t  *statnode = vp->v_data;
 283         contract_t      *ct = statnode->ctfs_ctl_contract;
 284         ct_type_t       *type = ct->ct_type;
 285         STRUCT_DECL(ct_status, st);
 286         nvlist_t        *foo;
 287         char            *bufp = NULL;
 288         size_t          len;
 289         model_t         mdl = get_udatamodel();
 290         uint_t          detail;
 291 
 292         STRUCT_INIT(st, mdl);
 293 
 294         if (cmd != CT_SSTATUS)
 295                 return (EINVAL);
 296 
 297         if (copyin((void *)arg, STRUCT_BUF(st), STRUCT_SIZE(st)))
 298                 return (EFAULT);
 299         detail = STRUCT_FGET(st, ctst_detail);
 300         if (detail == CTD_COMMON) {
 301                 mutex_enter(&ct->ct_lock);
 302                 contract_status_common(ct, VTOZONE(vp), STRUCT_BUF(st), mdl);
 303                 mutex_exit(&ct->ct_lock);
 304         } else if (detail <= CTD_ALL) {
 305                 VERIFY(nvlist_alloc(&foo, NV_UNIQUE_NAME, KM_SLEEP) == 0);
 306                 type->ct_type_ops->contop_status(ct, VTOZONE(vp), detail, foo,
 307                     STRUCT_BUF(st), mdl);
 308                 VERIFY(nvlist_pack(foo, &bufp, &len, NV_ENCODE_NATIVE,
 309                     KM_SLEEP) == 0);
 310                 nvlist_free(foo);
 311 
 312                 if ((len <= STRUCT_FGET(st, ctst_nbytes)) &&
 313                     (copyout(bufp, STRUCT_FGETP(st, ctst_buffer), len) == -1)) {
 314                         kmem_free(bufp, len);
 315                         return (EFAULT);
 316                 }
 317                 kmem_free(bufp, len);
 318                 STRUCT_FSET(st, ctst_nbytes, len);
 319         } else {
 320                 return (EINVAL);
 321         }
 322         if (copyout(STRUCT_BUF(st), (void *)arg, STRUCT_SIZE(st)))
 323                 return (EFAULT);
 324 
 325         return (0);
 326 }
 327 
 328 const fs_operation_def_t ctfs_tops_stat[] = {
 329         { VOPNAME_OPEN,         { .vop_open = ctfs_open } },
 330         { VOPNAME_CLOSE,        { .vop_close = ctfs_close } },
 331         { VOPNAME_IOCTL,        { .vop_ioctl = ctfs_stat_ioctl } },
 332         { VOPNAME_GETATTR,      { .vop_getattr = ctfs_stat_getattr } },
 333         { VOPNAME_ACCESS,       { .vop_access = ctfs_access_readonly } },
 334         { VOPNAME_READDIR,      { .error = fs_notdir } },
 335         { VOPNAME_LOOKUP,       { .error = fs_notdir } },
 336         { VOPNAME_INACTIVE,     { .vop_inactive = gfs_vop_inactive } },
 337         { NULL,                 { NULL } }
 338 };