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