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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
  24  * Copyright 2015 Joyent, Inc.
  25  */
  26 
  27 #include <smbsrv/smb_kproto.h>
  28 #include <smbsrv/smb_fsops.h>
  29 #include <sys/sdt.h>
  30 #include <sys/fcntl.h>
  31 #include <sys/vfs.h>
  32 #include <sys/vfs_opreg.h>
  33 #include <sys/vnode.h>
  34 #include <sys/fem.h>
  35 
  36 extern caller_context_t smb_ct;
  37 
  38 static boolean_t        smb_fem_initialized = B_FALSE;
  39 static fem_t            *smb_fcn_ops = NULL;
  40 static fem_t            *smb_oplock_ops = NULL;
  41 
  42 /*
  43  * Declarations for FCN (file change notification) FEM monitors
  44  */
  45 
  46 static int smb_fem_fcn_create(femarg_t *, char *, vattr_t *, vcexcl_t, int,
  47     vnode_t **, cred_t *, int, caller_context_t *, vsecattr_t *);
  48 static int smb_fem_fcn_remove(femarg_t *, char *, cred_t *,
  49     caller_context_t *, int);
  50 static int smb_fem_fcn_rename(femarg_t *, char *, vnode_t *, char *,
  51     cred_t *, caller_context_t *, int);
  52 static int smb_fem_fcn_mkdir(femarg_t *, char *, vattr_t *, vnode_t **,
  53     cred_t *, caller_context_t *, int, vsecattr_t *);
  54 static int smb_fem_fcn_rmdir(femarg_t *, char *, vnode_t *, cred_t *,
  55     caller_context_t *, int);
  56 static int smb_fem_fcn_link(femarg_t *, vnode_t *, char *, cred_t *,
  57     caller_context_t *, int);
  58 static int smb_fem_fcn_symlink(femarg_t *, char *, vattr_t *,
  59     char *, cred_t *, caller_context_t *, int);
  60 
  61 static const fs_operation_def_t smb_fcn_tmpl[] = {
  62         VOPNAME_CREATE, { .femop_create = smb_fem_fcn_create },
  63         VOPNAME_REMOVE, {.femop_remove = smb_fem_fcn_remove},
  64         VOPNAME_RENAME, {.femop_rename = smb_fem_fcn_rename},
  65         VOPNAME_MKDIR, {.femop_mkdir = smb_fem_fcn_mkdir},
  66         VOPNAME_RMDIR, {.femop_rmdir = smb_fem_fcn_rmdir},
  67         VOPNAME_LINK, {.femop_link = smb_fem_fcn_link},
  68         VOPNAME_SYMLINK, {.femop_symlink = smb_fem_fcn_symlink},
  69         NULL, NULL
  70 };
  71 
  72 /*
  73  * Declarations for oplock FEM monitors
  74  */
  75 
  76 static int smb_fem_oplock_open(femarg_t *, int, cred_t *,
  77     struct caller_context *);
  78 static int smb_fem_oplock_read(femarg_t *, uio_t *, int, cred_t *,
  79     struct caller_context *);
  80 static int smb_fem_oplock_write(femarg_t *, uio_t *, int, cred_t *,
  81     struct caller_context *);
  82 static int smb_fem_oplock_setattr(femarg_t *, vattr_t *, int, cred_t *,
  83     caller_context_t *);
  84 static int smb_fem_oplock_rwlock(femarg_t *, int, caller_context_t *);
  85 static int smb_fem_oplock_space(femarg_t *, int, flock64_t *, int,
  86     offset_t, cred_t *, caller_context_t *);
  87 static int smb_fem_oplock_vnevent(femarg_t *, vnevent_t, vnode_t *, char *,
  88     caller_context_t *);
  89 
  90 static const fs_operation_def_t smb_oplock_tmpl[] = {
  91         VOPNAME_OPEN,   { .femop_open = smb_fem_oplock_open },
  92         VOPNAME_READ,   { .femop_read = smb_fem_oplock_read },
  93         VOPNAME_WRITE,  { .femop_write = smb_fem_oplock_write },
  94         VOPNAME_SETATTR, { .femop_setattr = smb_fem_oplock_setattr },
  95         VOPNAME_RWLOCK, { .femop_rwlock = smb_fem_oplock_rwlock },
  96         VOPNAME_SPACE,  { .femop_space = smb_fem_oplock_space },
  97         VOPNAME_VNEVENT, { .femop_vnevent = smb_fem_oplock_vnevent },
  98         NULL, NULL
  99 };
 100 
 101 static int smb_fem_oplock_break(femarg_t *, caller_context_t *, uint32_t);
 102 
 103 /*
 104  * smb_fem_init
 105  *
 106  * This function is not multi-thread safe. The caller must make sure only one
 107  * thread makes the call.
 108  */
 109 int
 110 smb_fem_init(void)
 111 {
 112         int     rc = 0;
 113 
 114         if (smb_fem_initialized)
 115                 return (0);
 116 
 117         rc = fem_create("smb_fcn_ops", smb_fcn_tmpl, &smb_fcn_ops);
 118         if (rc)
 119                 return (rc);
 120 
 121         rc = fem_create("smb_oplock_ops", smb_oplock_tmpl,
 122             &smb_oplock_ops);
 123 
 124         if (rc) {
 125                 fem_free(smb_fcn_ops);
 126                 smb_fcn_ops = NULL;
 127                 return (rc);
 128         }
 129 
 130         smb_fem_initialized = B_TRUE;
 131 
 132         return (0);
 133 }
 134 
 135 /*
 136  * smb_fem_fini
 137  *
 138  * This function is not multi-thread safe. The caller must make sure only one
 139  * thread makes the call.
 140  */
 141 void
 142 smb_fem_fini(void)
 143 {
 144         if (!smb_fem_initialized)
 145                 return;
 146 
 147         if (smb_fcn_ops != NULL) {
 148                 fem_free(smb_fcn_ops);
 149                 smb_fcn_ops = NULL;
 150         }
 151         if (smb_oplock_ops != NULL) {
 152                 fem_free(smb_oplock_ops);
 153                 smb_oplock_ops = NULL;
 154         }
 155         smb_fem_initialized = B_FALSE;
 156 }
 157 
 158 int
 159 smb_fem_fcn_install(smb_node_t *node)
 160 {
 161         int rc;
 162 
 163         if (smb_fcn_ops == NULL)
 164                 return (ENOSYS);
 165         rc = fem_install(node->vp, smb_fcn_ops, (void *)node, OPARGUNIQ,
 166             (fem_func_t)smb_node_ref, (fem_func_t)smb_node_release);
 167         return (rc);
 168 }
 169 
 170 void
 171 smb_fem_fcn_uninstall(smb_node_t *node)
 172 {
 173         if (smb_fcn_ops == NULL)
 174                 return;
 175         VERIFY0(fem_uninstall(node->vp, smb_fcn_ops, (void *)node));
 176 }
 177 
 178 int
 179 smb_fem_oplock_install(smb_node_t *node)
 180 {
 181         int rc;
 182 
 183         if (smb_oplock_ops == NULL)
 184                 return (ENOSYS);
 185         rc = fem_install(node->vp, smb_oplock_ops, (void *)node, OPARGUNIQ,
 186             (fem_func_t)smb_node_ref, (fem_func_t)smb_node_release);
 187         return (rc);
 188 }
 189 
 190 void
 191 smb_fem_oplock_uninstall(smb_node_t *node)
 192 {
 193         if (smb_oplock_ops == NULL)
 194                 return;
 195         VERIFY0(fem_uninstall(node->vp, smb_oplock_ops, (void *)node));
 196 }
 197 
 198 /*
 199  * FEM FCN monitors
 200  *
 201  * The FCN monitors intercept the respective VOP_* call regardless
 202  * of whether the call originates from CIFS, NFS, or a local process.
 203  */
 204 
 205 /*
 206  * smb_fem_fcn_create()
 207  *
 208  * This monitor will catch only changes to VREG files and not to extended
 209  * attribute files.  This is fine because, for CIFS files, stream creates
 210  * should not trigger any file change notification on the VDIR directory
 211  * being monitored.  Creates of any other kind of extended attribute in
 212  * the directory will also not trigger any file change notification on the
 213  * VDIR directory being monitored.
 214  */
 215 
 216 static int
 217 smb_fem_fcn_create(
 218     femarg_t *arg,
 219     char *name,
 220     vattr_t *vap,
 221     vcexcl_t excl,
 222     int mode,
 223     vnode_t **vpp,
 224     cred_t *cr,
 225     int flag,
 226     caller_context_t *ct,
 227     vsecattr_t *vsecp)
 228 {
 229         smb_node_t *dnode;
 230         int error;
 231 
 232         dnode = (smb_node_t *)arg->fa_fnode->fn_available;
 233 
 234         ASSERT(dnode);
 235 
 236         error = vnext_create(arg, name, vap, excl, mode, vpp, cr, flag,
 237             ct, vsecp);
 238 
 239         if (error == 0 && ct != &smb_ct)
 240                 smb_node_notify_change(dnode, FILE_ACTION_ADDED, name);
 241 
 242         return (error);
 243 }
 244 
 245 /*
 246  * smb_fem_fcn_remove()
 247  *
 248  * This monitor will catch only changes to VREG files and to not extended
 249  * attribute files.  This is fine because, for CIFS files, stream deletes
 250  * should not trigger any file change notification on the VDIR directory
 251  * being monitored.  Deletes of any other kind of extended attribute in
 252  * the directory will also not trigger any file change notification on the
 253  * VDIR directory being monitored.
 254  */
 255 
 256 static int
 257 smb_fem_fcn_remove(
 258     femarg_t *arg,
 259     char *name,
 260     cred_t *cr,
 261     caller_context_t *ct,
 262     int flags)
 263 {
 264         smb_node_t *dnode;
 265         int error;
 266 
 267         dnode = (smb_node_t *)arg->fa_fnode->fn_available;
 268 
 269         ASSERT(dnode);
 270 
 271         error = vnext_remove(arg, name, cr, ct, flags);
 272 
 273         if (error == 0 && ct != &smb_ct)
 274                 smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name);
 275 
 276         return (error);
 277 }
 278 
 279 static int
 280 smb_fem_fcn_rename(
 281     femarg_t *arg,
 282     char *snm,
 283     vnode_t *tdvp,
 284     char *tnm,
 285     cred_t *cr,
 286     caller_context_t *ct,
 287     int flags)
 288 {
 289         smb_node_t *dnode;
 290         int error;
 291 
 292         dnode = (smb_node_t *)arg->fa_fnode->fn_available;
 293 
 294         ASSERT(dnode);
 295 
 296         error = vnext_rename(arg, snm, tdvp, tnm, cr, ct, flags);
 297 
 298         if (error == 0 && ct != &smb_ct) {
 299                 /*
 300                  * Note that renames in the same directory are normally
 301                  * delivered in {old,new} pairs, and clients expect them
 302                  * in that order, if both events are delivered.
 303                  */
 304                 smb_node_notify_change(dnode,
 305                     FILE_ACTION_RENAMED_OLD_NAME, snm);
 306                 smb_node_notify_change(dnode,
 307                     FILE_ACTION_RENAMED_NEW_NAME, tnm);
 308         }
 309 
 310         return (error);
 311 }
 312 
 313 static int
 314 smb_fem_fcn_mkdir(
 315     femarg_t *arg,
 316     char *name,
 317     vattr_t *vap,
 318     vnode_t **vpp,
 319     cred_t *cr,
 320     caller_context_t *ct,
 321     int flags,
 322     vsecattr_t *vsecp)
 323 {
 324         smb_node_t *dnode;
 325         int error;
 326 
 327         dnode = (smb_node_t *)arg->fa_fnode->fn_available;
 328 
 329         ASSERT(dnode);
 330 
 331         error = vnext_mkdir(arg, name, vap, vpp, cr, ct, flags, vsecp);
 332 
 333         if (error == 0 && ct != &smb_ct)
 334                 smb_node_notify_change(dnode, FILE_ACTION_ADDED, name);
 335 
 336         return (error);
 337 }
 338 
 339 static int
 340 smb_fem_fcn_rmdir(
 341     femarg_t *arg,
 342     char *name,
 343     vnode_t *cdir,
 344     cred_t *cr,
 345     caller_context_t *ct,
 346     int flags)
 347 {
 348         smb_node_t *dnode;
 349         int error;
 350 
 351         dnode = (smb_node_t *)arg->fa_fnode->fn_available;
 352 
 353         ASSERT(dnode);
 354 
 355         error = vnext_rmdir(arg, name, cdir, cr, ct, flags);
 356 
 357         if (error == 0 && ct != &smb_ct)
 358                 smb_node_notify_change(dnode, FILE_ACTION_REMOVED, name);
 359 
 360         return (error);
 361 }
 362 
 363 static int
 364 smb_fem_fcn_link(
 365     femarg_t *arg,
 366     vnode_t *svp,
 367     char *tnm,
 368     cred_t *cr,
 369     caller_context_t *ct,
 370     int flags)
 371 {
 372         smb_node_t *dnode;
 373         int error;
 374 
 375         dnode = (smb_node_t *)arg->fa_fnode->fn_available;
 376 
 377         ASSERT(dnode);
 378 
 379         error = vnext_link(arg, svp, tnm, cr, ct, flags);
 380 
 381         if (error == 0 && ct != &smb_ct)
 382                 smb_node_notify_change(dnode, FILE_ACTION_ADDED, tnm);
 383 
 384         return (error);
 385 }
 386 
 387 static int
 388 smb_fem_fcn_symlink(
 389     femarg_t *arg,
 390     char *linkname,
 391     vattr_t *vap,
 392     char *target,
 393     cred_t *cr,
 394     caller_context_t *ct,
 395     int flags)
 396 {
 397         smb_node_t *dnode;
 398         int error;
 399 
 400         dnode = (smb_node_t *)arg->fa_fnode->fn_available;
 401 
 402         ASSERT(dnode);
 403 
 404         error = vnext_symlink(arg, linkname, vap, target, cr, ct, flags);
 405 
 406         if (error == 0 && ct != &smb_ct)
 407                 smb_node_notify_change(dnode, FILE_ACTION_ADDED, linkname);
 408 
 409         return (error);
 410 }
 411 
 412 /*
 413  * FEM oplock monitors
 414  *
 415  * The monitors below are not intended to intercept CIFS calls.
 416  * CIFS higher-level routines will break oplocks as needed prior
 417  * to getting to the VFS layer.
 418  */
 419 static int
 420 smb_fem_oplock_open(
 421     femarg_t            *arg,
 422     int                 mode,
 423     cred_t              *cr,
 424     caller_context_t    *ct)
 425 {
 426         uint32_t        flags;
 427         int             rc = 0;
 428 
 429         if (ct != &smb_ct) {
 430                 if (mode & (FWRITE|FTRUNC))
 431                         flags = SMB_OPLOCK_BREAK_TO_NONE;
 432                 else
 433                         flags = SMB_OPLOCK_BREAK_TO_LEVEL_II;
 434                 rc = smb_fem_oplock_break(arg, ct, flags);
 435         }
 436         if (rc == 0)
 437                 rc = vnext_open(arg, mode, cr, ct);
 438 
 439         return (rc);
 440 }
 441 
 442 /*
 443  * Should normally be hit only via NFSv2/v3.  All other accesses
 444  * (CIFS/NFS/local) should call VOP_OPEN first.
 445  */
 446 
 447 static int
 448 smb_fem_oplock_read(
 449     femarg_t            *arg,
 450     uio_t               *uiop,
 451     int                 ioflag,
 452     cred_t              *cr,
 453     caller_context_t    *ct)
 454 {
 455         int     rc = 0;
 456 
 457         if (ct != &smb_ct) {
 458                 rc = smb_fem_oplock_break(arg, ct,
 459                     SMB_OPLOCK_BREAK_TO_LEVEL_II);
 460         }
 461         if (rc == 0)
 462                 rc = vnext_read(arg, uiop, ioflag, cr, ct);
 463 
 464         return (rc);
 465 }
 466 
 467 /*
 468  * Should normally be hit only via NFSv2/v3.  All other accesses
 469  * (CIFS/NFS/local) should call VOP_OPEN first.
 470  */
 471 
 472 static int
 473 smb_fem_oplock_write(
 474     femarg_t            *arg,
 475     uio_t               *uiop,
 476     int                 ioflag,
 477     cred_t              *cr,
 478     caller_context_t    *ct)
 479 {
 480         int     rc = 0;
 481 
 482         if (ct != &smb_ct)
 483                 rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE);
 484         if (rc == 0)
 485                 rc = vnext_write(arg, uiop, ioflag, cr, ct);
 486 
 487         return (rc);
 488 }
 489 
 490 static int
 491 smb_fem_oplock_setattr(
 492     femarg_t            *arg,
 493     vattr_t             *vap,
 494     int                 flags,
 495     cred_t              *cr,
 496     caller_context_t    *ct)
 497 {
 498         int     rc = 0;
 499 
 500         if (ct != &smb_ct && (vap->va_mask & AT_SIZE) != 0)
 501                 rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE);
 502         if (rc == 0)
 503                 rc = vnext_setattr(arg, vap, flags, cr, ct);
 504         return (rc);
 505 }
 506 
 507 static int
 508 smb_fem_oplock_rwlock(
 509     femarg_t            *arg,
 510     int                 write_lock,
 511     caller_context_t    *ct)
 512 {
 513         uint32_t        flags;
 514         int             rc = 0;
 515 
 516         if (ct != &smb_ct) {
 517                 if (write_lock)
 518                         flags = SMB_OPLOCK_BREAK_TO_NONE;
 519                 else
 520                         flags = SMB_OPLOCK_BREAK_TO_LEVEL_II;
 521                 rc = smb_fem_oplock_break(arg, ct, flags);
 522         }
 523         if (rc == 0)
 524                 rc = vnext_rwlock(arg, write_lock, ct);
 525 
 526         return (rc);
 527 }
 528 
 529 static int
 530 smb_fem_oplock_space(
 531     femarg_t            *arg,
 532     int                 cmd,
 533     flock64_t           *bfp,
 534     int                 flag,
 535     offset_t            offset,
 536     cred_t              *cr,
 537     caller_context_t    *ct)
 538 {
 539         int     rc = 0;
 540 
 541         if (ct != &smb_ct)
 542                 rc = smb_fem_oplock_break(arg, ct, SMB_OPLOCK_BREAK_TO_NONE);
 543         if (rc == 0)
 544                 rc = vnext_space(arg, cmd, bfp, flag, offset, cr, ct);
 545         return (rc);
 546 }
 547 
 548 /*
 549  * smb_fem_oplock_vnevent()
 550  *
 551  * To intercept NFS and local renames and removes in order to break any
 552  * existing oplock prior to the operation.
 553  *
 554  * Note: Currently, this monitor is traversed only when an FS is mounted
 555  * non-nbmand.  (When the FS is mounted nbmand, share reservation checking
 556  * will detect a share violation and return an error prior to the VOP layer
 557  * being reached.)  Thus, for nbmand NFS and local renames and removes,
 558  * an existing oplock is never broken prior to share checking (contrary to
 559  * how it is with intra-CIFS remove and rename requests).
 560  */
 561 
 562 static int
 563 smb_fem_oplock_vnevent(
 564     femarg_t            *arg,
 565     vnevent_t           vnevent,
 566     vnode_t             *dvp,
 567     char                *name,
 568     caller_context_t    *ct)
 569 {
 570         uint32_t        flags;
 571         int             rc = 0;
 572 
 573         if (ct != &smb_ct) {
 574                 switch (vnevent) {
 575                 case VE_REMOVE:
 576                 case VE_PRE_RENAME_DEST:
 577                 case VE_RENAME_DEST:
 578                         flags = SMB_OPLOCK_BREAK_TO_NONE |
 579                             SMB_OPLOCK_BREAK_BATCH;
 580                         rc = smb_fem_oplock_break(arg, ct, flags);
 581                         break;
 582                 case VE_PRE_RENAME_SRC:
 583                 case VE_RENAME_SRC:
 584                         flags = SMB_OPLOCK_BREAK_TO_LEVEL_II |
 585                             SMB_OPLOCK_BREAK_BATCH;
 586                         rc = smb_fem_oplock_break(arg, ct, flags);
 587                         break;
 588                 default:
 589                         rc = 0;
 590                         break;
 591                 }
 592         }
 593         if (rc == 0)
 594                 rc = vnext_vnevent(arg, vnevent, dvp, name, ct);
 595 
 596         return (rc);
 597 }
 598 
 599 static int
 600 smb_fem_oplock_break(femarg_t *arg, caller_context_t *ct, uint32_t flags)
 601 {
 602         smb_node_t      *node;
 603         int             rc;
 604 
 605         node = (smb_node_t *)((arg)->fa_fnode->fn_available);
 606         SMB_NODE_VALID(node);
 607 
 608         ASSERT(ct != &smb_ct);
 609 
 610         if (ct && (ct->cc_flags & CC_DONTBLOCK)) {
 611                 flags |= SMB_OPLOCK_BREAK_NOWAIT;
 612                 rc = smb_oplock_break(NULL, node, flags);
 613                 if (rc == EAGAIN)
 614                         ct->cc_flags |= CC_WOULDBLOCK;
 615         } else {
 616                 rc = smb_oplock_break(NULL, node, flags);
 617         }
 618 
 619         return (rc);
 620 }