Print this page
    
3484 enhance and document tail follow support
Reviewed by: Joshua M. Clulow <jmc@joyent.com>
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/fs/portfs/port_fop.c
          +++ new/usr/src/uts/common/fs/portfs/port_fop.c
   1    1  /*
   2    2   * CDDL HEADER START
   3    3   *
   4    4   * The contents of this file are subject to the terms of the
   5    5   * Common Development and Distribution License (the "License").
   6    6   * You may not use this file except in compliance with the License.
   7    7   *
   8    8   * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9    9   * or http://www.opensolaris.org/os/licensing.
  10   10   * See the License for the specific language governing permissions
  11   11   * and limitations under the License.
  12   12   *
  13   13   * When distributing Covered Code, include this CDDL HEADER in each
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  
    | 
      ↓ open down ↓ | 
    15 lines elided | 
    
      ↑ open up ↑ | 
  
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  /*
  22   22   * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23   23   * Use is subject to license terms.
  24   24   */
  25   25  
       26 +/*
       27 + * Copyright (c) 2013, Joyent, Inc. All rights reserved.
       28 + */
  26   29  
  27   30  /*
  28   31   * File Events Notification
  29   32   * ------------------------
  30   33   *
  31   34   * The File Events Notification facility provides file and directory change
  32   35   * notification. It is implemented as an event source(PORT_SOURCE_FILE)
  33   36   * under the Event Ports framework. Therefore the API is an extension to
  34   37   * the Event Ports API.
  35   38   *
  36   39   * It uses the FEM (File Events Monitoring) framework to intercept
  37   40   * operations on the files & directories and generate appropriate events.
  38   41   *
  39   42   * It provides event notification in accordance with what an application
  40   43   * can find out by stat`ing the file and comparing time stamps. The various
  41   44   * system calls that update the file's access, modification, and change
  42   45   * time stamps are documented in the man page section 2.
  43   46   *
  44   47   * It is non intrusive. That is, having an active file event watch on a file
  45   48   * or directory will not prevent it from being removed or renamed or block an
  46   49   * unmount operation of the file system where the watched file or directory
  47   50   * resides.
  48   51   *
  49   52   *
  50   53   * Interface:
  51   54   * ----------
  52   55   *
  53   56   *   The object for this event source is of type 'struct file_obj *'
  54   57   *
  55   58   *   The file that needs to be monitored is specified in 'fo_name'.
  56   59   *   The time stamps collected by a stat(2) call are passed in fo_atime,
  57   60   *   fo_mtime, fo_ctime. At the time a file events watch is registered, the
  58   61   *   time stamps passed in are compared with the current time stamps of the
  59   62   *   file. If it has changed, relevant events are sent immediately. If the time
  60   63   *   stamps are all '0', they will not be compared.
  61   64   *
  62   65   *
  63   66   * The events are delivered to an event port. A port is created using
  64   67   * port_create().
  65   68   *
  66   69   * To register a file events watch on a file or directory.
  67   70   *
  68   71   *   port_associate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj, events, user)
  69   72   *
  70   73   *   'user' is the user pointer to be returned with the event.
  71   74   *
  72   75   * To de-register a file events watch,
  73   76   *
  74   77   *   port_dissociate(int port, PORT_SOURCE_FILE, (uintptr_t)&fobj)
  75   78   *
  76   79   * The events are collected using the port_get()/port_getn() interface. The
  77   80   * event source will be PORT_SOURCE_FILE.
  78   81   *
  79   82   * After an event is delivered, the file events watch gets de-activated. To
  80   83   * receive the next event, the process will have to re-register the watch and
  81   84   * activate it by calling port_associate() again. This behavior is intentional
  82   85   * and supports proper multi threaded programming when using file events
  83   86   * notification API.
  84   87   *
  85   88   *
  86   89   * Implementation overview:
  87   90   * ------------------------
  88   91   *
  89   92   * Each file events watch is represented by 'portfop_t' in the kernel. A
  90   93   * cache(in portfop_cache_t) of these portfop_t's are maintained per event
  91   94   * port by this source. The object here is the pointer to the file_obj
  92   95   * structure. The portfop_t's are hashed in using the object pointer. Therefore
  93   96   * it is possible to have multiple file events watches on a file by the same
  94   97   * process by using different object structure(file_obj_t) and hence can
  95   98   * receive multiple event notification for a file. These watches can be for
  96   99   * different event types.
  97  100   *
  98  101   * The cached entries of these file objects are retained, even after delivering
  99  102   * an event, marking them inactive for performance reasons. The assumption
 100  103   * is that the process would come back and re-register the file to receive
 101  104   * further events. When there are more then 'port_fop_maxpfps' watches per file
 102  105   * it will attempt to free the oldest inactive watches.
 103  106   *
 104  107   * In case the event that is being delivered is an exception event, the cached
 105  108   * entries get removed. An exception event on a file or directory means its
 106  109   * identity got changed(rename to/from, delete, mounted over, file system
 107  110   * unmount).
 108  111   *
 109  112   * If the event port gets closed, all the associated file event watches will be
 110  113   * removed and discarded.
 111  114   *
 112  115   *
 113  116   * Data structures:
 114  117   * ----------------
 115  118   *
 116  119   * The list of file event watches per file are managed by the data structure
 117  120   * portfop_vp_t. The first time a file events watch is registered for a file,
 118  121   * a portfop_vp_t is installed on the vnode_t's member v_fopdata. This gets
 119  122   * removed and freed only when the vnode becomes inactive. The FEM hooks are
 120  123   * also installed when the first watch is registered on a file. The FEM hooks
 121  124   * get un-installed when all the watches are removed.
 122  125   *
 123  126   * Each file events watch is represented by the structure portfop_t. They
 124  127   * get added to a list of portfop_t's on the vnode(portfop_vp_t). After
 125  128   * delivering an event, the portfop_t is marked inactive but retained. It is
 126  129   * moved to the end of the list. All the active portfop_t's are maintained at
 127  130   * the beginning. In case of exception events, the portfop_t will be removed
 128  131   * and discarded.
 129  132   *
 130  133   * To intercept unmount operations, FSEM hooks are added to the file system
 131  134   * under which files are being watched. A hash table('portfop_vfs_hash_t') of
 132  135   * active file systems is maintained. Each file system that has active watches
 133  136   * is represented by 'portfop_vfs_t' and is added to the hash table.
 134  137   * The vnode's 'portfop_vp_t' structure is added to the list of files(vnodes)
 135  138   * being watched on the portfop_vfs_t structure.
 136  139   *
 137  140   *
 138  141   * File system support:
 139  142   * -------------------
 140  143   *
 141  144   * The file system implementation has to provide vnode event notifications
 142  145   * (vnevents) in order to support watching any files on that file system.
 143  146   * The vnode events(vnevents) are notifications provided by the file system
 144  147   * for name based file operations like rename, remove etc, which do not go
 145  148   * thru the VOP_** interfaces. If the file system does not implement vnode
 146  149   * notifications, watching for file events on such file systems is not
 147  150   * supported. The vnode event notifications support is determined by the call
 148  151   * vnevent_support(vp) (VOP_VNEVENT(vp, VE_SUPPORT)), which the file system
 149  152   * has to implement.
 150  153   *
 151  154   *
 152  155   * Locking order:
 153  156   * --------------
 154  157   *
 155  158   * A file(vnode) can have file event watches registered by different processes.
 156  159   * There is one portfop_t per watch registered. These are on the vnode's list
 157  160   * protected by the mutex 'pvp_mutex' in 'portfop_vp_t'. The portfop_t's are
 158  161   * also on the per port cache. The cache is protected by the pfc_lock of
 159  162   * portfop_cache_t. The lock order here is 'pfc_lock' -> 'pvp_mutex'.
 160  163   *
 161  164   */
 162  165  
 163  166  #include <sys/types.h>
 164  167  #include <sys/systm.h>
 165  168  #include <sys/stat.h>
 166  169  #include <sys/errno.h>
 167  170  #include <sys/kmem.h>
 168  171  #include <sys/sysmacros.h>
 169  172  #include <sys/debug.h>
 170  173  #include <sys/vnode.h>
 171  174  #include <sys/poll_impl.h>
 172  175  #include <sys/port_impl.h>
 173  176  #include <sys/fem.h>
 174  177  #include <sys/vfs_opreg.h>
 175  178  #include <sys/atomic.h>
 176  179  #include <sys/mount.h>
 177  180  #include <sys/mntent.h>
 178  181  
 179  182  /*
 180  183   * For special case support of mnttab (/etc/mnttab).
 181  184   */
 182  185  extern struct vnode *vfs_mntdummyvp;
 183  186  extern int mntfstype;
 184  187  
 185  188  #define PORTFOP_PVFSH(vfsp)     (&portvfs_hash[PORTFOP_PVFSHASH(vfsp)])
 186  189  portfop_vfs_hash_t       portvfs_hash[PORTFOP_PVFSHASH_SZ];
 187  190  
 188  191  #define PORTFOP_NVP     20
 189  192  /*
 190  193   * Inactive file event watches(portfop_t) are retained on the vnode's list
 191  194   * for performance reason. If the applications re-registers the file, the
 192  195   * inactive entry is made active and moved up the list.
 193  196   *
 194  197   * If there are greater then the following number of watches on a vnode,
 195  198   * it will attempt to discard an oldest inactive watch(pfp) at the time
 196  199   * a new watch is being registered and when events get delivered. We
 197  200   * do this to avoid accumulating inactive watches on a file.
 198  201   */
 199  202  int     port_fop_maxpfps = 20;
 200  203  
 201  204  /* local functions */
 202  205  static int      port_fop_callback(void *, int *, pid_t, int, void *);
 203  206  
 204  207  static void     port_pcache_insert(portfop_cache_t *, portfop_t *);
 205  208  static void     port_pcache_delete(portfop_cache_t *, portfop_t *);
 206  209  static void     port_close_fop(void *arg, int port, pid_t pid, int lastclose);
 207  210  
 208  211  /*
 209  212   * port fop functions that will be the fem hooks.
 210  213   */
 211  214  static int port_fop_open(femarg_t *vf, int mode, cred_t *cr,
 212  215      caller_context_t *);
 213  216  static int port_fop_read(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
 214  217      struct caller_context *ct);
 215  218  static int port_fop_write(femarg_t *vf, uio_t *uiop, int ioflag, cred_t *cr,
 216  219      caller_context_t *ct);
 217  220  static int port_fop_map(femarg_t *vf, offset_t off, struct as *as,
 218  221      caddr_t *addrp, size_t len, uchar_t prot, uchar_t maxport,
 219  222      uint_t flags, cred_t *cr, caller_context_t *ct);
 220  223  static int port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
 221  224      caller_context_t *ct);
 222  225  static int port_fop_create(femarg_t *vf, char *name, vattr_t *vap,
 223  226      vcexcl_t excl, int mode, vnode_t **vpp, cred_t *cr, int flag,
 224  227      caller_context_t *ct, vsecattr_t *vsecp);
 225  228  static int port_fop_remove(femarg_t *vf, char *nm, cred_t *cr,
 226  229      caller_context_t *ct, int flags);
 227  230  static int port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
 228  231      caller_context_t *ct, int flags);
 229  232  static int port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm,
 230  233      cred_t *cr, caller_context_t *ct, int flags);
 231  234  static int port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap,
 232  235      vnode_t **vpp, cred_t *cr, caller_context_t *ct, int flags,
 233  236      vsecattr_t *vsecp);
 234  237  static int port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
 235  238      caller_context_t *ct, int flags);
 236  239  static int port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
 237  240      caller_context_t *ct, int flags);
 238  241  static int port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap,
 239  242      char *target, cred_t *cr, caller_context_t *ct, int flags);
 240  243  static int port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flag,
 241  244      cred_t *cr, caller_context_t *ct);
 242  245  
 243  246  static int port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp,
 244  247      char *cname, caller_context_t *ct);
 245  248  
 246  249  static int port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr);
 247  250  
 248  251  
 249  252  /*
 250  253   * Fem hooks.
 251  254   */
 252  255  const fs_operation_def_t        port_vnodesrc_template[] = {
 253  256          VOPNAME_OPEN,           { .femop_open = port_fop_open },
 254  257          VOPNAME_READ,           { .femop_read = port_fop_read },
 255  258          VOPNAME_WRITE,          { .femop_write = port_fop_write },
 256  259          VOPNAME_MAP,            { .femop_map = port_fop_map },
 257  260          VOPNAME_SETATTR,        { .femop_setattr = port_fop_setattr },
 258  261          VOPNAME_CREATE,         { .femop_create = port_fop_create },
 259  262          VOPNAME_REMOVE,         { .femop_remove = port_fop_remove },
 260  263          VOPNAME_LINK,           { .femop_link = port_fop_link },
 261  264          VOPNAME_RENAME,         { .femop_rename = port_fop_rename },
 262  265          VOPNAME_MKDIR,          { .femop_mkdir = port_fop_mkdir },
 263  266          VOPNAME_RMDIR,          { .femop_rmdir = port_fop_rmdir },
 264  267          VOPNAME_READDIR,        { .femop_readdir = port_fop_readdir },
 265  268          VOPNAME_SYMLINK,        { .femop_symlink = port_fop_symlink },
 266  269          VOPNAME_SETSECATTR,     { .femop_setsecattr = port_fop_setsecattr },
 267  270          VOPNAME_VNEVENT,        { .femop_vnevent = port_fop_vnevent },
 268  271          NULL,   NULL
 269  272  };
 270  273  
 271  274  /*
 272  275   * Fsem - vfs ops hooks
 273  276   */
 274  277  const fs_operation_def_t        port_vfssrc_template[] = {
 275  278          VFSNAME_UNMOUNT,        { .fsemop_unmount = port_fop_unmount },
 276  279          NULL,   NULL
 277  280  };
 278  281  
 279  282  fem_t *fop_femop;
 280  283  fsem_t *fop_fsemop;
 281  284  
 282  285  static fem_t *
 283  286  port_fop_femop()
 284  287  {
 285  288          fem_t *femp;
 286  289          if (fop_femop != NULL)
 287  290                  return (fop_femop);
 288  291          if (fem_create("portfop_fem",
 289  292              (const struct fs_operation_def *)port_vnodesrc_template,
 290  293              (fem_t **)&femp)) {
 291  294                  return (NULL);
 292  295          }
 293  296          if (casptr(&fop_femop, NULL, femp) != NULL) {
 294  297                  /*
 295  298                   * some other thread beat us to it.
 296  299                   */
 297  300                  fem_free(femp);
 298  301          }
 299  302          return (fop_femop);
 300  303  }
 301  304  
 302  305  static fsem_t *
 303  306  port_fop_fsemop()
 304  307  {
 305  308          fsem_t *fsemp;
 306  309          if (fop_fsemop != NULL)
 307  310                  return (fop_fsemop);
 308  311          if (fsem_create("portfop_fsem", port_vfssrc_template, &fsemp)) {
 309  312                  return (NULL);
 310  313          }
 311  314          if (casptr(&fop_fsemop, NULL, fsemp) != NULL) {
 312  315                  /*
 313  316                   * some other thread beat us to it.
 314  317                   */
 315  318                  fsem_free(fsemp);
 316  319          }
 317  320          return (fop_fsemop);
 318  321  }
 319  322  
 320  323  /*
 321  324   * port_fop_callback()
 322  325   * - PORT_CALLBACK_DEFAULT
 323  326   *      The file event will be delivered to the application.
 324  327   * - PORT_CALLBACK_DISSOCIATE
 325  328   *      The object will be dissociated from  the port.
 326  329   * - PORT_CALLBACK_CLOSE
 327  330   *      The object will be dissociated from the port because the port
 328  331   *      is being closed.
 329  332   */
 330  333  /* ARGSUSED */
 331  334  static int
 332  335  port_fop_callback(void *arg, int *events, pid_t pid, int flag, void *evp)
 333  336  {
 334  337          portfop_t       *pfp = (portfop_t *)arg;
 335  338          port_kevent_t   *pkevp = (port_kevent_t *)evp;
 336  339          int             error = 0;
 337  340  
 338  341          ASSERT((events != NULL));
 339  342          if (flag == PORT_CALLBACK_DEFAULT) {
 340  343                  if (curproc->p_pid != pid) {
 341  344                                  return (EACCES); /* deny delivery of events */
 342  345                  }
 343  346  
 344  347                  *events = pkevp->portkev_events;
 345  348                  pkevp->portkev_events = 0;
 346  349                  if (pfp != NULL) {
 347  350                          pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
 348  351                  }
 349  352          }
 350  353          return (error);
 351  354  }
 352  355  
 353  356  /*
 354  357   * Inserts a portfop_t into the port sources cache's.
 355  358   */
 356  359  static void
 357  360  port_pcache_insert(portfop_cache_t *pfcp, portfop_t *pfp)
 358  361  {
 359  362          portfop_t       **bucket;
 360  363  
 361  364          ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
 362  365          bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
 363  366          pfp->pfop_hashnext = *bucket;
 364  367          *bucket = pfp;
 365  368          pfcp->pfc_objcount++;
 366  369  }
 367  370  
 368  371  /*
 369  372   * Remove the pfp from the port source cache.
 370  373   */
 371  374  static void
 372  375  port_pcache_delete(portfop_cache_t *pfcp, portfop_t *pfp)
 373  376  {
 374  377          portfop_t       *lpdp;
 375  378          portfop_t       *cpdp;
 376  379          portfop_t       **bucket;
 377  380  
 378  381          bucket = PORT_FOP_BUCKET(pfcp, pfp->pfop_object);
 379  382          cpdp = *bucket;
 380  383          if (pfp == cpdp) {
 381  384                  *bucket = pfp->pfop_hashnext;
 382  385          } else {
 383  386                  while (cpdp != NULL) {
 384  387                          lpdp = cpdp;
 385  388                          cpdp = cpdp->pfop_hashnext;
 386  389                          if (cpdp == pfp) {
 387  390                                  /* portfop struct found */
 388  391                                  lpdp->pfop_hashnext = pfp->pfop_hashnext;
 389  392                                  break;
 390  393                          }
 391  394                  }
 392  395          }
 393  396          pfcp->pfc_objcount--;
 394  397  }
 395  398  
 396  399  /*
 397  400   * The vnode's(portfop_vp_t) pfp list management. The 'pvp_mutex' is held
 398  401   * when these routines are called.
 399  402   *
 400  403   * The 'pvp_lpfop' member points to the oldest inactive entry on the list.
 401  404   * It is used to discard the oldtest inactive pfp if the number of entries
 402  405   * exceed the limit.
 403  406   */
 404  407  static void
 405  408  port_fop_listinsert(portfop_vp_t *pvp, portfop_t *pfp, int where)
 406  409  {
 407  410          if (where == 1) {
 408  411                  list_insert_head(&pvp->pvp_pfoplist, (void *)pfp);
 409  412          } else {
 410  413                  list_insert_tail(&pvp->pvp_pfoplist, (void *)pfp);
 411  414          }
 412  415          if (pvp->pvp_lpfop == NULL) {
 413  416                  pvp->pvp_lpfop = pfp;
 414  417          }
 415  418          pvp->pvp_cnt++;
 416  419  }
 417  420  
 418  421  static void
 419  422  port_fop_listinsert_head(portfop_vp_t *pvp, portfop_t *pfp)
 420  423  {
 421  424          port_fop_listinsert(pvp, pfp, 1);
 422  425  }
 423  426  
 424  427  static void
 425  428  port_fop_listinsert_tail(portfop_vp_t *pvp, portfop_t *pfp)
 426  429  {
 427  430          /*
 428  431           * We point lpfop to an inactive one, if it was initially pointing
 429  432           * to an active one. Insert to the tail is done only when a pfp goes
 430  433           * inactive.
 431  434           */
 432  435          if (pvp->pvp_lpfop && pvp->pvp_lpfop->pfop_flags & PORT_FOP_ACTIVE) {
 433  436                  pvp->pvp_lpfop = pfp;
 434  437          }
 435  438          port_fop_listinsert(pvp, pfp, 0);
 436  439  }
 437  440  
 438  441  static void
 439  442  port_fop_listremove(portfop_vp_t *pvp, portfop_t *pfp)
 440  443  {
 441  444          if (pvp->pvp_lpfop == pfp) {
 442  445                  pvp->pvp_lpfop = list_next(&pvp->pvp_pfoplist, (void *)pfp);
 443  446          }
 444  447  
 445  448          list_remove(&pvp->pvp_pfoplist, (void *)pfp);
 446  449  
 447  450          pvp->pvp_cnt--;
 448  451          if (pvp->pvp_cnt && pvp->pvp_lpfop == NULL) {
 449  452                  pvp->pvp_lpfop = list_head(&pvp->pvp_pfoplist);
 450  453          }
 451  454  }
 452  455  
 453  456  static void
 454  457  port_fop_listmove(portfop_vp_t *pvp, list_t *tlist)
 455  458  {
 456  459          list_move_tail(tlist, &pvp->pvp_pfoplist);
 457  460          pvp->pvp_lpfop = NULL;
 458  461          pvp->pvp_cnt = 0;
 459  462  }
 460  463  
 461  464  /*
 462  465   * Remove a portfop_t from the port cache hash table and discard it.
 463  466   * It is called only when pfp is not on the vnode's list. Otherwise,
 464  467   * port_remove_fop() is called.
 465  468   */
 466  469  void
 467  470  port_pcache_remove_fop(portfop_cache_t *pfcp, portfop_t *pfp)
 468  471  {
 469  472          port_kevent_t   *pkevp;
 470  473  
 471  474  
 472  475          ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
 473  476  
 474  477          pkevp = pfp->pfop_pev;
 475  478          pfp->pfop_pev = NULL;
 476  479  
 477  480          if (pkevp != NULL) {
 478  481                  (void) port_remove_done_event(pkevp);
 479  482                  port_free_event_local(pkevp, 0);
 480  483          }
 481  484  
 482  485          port_pcache_delete(pfcp, pfp);
 483  486  
 484  487          if (pfp->pfop_cname != NULL)
 485  488                  kmem_free(pfp->pfop_cname, pfp->pfop_clen + 1);
 486  489          kmem_free(pfp, sizeof (portfop_t));
 487  490          if (pfcp->pfc_objcount == 0)
 488  491                  cv_signal(&pfcp->pfc_lclosecv);
 489  492  }
 490  493  
 491  494  /*
 492  495   * if we have too many watches on the vnode, attempt to discard an
 493  496   * inactive one.
 494  497   */
 495  498  static void
 496  499  port_fop_trimpfplist(vnode_t *vp)
 497  500  {
 498  501          portfop_vp_t *pvp;
 499  502          portfop_t *pfp = NULL;
 500  503          portfop_cache_t *pfcp;
 501  504          vnode_t *tdvp;
 502  505  
 503  506          /*
 504  507           * Due to a reference the vnode cannot disappear, v_fopdata should
 505  508           * not change.
 506  509           */
 507  510          if ((pvp = vp->v_fopdata) != NULL &&
 508  511              pvp->pvp_cnt > port_fop_maxpfps) {
 509  512                  mutex_enter(&pvp->pvp_mutex);
 510  513                  pfp = pvp->pvp_lpfop;
 511  514                  pfcp = pfp->pfop_pcache;
 512  515                  /*
 513  516                   * only if we can get the cache lock, we need to
 514  517                   * do this due to reverse lock order and some thread
 515  518                   * that may be trying to reactivate this entry.
 516  519                   */
 517  520                  if (mutex_tryenter(&pfcp->pfc_lock)) {
 518  521                          if (pfp && !(pfp->pfop_flags & PORT_FOP_ACTIVE) &&
 519  522                              !(pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
 520  523                                  port_fop_listremove(pvp, pfp);
 521  524                                  pfp->pfop_flags |= PORT_FOP_REMOVING;
 522  525                          } else {
 523  526                                  mutex_exit(&pfcp->pfc_lock);
 524  527                                  pfp = NULL;
 525  528                          }
 526  529                  } else {
 527  530                          pfp = NULL;
 528  531                  }
 529  532                  mutex_exit(&pvp->pvp_mutex);
 530  533  
 531  534                  /*
 532  535                   * discard pfp if any.
 533  536                   */
 534  537                  if (pfp != NULL) {
 535  538                          tdvp = pfp->pfop_dvp;
 536  539                          port_pcache_remove_fop(pfcp, pfp);
 537  540                          mutex_exit(&pfcp->pfc_lock);
 538  541                          if (tdvp != NULL)
 539  542                                  VN_RELE(tdvp);
 540  543                  }
 541  544          }
 542  545  }
 543  546  
 544  547  /*
 545  548   * This routine returns 1, if the vnode can be rele'ed by the caller.
 546  549   * The caller has to VN_RELE the vnode with out holding any
 547  550   * locks.
 548  551   */
 549  552  int
 550  553  port_fop_femuninstall(vnode_t *vp)
 551  554  {
 552  555          portfop_vp_t    *pvp;
 553  556          vfs_t           *vfsp;
 554  557          portfop_vfs_t *pvfsp;
 555  558          portfop_vfs_hash_t      *pvfsh;
 556  559          kmutex_t        *mtx;
 557  560          int     ret = 0;
 558  561  
 559  562          /*
 560  563           * if list is empty, uninstall fem.
 561  564           */
 562  565          pvp = vp->v_fopdata;
 563  566          ASSERT(MUTEX_HELD(&pvp->pvp_mutex));
 564  567  
 565  568          /*
 566  569           * make sure the list is empty.
 567  570           */
 568  571          if (!list_head(&pvp->pvp_pfoplist)) {
 569  572  
 570  573                  /*
 571  574                   * we could possibly uninstall the fem hooks when
 572  575                   * the vnode becomes inactive and the v_fopdata is
 573  576                   * free. But the hooks get triggered unnecessarily
 574  577                   * even though there are no active watches. So, we
 575  578                   * uninstall it here.
 576  579                   */
 577  580                  (void) fem_uninstall(vp, (fem_t *)pvp->pvp_femp, vp);
 578  581                  pvp->pvp_femp = NULL;
 579  582                  mutex_exit(&pvp->pvp_mutex);
 580  583  
 581  584  
 582  585                  /*
 583  586                   * If we successfully uninstalled fem, no process is watching
 584  587                   * this vnode, Remove it from the vfs's list of watched vnodes.
 585  588                   */
 586  589                  pvfsp = pvp->pvp_pvfsp;
 587  590                  vfsp = vp->v_vfsp;
 588  591                  pvfsh = PORTFOP_PVFSH(vfsp);
 589  592                  mtx = &pvfsh->pvfshash_mutex;
 590  593                  mutex_enter(mtx);
 591  594                  /*
 592  595                   * If unmount is in progress, that thread will remove and
 593  596                   * release the vnode from the vfs's list, just leave.
 594  597                   */
 595  598                  if (!pvfsp->pvfs_unmount) {
 596  599                          list_remove(&pvfsp->pvfs_pvplist, pvp);
 597  600                          mutex_exit(mtx);
 598  601                          ret = 1;
 599  602                  } else {
 600  603                          mutex_exit(mtx);
 601  604                  }
 602  605          } else {
 603  606                  mutex_exit(&pvp->pvp_mutex);
 604  607          }
 605  608          return (ret);
 606  609  }
 607  610  
 608  611  /*
 609  612   * Remove pfp from the vnode's watch list and the cache and discard it.
 610  613   * If it is the last pfp on the vnode's list, the fem hooks get uninstalled.
 611  614   * Returns 1 if pfp removed successfully.
 612  615   *
 613  616   * The *active is set to indicate if the pfp was still active(no events had
 614  617   * been posted, or the posted event had not been collected yet and it was
 615  618   * able to remove it from the port's queue).
 616  619   *
 617  620   * vpp and dvpp will point to the vnode and directory vnode which the caller
 618  621   * is required to VN_RELE without holding any locks.
 619  622   */
 620  623  int
 621  624  port_remove_fop(portfop_t *pfp, portfop_cache_t *pfcp, int cleanup,
 622  625      int *active, vnode_t **vpp, vnode_t **dvpp)
 623  626  {
 624  627          vnode_t         *vp;
 625  628          portfop_vp_t    *pvp;
 626  629          int     tactive = 0;
 627  630  
 628  631          ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
 629  632          vp = pfp->pfop_vp;
 630  633          pvp = vp->v_fopdata;
 631  634          mutex_enter(&pvp->pvp_mutex);
 632  635  
 633  636          /*
 634  637           * if not cleanup, remove it only if the pfp is still active and
 635  638           * is not being removed by some other thread.
 636  639           */
 637  640          if (!cleanup && (!(pfp->pfop_flags & PORT_FOP_ACTIVE) ||
 638  641              pfp->pfop_flags & PORT_FOP_REMOVING)) {
 639  642                  mutex_exit(&pvp->pvp_mutex);
 640  643                  return (0);
 641  644          }
 642  645  
 643  646          /*
 644  647           * mark it inactive.
 645  648           */
 646  649          if (pfp->pfop_flags & PORT_FOP_ACTIVE) {
 647  650                  pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
 648  651                  tactive = 1;
 649  652          }
 650  653  
 651  654          /*
 652  655           * Check if the pfp is still on the vnode's list. This can
 653  656           * happen if port_fop_excep() is in the process of removing it.
 654  657           * In case of cleanup, just mark this pfp as inactive so that no
 655  658           * new events (VNEVENT) will be delivered, and remove it from the
 656  659           * event queue if it was already queued. Since the cache lock is
 657  660           * held, the pfp will not disappear, even though it is being
 658  661           * removed.
 659  662           */
 660  663          if (pfp->pfop_flags & PORT_FOP_REMOVING) {
 661  664                  mutex_exit(&pvp->pvp_mutex);
 662  665                  if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
 663  666                          pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
 664  667                          tactive = 1;
 665  668                  }
 666  669                  if (active) {
 667  670                          *active = tactive;
 668  671                  }
 669  672                  return (1);
 670  673          }
 671  674  
 672  675          /*
 673  676           * if we find an event on the queue and removed it, then this
 674  677           * association is considered active.
 675  678           */
 676  679          if (!tactive && port_remove_done_event(pfp->pfop_pev)) {
 677  680                  pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
 678  681                  tactive = 1;
 679  682          }
 680  683  
 681  684          if (active) {
 682  685                  *active = tactive;
 683  686          }
 684  687          pvp = (portfop_vp_t *)vp->v_fopdata;
 685  688  
 686  689          /*
 687  690           * remove pfp from the vnode's list
 688  691           */
 689  692          port_fop_listremove(pvp, pfp);
 690  693  
 691  694          /*
 692  695           * If no more associations on the vnode, uninstall fem hooks.
 693  696           * The pvp mutex will be released in this routine.
 694  697           */
 695  698          if (port_fop_femuninstall(vp))
 696  699                  *vpp = vp;
 697  700          *dvpp = pfp->pfop_dvp;
 698  701          port_pcache_remove_fop(pfcp, pfp);
 699  702          return (1);
 700  703  }
 701  704  
 702  705  /*
 703  706   * This routine returns a pointer to a cached portfop entry, or NULL if it
 704  707   * does not find it in the hash table. The object pointer is used as index.
 705  708   * The entries are hashed by the object's address. We need to match the pid
 706  709   * as the evet port can be shared between processes. The file events
 707  710   * watches are per process only.
 708  711   */
 709  712  portfop_t *
 710  713  port_cache_lookup_fop(portfop_cache_t *pfcp, pid_t pid, uintptr_t obj)
 711  714  {
 712  715          portfop_t       *pfp = NULL;
 713  716          portfop_t       **bucket;
 714  717  
 715  718          ASSERT(MUTEX_HELD(&pfcp->pfc_lock));
 716  719          bucket = PORT_FOP_BUCKET(pfcp, obj);
 717  720          pfp = *bucket;
 718  721          while (pfp != NULL) {
 719  722                  if (pfp->pfop_object == obj && pfp->pfop_pid == pid)
 720  723                          break;
 721  724                  pfp = pfp->pfop_hashnext;
 722  725          }
 723  726          return (pfp);
 724  727  }
 725  728  
 726  729  /*
 727  730   * Given the file name, get the vnode and also the directory vnode
 728  731   * On return, the vnodes are held (VN_HOLD). The caller has to VN_RELE
 729  732   * the vnode(s).
 730  733   */
 731  734  int
 732  735  port_fop_getdvp(void *objptr, vnode_t **vp, vnode_t **dvp,
 733  736          char **cname, int *len, int follow)
 734  737  {
 735  738          int error = 0;
 736  739          struct pathname pn;
 737  740          char *fname;
 738  741  
 739  742          if (get_udatamodel() == DATAMODEL_NATIVE) {
 740  743                  fname = ((file_obj_t *)objptr)->fo_name;
 741  744  #ifdef  _SYSCALL32_IMPL
 742  745          } else {
 743  746                  fname = (caddr_t)(uintptr_t)((file_obj32_t *)objptr)->fo_name;
 744  747  #endif  /* _SYSCALL32_IMPL */
 745  748          }
 746  749  
 747  750          /*
 748  751           * lookuppn may fail with EINVAL, if dvp is  non-null(like when
 749  752           * looking for "."). So call again with dvp = NULL.
 750  753           */
 751  754          if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
 752  755                  return (error);
 753  756          }
 754  757  
 755  758          error = lookuppn(&pn, NULL, follow, dvp, vp);
 756  759          if (error == EINVAL) {
 757  760                  pn_free(&pn);
 758  761                  if ((error = pn_get(fname, UIO_USERSPACE, &pn)) != 0) {
 759  762                          return (error);
 760  763                  }
 761  764                  error = lookuppn(&pn, NULL, follow, NULL, vp);
 762  765                  if (dvp != NULL) {
 763  766                          *dvp = NULL;
 764  767                  }
 765  768          }
 766  769  
 767  770          if (error == 0 && cname != NULL && len != NULL) {
 768  771                  pn_setlast(&pn);
 769  772                  *len = pn.pn_pathlen;
 770  773                  *cname = kmem_alloc(*len + 1, KM_SLEEP);
 771  774                  (void) strcpy(*cname, pn.pn_path);
 772  775          } else {
 773  776                  if (cname != NULL && len != NULL) {
 774  777                          *cname = NULL;
 775  778                          *len = 0;
 776  779                  }
 777  780          }
 778  781  
 779  782          pn_free(&pn);
 780  783          return (error);
 781  784  }
 782  785  
 783  786  port_source_t *
 784  787  port_getsrc(port_t *pp, int source)
 785  788  {
 786  789          port_source_t *pse;
 787  790          int     lock = 0;
 788  791          /*
 789  792           * get the port source structure.
 790  793           */
 791  794          if (!MUTEX_HELD(&pp->port_queue.portq_source_mutex)) {
 792  795                  mutex_enter(&pp->port_queue.portq_source_mutex);
 793  796                  lock = 1;
 794  797          }
 795  798  
 796  799          pse = pp->port_queue.portq_scache[PORT_SHASH(source)];
 797  800          for (; pse != NULL; pse = pse->portsrc_next) {
 798  801                  if (pse->portsrc_source == source)
 799  802                          break;
 800  803          }
 801  804  
 802  805          if (lock) {
 803  806                  mutex_exit(&pp->port_queue.portq_source_mutex);
 804  807          }
 805  808          return (pse);
 806  809  }
 807  810  
 808  811  
 809  812  /*
 810  813   * Compare time stamps and generate an event if it has changed.
 811  814   * Note that the port cache pointer will be valid due to a reference
 812  815   * to the port. We need to grab the port cache lock and verify that
 813  816   * the pfp is still the same before proceeding to deliver an event.
 814  817   */
 815  818  static void
 816  819  port_check_timestamp(portfop_cache_t *pfcp, vnode_t *vp, vnode_t *dvp,
 817  820          portfop_t *pfp, void *objptr, uintptr_t object)
 818  821  {
 819  822          vattr_t         vatt;
 820  823          portfop_vp_t    *pvp = vp->v_fopdata;
 821  824          int             events = 0;
 822  825          port_kevent_t   *pkevp;
 823  826          file_obj_t      *fobj;
 824  827          portfop_t       *tpfp;
 825  828  
 826  829          /*
 827  830           * If time stamps are specified, get attributes and compare.
 828  831           */
 829  832          vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
 830  833          if (get_udatamodel() == DATAMODEL_NATIVE) {
 831  834                  fobj = (file_obj_t *)objptr;
 832  835                  if (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec ||
 833  836                      fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec ||
 834  837                      fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) {
 835  838                          if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) {
 836  839                                  return;
 837  840                          }
 838  841                  } else {
 839  842                          /*
 840  843                           * timestamp not specified, all 0's,
 841  844                           */
 842  845                          return;
 843  846                  }
 844  847  #ifdef  _SYSCALL32_IMPL
 845  848          } else {
 846  849                  file_obj32_t    *fobj32;
 847  850                  fobj32 = (file_obj32_t *)objptr;
 848  851                  if (fobj32->fo_atime.tv_sec || fobj32->fo_atime.tv_nsec ||
 849  852                      fobj32->fo_mtime.tv_sec || fobj32->fo_mtime.tv_nsec ||
 850  853                      fobj32->fo_ctime.tv_sec || fobj32->fo_ctime.tv_nsec) {
 851  854                          if (VOP_GETATTR(vp, &vatt, 0, CRED(), NULL)) {
 852  855                                  return;
 853  856                          }
 854  857                  } else {
 855  858                          /*
 856  859                           * timestamp not specified, all 0.
 857  860                           */
 858  861                          return;
 859  862                  }
 860  863  #endif /* _SYSCALL32_IMPL */
 861  864          }
 862  865  
 863  866          /*
 864  867           * Now grab the cache lock and verify that we are still
 865  868           * dealing with the same pfp and curthread is the one
 866  869           * which registered it. We need to do this to avoid
 867  870           * delivering redundant events.
 868  871           */
 869  872          mutex_enter(&pfcp->pfc_lock);
 870  873          tpfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
 871  874  
 872  875          if (tpfp == NULL || tpfp != pfp ||
 873  876              pfp->pfop_vp != vp || pfp->pfop_dvp != dvp ||
 874  877              pfp->pfop_callrid != curthread ||
 875  878              !(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
 876  879                  /*
 877  880                   * Some other event was delivered, the file
 878  881                   * watch was removed or reassociated. Just
 879  882                   * ignore it and leave
 880  883                   */
 881  884                  mutex_exit(&pfcp->pfc_lock);
 882  885                  return;
 883  886          }
 884  887  
 885  888          mutex_enter(&pvp->pvp_mutex);
 886  889          /*
 887  890           * The pfp cannot disappear as the port cache lock is held.
 888  891           * While the pvp_mutex is held, no events will get delivered.
 889  892           */
 890  893          if (pfp->pfop_flags & PORT_FOP_ACTIVE &&
 891  894              !(pfp->pfop_flags & PORT_FOP_REMOVING)) {
 892  895                  if (get_udatamodel() == DATAMODEL_NATIVE) {
 893  896                          fobj = (file_obj_t *)objptr;
 894  897                          if (pfp->pfop_events & FILE_ACCESS &&
 895  898                              (fobj->fo_atime.tv_sec || fobj->fo_atime.tv_nsec) &&
 896  899                              (vatt.va_atime.tv_sec != fobj->fo_atime.tv_sec ||
 897  900                              vatt.va_atime.tv_nsec != fobj->fo_atime.tv_nsec))
 898  901                                  events |= FILE_ACCESS;
 899  902  
 900  903                          if (pfp->pfop_events & FILE_MODIFIED &&
 901  904                              (fobj->fo_mtime.tv_sec || fobj->fo_mtime.tv_nsec) &&
 902  905                              (vatt.va_mtime.tv_sec != fobj->fo_mtime.tv_sec ||
 903  906                              vatt.va_mtime.tv_nsec != fobj->fo_mtime.tv_nsec))
 904  907                                  events |= FILE_MODIFIED;
 905  908  
 906  909                          if (pfp->pfop_events & FILE_ATTRIB &&
 907  910                              (fobj->fo_ctime.tv_sec || fobj->fo_ctime.tv_nsec) &&
 908  911                              (vatt.va_ctime.tv_sec != fobj->fo_ctime.tv_sec ||
 909  912                              vatt.va_ctime.tv_nsec != fobj->fo_ctime.tv_nsec))
 910  913                                  events |= FILE_ATTRIB;
 911  914  #ifdef  _SYSCALL32_IMPL
 912  915                  } else {
 913  916                          file_obj32_t    *fobj32;
 914  917                          fobj32 = (file_obj32_t *)objptr;
 915  918                          if (pfp->pfop_events & FILE_ACCESS &&
 916  919                              (fobj32->fo_atime.tv_sec ||
 917  920                              fobj32->fo_atime.tv_nsec) &&
 918  921                              (vatt.va_atime.tv_sec != fobj32->fo_atime.tv_sec ||
 919  922                              vatt.va_atime.tv_nsec != fobj32->fo_atime.tv_nsec))
 920  923                                  events |= FILE_ACCESS;
 921  924  
 922  925                          if (pfp->pfop_events & FILE_MODIFIED &&
 923  926                              (fobj32->fo_mtime.tv_sec ||
 924  927                              fobj32->fo_mtime.tv_nsec) &&
 925  928                              (vatt.va_mtime.tv_sec != fobj32->fo_mtime.tv_sec ||
 926  929                              vatt.va_mtime.tv_nsec != fobj32->fo_mtime.tv_nsec))
 927  930                                  events |= FILE_MODIFIED;
 928  931  
 929  932                          if (pfp->pfop_events & FILE_ATTRIB &&
 930  933                              (fobj32->fo_ctime.tv_sec ||
 931  934                              fobj32->fo_ctime.tv_nsec) &&
 932  935                              (vatt.va_ctime.tv_sec != fobj32->fo_ctime.tv_sec ||
 933  936                              vatt.va_ctime.tv_nsec != fobj32->fo_ctime.tv_nsec))
 934  937                                  events |= FILE_ATTRIB;
 935  938  #endif /* _SYSCALL32_IMPL */
 936  939                  }
 937  940  
 938  941                  /*
 939  942                   * No events to deliver
 940  943                   */
 941  944                  if (events == 0) {
 942  945                          mutex_exit(&pvp->pvp_mutex);
 943  946                          mutex_exit(&pfcp->pfc_lock);
 944  947                          return;
 945  948                  }
 946  949  
 947  950                  /*
 948  951                   * Deliver the event now.
 949  952                   */
 950  953                  pkevp = pfp->pfop_pev;
 951  954                  pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
 952  955                  pkevp->portkev_events |= events;
 953  956                  /*
 954  957                   * Move it to the tail as active once are in the
 955  958                   * beginning of the list.
 956  959                   */
 957  960                  port_fop_listremove(pvp, pfp);
 958  961                  port_fop_listinsert_tail(pvp, pfp);
 959  962                  port_send_event(pkevp);
 960  963                  pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
 961  964          }
 962  965          mutex_exit(&pvp->pvp_mutex);
 963  966          mutex_exit(&pfcp->pfc_lock);
 964  967  }
 965  968  
 966  969  /*
 967  970   * Add the event source to the port and return the port source cache pointer.
 968  971   */
 969  972  int
 970  973  port_fop_associate_source(portfop_cache_t **pfcpp, port_t *pp, int source)
 971  974  {
 972  975          portfop_cache_t *pfcp;
 973  976          port_source_t   *pse;
 974  977          int             error;
 975  978  
 976  979          /*
 977  980           * associate PORT_SOURCE_FILE source with the port, if it is
 978  981           * not associated yet. Note the PORT_SOURCE_FILE source is
 979  982           * associated once and will not be dissociated.
 980  983           */
 981  984          if ((pse = port_getsrc(pp, PORT_SOURCE_FILE)) == NULL) {
 982  985                  if (error = port_associate_ksource(pp->port_fd, source,
 983  986                      &pse, port_close_fop, pp, NULL)) {
 984  987                          *pfcpp = NULL;
 985  988                          return (error);
 986  989                  }
 987  990          }
 988  991  
 989  992          /*
 990  993           * Get the portfop cache pointer.
 991  994           */
 992  995          if ((pfcp = pse->portsrc_data) == NULL) {
 993  996                  /*
 994  997                   * This is the first time that a file is being associated,
 995  998                   * create the portfop cache.
 996  999                   */
 997 1000                  pfcp = kmem_zalloc(sizeof (portfop_cache_t), KM_SLEEP);
 998 1001                  mutex_enter(&pp->port_queue.portq_source_mutex);
 999 1002                  if (pse->portsrc_data == NULL) {
1000 1003                          pse->portsrc_data = pfcp;
1001 1004                          mutex_exit(&pp->port_queue.portq_source_mutex);
1002 1005                  } else {
1003 1006                          /*
1004 1007                           * someone else created the port cache, free
1005 1008                           * what we just now allocated.
1006 1009                           */
1007 1010                          mutex_exit(&pp->port_queue.portq_source_mutex);
1008 1011                          kmem_free(pfcp, sizeof (portfop_cache_t));
1009 1012                          pfcp = pse->portsrc_data;
1010 1013                  }
1011 1014          }
1012 1015          *pfcpp = pfcp;
1013 1016          return (0);
1014 1017  }
1015 1018  
1016 1019  /*
1017 1020   * Add the given pvp on the file system's list of vnodes watched.
1018 1021   */
1019 1022  int
1020 1023  port_fop_pvfsadd(portfop_vp_t *pvp)
1021 1024  {
1022 1025          int error = 0;
1023 1026          vnode_t *vp = pvp->pvp_vp;
1024 1027          portfop_vfs_hash_t *pvfsh;
1025 1028          portfop_vfs_t    *pvfsp;
1026 1029          fsem_t          *fsemp;
1027 1030  
1028 1031          pvfsh = PORTFOP_PVFSH(vp->v_vfsp);
1029 1032          mutex_enter(&pvfsh->pvfshash_mutex);
1030 1033          for (pvfsp = pvfsh->pvfshash_pvfsp; pvfsp &&
1031 1034              pvfsp->pvfs != vp->v_vfsp; pvfsp = pvfsp->pvfs_next)
1032 1035                  ;
1033 1036  
1034 1037          if (!pvfsp) {
1035 1038                  if ((fsemp = port_fop_fsemop()) != NULL) {
1036 1039                          if ((error = fsem_install(vp->v_vfsp, fsemp,
1037 1040                              vp->v_vfsp, OPUNIQ, NULL, NULL))) {
1038 1041                                  mutex_exit(&pvfsh->pvfshash_mutex);
1039 1042                                  return (error);
1040 1043                          }
1041 1044                  } else {
1042 1045                          mutex_exit(&pvfsh->pvfshash_mutex);
1043 1046                          return (EINVAL);
1044 1047                  }
1045 1048                  pvfsp = kmem_zalloc(sizeof (portfop_vfs_t), KM_SLEEP);
1046 1049                  pvfsp->pvfs = vp->v_vfsp;
1047 1050                  list_create(&(pvfsp->pvfs_pvplist), sizeof (portfop_vp_t),
1048 1051                      offsetof(portfop_vp_t, pvp_pvfsnode));
1049 1052                  pvfsp->pvfs_fsemp = fsemp;
1050 1053                  pvfsp->pvfs_next = pvfsh->pvfshash_pvfsp;
1051 1054                  pvfsh->pvfshash_pvfsp = pvfsp;
1052 1055          }
1053 1056  
1054 1057          /*
1055 1058           * check if an unmount is in progress.
1056 1059           */
1057 1060          if (!pvfsp->pvfs_unmount) {
1058 1061                  /*
1059 1062                   * insert the pvp on list.
1060 1063                   */
1061 1064                  pvp->pvp_pvfsp = pvfsp;
1062 1065                  list_insert_head(&pvfsp->pvfs_pvplist, (void *)pvp);
1063 1066          } else {
1064 1067                  error = EINVAL;
1065 1068          }
1066 1069          mutex_exit(&pvfsh->pvfshash_mutex);
1067 1070          return (error);
1068 1071  }
1069 1072  
1070 1073  /*
1071 1074   * Installs the portfop_vp_t data structure on the
1072 1075   * vnode. The 'pvp_femp == NULL' indicates it is not
1073 1076   * active. The fem hooks have to be installed.
1074 1077   * The portfop_vp_t is only freed when the vnode gets freed.
1075 1078   */
1076 1079  void
1077 1080  port_install_fopdata(vnode_t *vp)
1078 1081  {
1079 1082          portfop_vp_t *npvp;
1080 1083  
1081 1084          npvp = kmem_zalloc(sizeof (*npvp), KM_SLEEP);
1082 1085          mutex_init(&npvp->pvp_mutex, NULL, MUTEX_DEFAULT, NULL);
1083 1086          list_create(&npvp->pvp_pfoplist, sizeof (portfop_t),
1084 1087              offsetof(portfop_t, pfop_node));
1085 1088          npvp->pvp_vp = vp;
1086 1089          /*
1087 1090           * If v_fopdata is not null, some other thread beat us to it.
1088 1091           */
1089 1092          if (casptr(&vp->v_fopdata, NULL, npvp) != NULL) {
1090 1093                  mutex_destroy(&npvp->pvp_mutex);
1091 1094                  list_destroy(&npvp->pvp_pfoplist);
1092 1095                  kmem_free(npvp, sizeof (*npvp));
1093 1096          }
1094 1097  }
1095 1098  
1096 1099  
1097 1100  /*
1098 1101   * Allocate and add a portfop_t to the per port cache. Also add the portfop_t
1099 1102   * to the vnode's list. The association is identified by the object pointer
1100 1103   * address and pid.
1101 1104   */
1102 1105  int
1103 1106  port_pfp_setup(portfop_t **pfpp, port_t *pp, vnode_t *vp, portfop_cache_t *pfcp,
1104 1107          uintptr_t object, int events, void *user, char *cname, int clen,
1105 1108          vnode_t *dvp)
1106 1109  {
1107 1110          portfop_t       *pfp = NULL;
1108 1111          port_kevent_t   *pkevp;
1109 1112          fem_t           *femp;
1110 1113          int             error = 0;
1111 1114          portfop_vp_t    *pvp;
1112 1115  
1113 1116  
1114 1117          /*
1115 1118           * The port cache mutex is held.
1116 1119           */
1117 1120          *pfpp  = NULL;
1118 1121  
1119 1122  
1120 1123          /*
1121 1124           * At this point the fem monitor is installed.
1122 1125           * Allocate a port event structure per vnode association.
1123 1126           */
1124 1127          if (pfp == NULL) {
1125 1128                  if (error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
1126 1129                      PORT_ALLOC_CACHED, &pkevp)) {
1127 1130                          return (error);
1128 1131                  }
1129 1132                  pfp = kmem_zalloc(sizeof (portfop_t), KM_SLEEP);
1130 1133                  pfp->pfop_pev = pkevp;
1131 1134          }
1132 1135  
1133 1136          pfp->pfop_vp = vp;
1134 1137          pfp->pfop_pid = curproc->p_pid;
1135 1138          pfp->pfop_pcache = pfcp;
1136 1139          pfp->pfop_pp = pp;
1137 1140          pfp->pfop_flags |= PORT_FOP_ACTIVE;
1138 1141          pfp->pfop_cname = cname;
1139 1142          pfp->pfop_clen = clen;
1140 1143          pfp->pfop_dvp = dvp;
1141 1144          pfp->pfop_object = object;
1142 1145  
1143 1146          pkevp->portkev_callback = port_fop_callback;
1144 1147          pkevp->portkev_arg = pfp;
1145 1148          pkevp->portkev_object = object;
1146 1149          pkevp->portkev_user = user;
1147 1150          pkevp->portkev_events = 0;
1148 1151  
1149 1152          port_pcache_insert(pfcp, pfp);
1150 1153  
1151 1154          /*
1152 1155           * Register a new file events monitor for this file(vnode), if not
1153 1156           * done already.
1154 1157           */
1155 1158          if ((pvp = vp->v_fopdata) == NULL) {
1156 1159                  port_install_fopdata(vp);
1157 1160                  pvp = vp->v_fopdata;
1158 1161          }
1159 1162  
1160 1163          mutex_enter(&pvp->pvp_mutex);
1161 1164          /*
1162 1165           * if the vnode does not have the file events hooks, install it.
1163 1166           */
1164 1167          if (pvp->pvp_femp == NULL) {
1165 1168                  if ((femp = port_fop_femop()) != NULL) {
1166 1169                          if (!(error = fem_install(pfp->pfop_vp, femp,
1167 1170                              (void *)vp, OPUNIQ, NULL, NULL))) {
1168 1171                                  pvp->pvp_femp = femp;
1169 1172                                  /*
1170 1173                                   * add fsem_t hooks to the vfsp and add pvp to
1171 1174                                   * the list of vnodes for this vfs.
1172 1175                                   */
1173 1176                                  if (!(error = port_fop_pvfsadd(pvp))) {
1174 1177                                          /*
1175 1178                                           * Hold a reference to the vnode since
1176 1179                                           * we successfully installed the hooks.
1177 1180                                           */
1178 1181                                          VN_HOLD(vp);
1179 1182                                  } else {
1180 1183                                          (void) fem_uninstall(vp, femp, vp);
1181 1184                                          pvp->pvp_femp = NULL;
1182 1185                                  }
1183 1186                          }
1184 1187                  } else {
1185 1188                          error = EINVAL;
1186 1189                  }
1187 1190          }
1188 1191  
1189 1192          if (error) {
1190 1193                  /*
1191 1194                   * pkevp will get freed here.
1192 1195                   */
1193 1196                  pfp->pfop_cname = NULL;
1194 1197                  port_pcache_remove_fop(pfcp, pfp);
1195 1198                  mutex_exit(&pvp->pvp_mutex);
1196 1199                  return (error);
1197 1200          }
1198 1201  
1199 1202          /*
1200 1203           * insert the pfp on the vnode's list. After this
1201 1204           * events can get delivered.
1202 1205           */
1203 1206          pfp->pfop_events = events;
1204 1207          port_fop_listinsert_head(pvp, pfp);
1205 1208  
1206 1209          mutex_exit(&pvp->pvp_mutex);
1207 1210          /*
1208 1211           * Hold the directory vnode since we have a reference now.
1209 1212           */
1210 1213          if (dvp != NULL)
1211 1214                  VN_HOLD(dvp);
1212 1215          *pfpp = pfp;
1213 1216          return (0);
1214 1217  }
1215 1218  
1216 1219  vnode_t *
1217 1220  port_resolve_vp(vnode_t *vp)
1218 1221  {
1219 1222          vnode_t *rvp;
1220 1223          /*
1221 1224           * special case /etc/mnttab(mntfs type). The mntfstype != 0
1222 1225           * if mntfs got mounted.
1223 1226           */
1224 1227          if (vfs_mntdummyvp && mntfstype != 0 &&
1225 1228              vp->v_vfsp->vfs_fstype == mntfstype) {
1226 1229                  VN_RELE(vp);
1227 1230                  vp = vfs_mntdummyvp;
1228 1231                  VN_HOLD(vfs_mntdummyvp);
1229 1232          }
1230 1233  
1231 1234          /*
1232 1235           * This should take care of lofs mounted fs systems and nfs4
1233 1236           * hardlinks.
1234 1237           */
1235 1238          if ((VOP_REALVP(vp, &rvp, NULL) == 0) && vp != rvp) {
1236 1239                  VN_HOLD(rvp);
1237 1240                  VN_RELE(vp);
1238 1241                  vp = rvp;
1239 1242          }
1240 1243          return (vp);
1241 1244  }
1242 1245  
1243 1246  /*
1244 1247   * Register a file events watch on the given file associated to the port *pp.
1245 1248   *
1246 1249   * The association is identified by the object pointer and the pid.
1247 1250   * The events argument contains the events to be monitored for.
1248 1251   *
1249 1252   * The vnode will have a VN_HOLD once the fem hooks are installed.
1250 1253   *
1251 1254   * Every reference(pfp) to the directory vnode will have a VN_HOLD to ensure
1252 1255   * that the directory vnode pointer does not change.
1253 1256   */
1254 1257  int
1255 1258  port_associate_fop(port_t *pp, int source, uintptr_t object, int events,
1256 1259      void *user)
1257 1260  {
1258 1261          portfop_cache_t *pfcp;
1259 1262          vnode_t         *vp, *dvp, *oldvp = NULL, *olddvp = NULL;
1260 1263          portfop_t       *pfp;
1261 1264          int             error = 0;
1262 1265          file_obj_t      fobj;
1263 1266          void            *objptr;
1264 1267          char            *cname;
1265 1268          int             clen;
1266 1269          int             follow;
1267 1270  
1268 1271          /*
1269 1272           * check that events specified are valid.
1270 1273           */
1271 1274          if ((events & ~FILE_EVENTS_MASK) != 0)
1272 1275                  return (EINVAL);
1273 1276  
1274 1277          if (get_udatamodel() == DATAMODEL_NATIVE) {
1275 1278                  if (copyin((void *)object, &fobj, sizeof (file_obj_t)))
1276 1279                          return (EFAULT);
1277 1280                  objptr = (void *)&fobj;
1278 1281  #ifdef  _SYSCALL32_IMPL
1279 1282          } else {
1280 1283                  file_obj32_t    fobj32;
1281 1284                  if (copyin((void *)object, &fobj32, sizeof (file_obj32_t)))
1282 1285                          return (EFAULT);
1283 1286                  objptr = (void *)&fobj32;
1284 1287  #endif  /* _SYSCALL32_IMPL */
1285 1288          }
1286 1289  
1287 1290          vp = dvp = NULL;
1288 1291  
1289 1292          /*
1290 1293           * find out if we need to follow symbolic links.
1291 1294           */
1292 1295          follow = !(events & FILE_NOFOLLOW);
1293 1296          events = events & ~FILE_NOFOLLOW;
1294 1297  
1295 1298          /*
1296 1299           * lookup and find the vnode and its directory vnode of the given
1297 1300           * file.
1298 1301           */
1299 1302          if ((error = port_fop_getdvp(objptr, &vp, &dvp, &cname, &clen,
1300 1303              follow)) != 0) {
1301 1304                  return (error);
1302 1305          }
1303 1306  
1304 1307          if (dvp != NULL) {
1305 1308                  dvp = port_resolve_vp(dvp);
1306 1309          }
1307 1310  
1308 1311          /*
1309 1312           * Not found
1310 1313           */
1311 1314          if (vp == NULL) {
1312 1315                  error = ENOENT;
1313 1316                  goto errout;
1314 1317          }
1315 1318  
1316 1319          vp = port_resolve_vp(vp);
1317 1320  
1318 1321  
1319 1322          if (vp != NULL && vnevent_support(vp, NULL)) {
1320 1323                  error = ENOTSUP;
1321 1324                  goto errout;
1322 1325          }
1323 1326  
1324 1327          /*
1325 1328           * If dvp belongs to a different filesystem just ignore it.
1326 1329           * Hardlinks cannot exist across filesystems.
1327 1330           */
1328 1331          if (dvp != NULL && dvp->v_vfsp != vp->v_vfsp) {
1329 1332                  VN_RELE(dvp);
1330 1333                  dvp = NULL;
1331 1334          }
1332 1335  
1333 1336          /*
1334 1337           * Associate this source to the port and get the per port
1335 1338           * fop cache pointer. If the source is already associated, it
1336 1339           * will just return the cache pointer.
1337 1340           */
1338 1341          if (error = port_fop_associate_source(&pfcp, pp, source)) {
1339 1342                  goto errout;
1340 1343          }
1341 1344  
1342 1345          /*
1343 1346           * Check if there is an existing association of this file.
1344 1347           */
1345 1348          mutex_enter(&pfcp->pfc_lock);
1346 1349          pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
1347 1350  
1348 1351          /*
1349 1352           * If it is not the same vnode, just discard it. VN_RELE needs to be
1350 1353           * called with no locks held, therefore save vnode pointers and
1351 1354           * vn_rele them later.
1352 1355           */
1353 1356          if (pfp != NULL && (pfp->pfop_vp != vp || pfp->pfop_dvp != dvp)) {
1354 1357                  (void) port_remove_fop(pfp, pfcp, 1, NULL, &oldvp, &olddvp);
1355 1358                  pfp = NULL;
1356 1359          }
1357 1360  
1358 1361          if (pfp == NULL) {
1359 1362                  vnode_t *tvp, *tdvp;
1360 1363                  portfop_t       *tpfp;
1361 1364                  int error;
1362 1365  
1363 1366                  /*
1364 1367                   * Add a new association, save the file name and the
1365 1368                   * directory vnode pointer.
1366 1369                   */
1367 1370                  if (error = port_pfp_setup(&pfp, pp, vp, pfcp, object,
1368 1371                      events, user, cname, clen, dvp)) {
1369 1372                          mutex_exit(&pfcp->pfc_lock);
1370 1373                          goto errout;
1371 1374                  }
1372 1375  
1373 1376                  pfp->pfop_callrid = curthread;
1374 1377                  /*
1375 1378                   * File name used, so make sure we don't free it.
1376 1379                   */
1377 1380                  cname = NULL;
1378 1381  
1379 1382                  /*
1380 1383                   * We need to check if the file was removed after the
1381 1384                   * the lookup and before the fem hooks where added. If
1382 1385                   * so, return error. The vnode will still exist as we have
1383 1386                   * a hold on it.
1384 1387                   *
1385 1388                   * Drop the cache lock before calling port_fop_getdvp().
1386 1389                   * port_fop_getdvp() may block either in the vfs layer
1387 1390                   * or some filesystem.  Therefore there is potential
1388 1391                   * for deadlock if cache lock is held and if some other
1389 1392                   * thread is attempting to deliver file events which would
1390 1393                   * require getting the cache lock, while it may be holding
1391 1394                   * the filesystem or vfs layer locks.
1392 1395                   */
1393 1396                  mutex_exit(&pfcp->pfc_lock);
1394 1397                  tvp = NULL;
1395 1398                  if ((error = port_fop_getdvp(objptr, &tvp, NULL,
1396 1399                      NULL, NULL, follow)) == 0) {
1397 1400                          if (tvp != NULL) {
1398 1401                                  tvp = port_resolve_vp(tvp);
1399 1402                                  /*
1400 1403                                   * This vnode pointer is just used
1401 1404                                   * for comparison, so rele it
1402 1405                                   */
1403 1406                                  VN_RELE(tvp);
1404 1407                          }
1405 1408                  }
1406 1409  
1407 1410                  if (error || tvp == NULL || tvp != vp) {
1408 1411                          /*
1409 1412                           * Since we dropped the cache lock, make sure
1410 1413                           * we are still dealing with the same pfp and this
1411 1414                           * is the thread which registered it.
1412 1415                           */
1413 1416                          mutex_enter(&pfcp->pfc_lock);
1414 1417                          tpfp = port_cache_lookup_fop(pfcp,
1415 1418                              curproc->p_pid, object);
1416 1419  
1417 1420                          error = 0;
1418 1421                          if (tpfp == NULL || tpfp != pfp ||
1419 1422                              pfp->pfop_vp != vp ||
1420 1423                              pfp->pfop_dvp != dvp ||
1421 1424                              pfp->pfop_callrid != curthread) {
1422 1425                                  /*
1423 1426                                   * Some other event was delivered, the file
1424 1427                                   * watch was removed or reassociated, just
1425 1428                                   * ignore it and leave
1426 1429                                   */
1427 1430                                  mutex_exit(&pfcp->pfc_lock);
1428 1431                                  goto errout;
1429 1432                          }
1430 1433  
1431 1434                          /*
1432 1435                           * remove the pfp and fem hooks, if pfp still
1433 1436                           * active and it is not being removed from
1434 1437                           * the vnode list. This is checked in
1435 1438                           * port_remove_fop with the vnode lock held.
1436 1439                           * The vnode returned is VN_RELE'ed after dropping
1437 1440                           * the locks.
1438 1441                           */
1439 1442                          tdvp = tvp = NULL;
1440 1443                          if (port_remove_fop(pfp, pfcp, 0, NULL, &tvp, &tdvp)) {
1441 1444                                  /*
1442 1445                                   * The pfp was removed, means no
1443 1446                                   * events where queued. Report the
1444 1447                                   * error now.
1445 1448                                   */
1446 1449                                  error = EINVAL;
1447 1450                          }
1448 1451                          mutex_exit(&pfcp->pfc_lock);
1449 1452                          if (tvp != NULL)
1450 1453                                  VN_RELE(tvp);
1451 1454                          if (tdvp != NULL)
1452 1455                                  VN_RELE(tdvp);
1453 1456                          goto errout;
1454 1457                  }
1455 1458          } else {
1456 1459                  portfop_vp_t    *pvp = vp->v_fopdata;
1457 1460  
1458 1461                  /*
1459 1462                   * Re-association of the object.
1460 1463                   */
1461 1464                  mutex_enter(&pvp->pvp_mutex);
1462 1465  
1463 1466                  /*
1464 1467                   * remove any queued up event.
1465 1468                   */
1466 1469                  if (port_remove_done_event(pfp->pfop_pev)) {
1467 1470                          pfp->pfop_flags &= ~PORT_FOP_KEV_ONQ;
1468 1471                  }
1469 1472  
1470 1473                  /*
1471 1474                   * set new events to watch.
1472 1475                   */
1473 1476                  pfp->pfop_events = events;
1474 1477  
1475 1478                  /*
1476 1479                   * If not active, mark it active even if it is being
1477 1480                   * removed. Then it can send an exception event.
1478 1481                   *
1479 1482                   * Move it to the head, as the active ones are only
1480 1483                   * in the beginning. If removing, the pfp will be on
1481 1484                   * a temporary list, no need to move it to the front
1482 1485                   * all the entries will be processed. Some exception
1483 1486                   * events will be delivered in port_fop_excep();
1484 1487                   */
1485 1488                  if (!(pfp->pfop_flags & PORT_FOP_ACTIVE)) {
1486 1489                          pfp->pfop_flags |= PORT_FOP_ACTIVE;
1487 1490                          if (!(pfp->pfop_flags & PORT_FOP_REMOVING)) {
1488 1491                                  pvp = (portfop_vp_t *)vp->v_fopdata;
1489 1492                                  port_fop_listremove(pvp, pfp);
1490 1493                                  port_fop_listinsert_head(pvp, pfp);
1491 1494                          }
1492 1495                  }
1493 1496                  pfp->pfop_callrid = curthread;
1494 1497                  mutex_exit(&pvp->pvp_mutex);
1495 1498                  mutex_exit(&pfcp->pfc_lock);
1496 1499          }
1497 1500  
1498 1501          /*
1499 1502           * Compare time stamps and deliver events.
1500 1503           */
1501 1504          if (vp->v_type != VFIFO) {
1502 1505                  port_check_timestamp(pfcp, vp, dvp, pfp, objptr, object);
1503 1506          }
1504 1507  
1505 1508          error = 0;
1506 1509  
1507 1510          /*
1508 1511           *  If we have too many watches on the vnode, discard an
1509 1512           *  inactive watch.
1510 1513           */
1511 1514          port_fop_trimpfplist(vp);
1512 1515  
1513 1516  errout:
1514 1517          /*
1515 1518           * Release the hold acquired due to the lookup operation.
1516 1519           */
1517 1520          if (vp != NULL)
1518 1521                  VN_RELE(vp);
1519 1522          if (dvp != NULL)
1520 1523                  VN_RELE(dvp);
1521 1524  
1522 1525          if (oldvp != NULL)
1523 1526                  VN_RELE(oldvp);
1524 1527          if (olddvp != NULL)
1525 1528                  VN_RELE(olddvp);
1526 1529  
1527 1530          /*
1528 1531           * copied file name not used, free it.
1529 1532           */
1530 1533          if (cname != NULL) {
1531 1534                  kmem_free(cname, clen + 1);
1532 1535          }
1533 1536          return (error);
1534 1537  }
1535 1538  
1536 1539  
1537 1540  /*
1538 1541   * The port_dissociate_fop() function dissociates the file object
1539 1542   * from the event port and removes any events that are already on the queue.
1540 1543   * Only the owner of the association is allowed to dissociate the file from
1541 1544   * the port. Returns  success (0) if it was found and removed. Otherwise
1542 1545   * ENOENT.
1543 1546   */
1544 1547  int
1545 1548  port_dissociate_fop(port_t *pp, uintptr_t object)
1546 1549  {
1547 1550          portfop_cache_t *pfcp;
1548 1551          portfop_t       *pfp;
1549 1552          port_source_t   *pse;
1550 1553          int             active = 0;
1551 1554          vnode_t         *tvp = NULL, *tdvp = NULL;
1552 1555  
1553 1556          pse = port_getsrc(pp, PORT_SOURCE_FILE);
1554 1557  
1555 1558          /*
1556 1559           * if this source is not associated or if there is no
1557 1560           * cache, nothing to do just return.
1558 1561           */
1559 1562          if (pse == NULL ||
1560 1563              (pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
1561 1564                  return (EINVAL);
1562 1565  
1563 1566          /*
1564 1567           * Check if this object is on the cache. Only the owner pid
1565 1568           * is allowed to dissociate.
1566 1569           */
1567 1570          mutex_enter(&pfcp->pfc_lock);
1568 1571          pfp = port_cache_lookup_fop(pfcp, curproc->p_pid, object);
1569 1572          if (pfp == NULL) {
1570 1573                  mutex_exit(&pfcp->pfc_lock);
1571 1574                  return (ENOENT);
1572 1575          }
1573 1576  
1574 1577          /*
1575 1578           * If this was the last association, it will release
1576 1579           * the hold on the vnode. There is a race condition where
1577 1580           * the the pfp is being removed due to an exception event
1578 1581           * in port_fop_sendevent()->port_fop_excep() and port_remove_fop().
1579 1582           * Since port source cache lock is held, port_fop_excep() cannot
1580 1583           * complete. The vnode itself will not disappear as long its pfps
1581 1584           * have a reference.
1582 1585           */
1583 1586          (void) port_remove_fop(pfp, pfcp, 1, &active, &tvp, &tdvp);
1584 1587          mutex_exit(&pfcp->pfc_lock);
1585 1588          if (tvp != NULL)
1586 1589                  VN_RELE(tvp);
1587 1590          if (tdvp != NULL)
1588 1591                  VN_RELE(tdvp);
1589 1592          return (active ? 0 : ENOENT);
1590 1593  }
1591 1594  
1592 1595  
1593 1596  /*
1594 1597   * port_close() calls this function to request the PORT_SOURCE_FILE source
1595 1598   * to remove/free all resources allocated and associated with the port.
1596 1599   */
1597 1600  
1598 1601  /* ARGSUSED */
1599 1602  static void
1600 1603  port_close_fop(void *arg, int port, pid_t pid, int lastclose)
1601 1604  {
1602 1605          port_t          *pp = arg;
1603 1606          portfop_cache_t *pfcp;
1604 1607          portfop_t       **hashtbl;
1605 1608          portfop_t       *pfp;
1606 1609          portfop_t       *pfpnext;
1607 1610          int             index, i;
1608 1611          port_source_t   *pse;
1609 1612          vnode_t         *tdvp = NULL;
1610 1613          vnode_t         *vpl[PORTFOP_NVP];
1611 1614  
1612 1615          pse = port_getsrc(pp, PORT_SOURCE_FILE);
1613 1616  
1614 1617          /*
1615 1618           * No source or no cache, nothing to do.
1616 1619           */
1617 1620          if (pse == NULL ||
1618 1621              (pfcp = (portfop_cache_t *)pse->portsrc_data) == NULL)
1619 1622                  return;
1620 1623          /*
1621 1624           * Scan the cache and free all allocated portfop_t and port_kevent_t
1622 1625           * structures of this pid. Note, no new association for this pid will
1623 1626           * be possible as the port is being closed.
1624 1627           *
1625 1628           * The common case is that the port is not shared and all the entries
1626 1629           * are of this pid and have to be freed. Since VN_RELE has to be
1627 1630           * called outside the lock, we do it in batches.
1628 1631           */
1629 1632          hashtbl = (portfop_t **)pfcp->pfc_hash;
1630 1633          index = i = 0;
1631 1634          bzero(vpl, sizeof (vpl));
1632 1635          mutex_enter(&pfcp->pfc_lock);
1633 1636          while (index < PORTFOP_HASHSIZE) {
1634 1637                  pfp = hashtbl[index];
1635 1638                  while (pfp != NULL && i < (PORTFOP_NVP - 1)) {
1636 1639                          pfpnext = pfp->pfop_hashnext;
1637 1640                          if (pid == pfp->pfop_pid) {
1638 1641                                  (void) port_remove_fop(pfp, pfcp, 1, NULL,
1639 1642                                      &vpl[i], &tdvp);
1640 1643                                  if (vpl[i] != NULL) {
1641 1644                                          i++;
1642 1645                                  }
1643 1646                                  if (tdvp != NULL) {
1644 1647                                          vpl[i++] = tdvp;
1645 1648                                          tdvp = NULL;
1646 1649                                  }
1647 1650                          }
1648 1651                          pfp = pfpnext;
1649 1652                  }
1650 1653                  if (pfp == NULL)
1651 1654                          index++;
1652 1655                  /*
1653 1656                   * Now call VN_RELE if we have collected enough vnodes or
1654 1657                   * we have reached the end of the hash table.
1655 1658                   */
1656 1659                  if (i >= (PORTFOP_NVP - 1) ||
1657 1660                      (i > 0 && index == PORTFOP_HASHSIZE)) {
1658 1661                          mutex_exit(&pfcp->pfc_lock);
1659 1662                          while (i > 0) {
1660 1663                                  VN_RELE(vpl[--i]);
1661 1664                                  vpl[i] = NULL;
1662 1665                          }
1663 1666                          mutex_enter(&pfcp->pfc_lock);
1664 1667                  }
1665 1668          }
1666 1669  
1667 1670          /*
1668 1671           * Due to a race between port_close_fop() and port_fop()
1669 1672           * trying to remove the pfp's from the port's cache, it is
1670 1673           * possible that some pfp's are still in the process of being
1671 1674           * freed so we wait.
1672 1675           */
1673 1676          while (lastclose && pfcp->pfc_objcount) {
1674 1677                  (void) cv_wait_sig(&pfcp->pfc_lclosecv, &pfcp->pfc_lock);
1675 1678          }
1676 1679          mutex_exit(&pfcp->pfc_lock);
1677 1680          /*
1678 1681           * last close, free the cache.
1679 1682           */
1680 1683          if (lastclose) {
1681 1684                  ASSERT(pfcp->pfc_objcount == 0);
1682 1685                  pse->portsrc_data = NULL;
1683 1686                  kmem_free(pfcp, sizeof (portfop_cache_t));
1684 1687          }
1685 1688  }
1686 1689  
1687 1690  /*
1688 1691   * Given the list of associations(watches), it will send exception events,
1689 1692   * if still active, and discard them. The exception events are handled
1690 1693   * separately because, the pfp needs to be removed from the port cache and
1691 1694   * freed as the vnode's identity is changing or being removed. To remove
1692 1695   * the pfp from the port's cache, we need to hold the cache lock (pfc_lock).
1693 1696   * The lock order is pfc_lock -> pvp_mutex(vnode's) mutex and that is why
1694 1697   * the cache's lock cannot be acquired in port_fop_sendevent().
1695 1698   */
1696 1699  static void
1697 1700  port_fop_excep(list_t *tlist, int op)
1698 1701  {
1699 1702          portfop_t       *pfp;
1700 1703          portfop_cache_t *pfcp;
1701 1704          port_t  *pp;
1702 1705          port_kevent_t   *pkevp;
1703 1706          vnode_t         *tdvp;
1704 1707          int             error = 0;
1705 1708  
1706 1709          while (pfp = (portfop_t *)list_head(tlist)) {
1707 1710                  int removed = 0;
1708 1711                  /*
1709 1712                   * remove from the temp list. Since PORT_FOP_REMOVING is
1710 1713                   * set, no other thread should attempt to perform a
1711 1714                   * list_remove on this pfp.
1712 1715                   */
1713 1716                  list_remove(tlist, pfp);
1714 1717  
1715 1718                  pfcp = pfp->pfop_pcache;
1716 1719                  mutex_enter(&pfcp->pfc_lock);
1717 1720  
1718 1721                  /*
1719 1722                   * Remove the event from the port queue if it was queued up.
1720 1723                   * No need to clear the PORT_FOP_KEV_ONQ flag as this pfp is
1721 1724                   * no longer on the vnode's list.
1722 1725                   */
1723 1726                  if ((pfp->pfop_flags & PORT_FOP_KEV_ONQ)) {
1724 1727                          removed = port_remove_done_event(pfp->pfop_pev);
1725 1728                  }
1726 1729  
1727 1730                  /*
1728 1731                   * If still active or the event was queued up and
1729 1732                   * had not been collected yet, send an EXCEPTION event.
1730 1733                   */
1731 1734                  if (pfp->pfop_flags & (PORT_FOP_ACTIVE) || removed) {
1732 1735                          pp = pfp->pfop_pp;
1733 1736                          /*
1734 1737                           * Allocate a port_kevent_t non cached to send this
1735 1738                           * event since we will be de-registering.
1736 1739                           * The port_kevent_t cannot be pointing back to the
1737 1740                           * pfp anymore.
1738 1741                           */
1739 1742                          pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
1740 1743                          error = port_alloc_event_local(pp, PORT_SOURCE_FILE,
1741 1744                              PORT_ALLOC_DEFAULT, &pkevp);
1742 1745                          if (!error) {
1743 1746  
1744 1747                                  pkevp->portkev_callback = port_fop_callback;
1745 1748                                  pkevp->portkev_arg = NULL;
1746 1749                                  pkevp->portkev_object =
1747 1750                                      pfp->pfop_pev->portkev_object;
1748 1751                                  pkevp->portkev_user =
1749 1752                                      pfp->pfop_pev->portkev_user;
1750 1753                                  /*
1751 1754                                   * Copy the pid of the watching process.
1752 1755                                   */
1753 1756                                  pkevp->portkev_pid =
1754 1757                                      pfp->pfop_pev->portkev_pid;
1755 1758                                  pkevp->portkev_events = op;
1756 1759                                  port_send_event(pkevp);
1757 1760                          }
1758 1761                  }
1759 1762                  /*
1760 1763                   * At this point the pfp has been removed from the vnode's
1761 1764                   * list its cached port_kevent_t is not on the done queue.
1762 1765                   * Remove the pfp and free it from the cache.
1763 1766                   */
1764 1767                  tdvp = pfp->pfop_dvp;
1765 1768                  port_pcache_remove_fop(pfcp, pfp);
1766 1769                  mutex_exit(&pfcp->pfc_lock);
1767 1770                  if (tdvp != NULL)
1768 1771                          VN_RELE(tdvp);
1769 1772          }
1770 1773  }
1771 1774  
1772 1775  /*
1773 1776   * Send the file events to all of the processes watching this
1774 1777   * vnode. In case of hard links, the directory vnode pointer and
1775 1778   * the file name are compared. If the names match, then the specified
1776 1779   * event is sent or else, the FILE_ATTRIB event is sent, This is the
1777 1780   * documented behavior.
1778 1781   */
1779 1782  void
1780 1783  port_fop_sendevent(vnode_t *vp, int events, vnode_t *dvp, char *cname)
1781 1784  {
1782 1785          port_kevent_t   *pkevp;
1783 1786          portfop_t       *pfp, *npfp;
1784 1787          portfop_vp_t    *pvp;
1785 1788          list_t          tmplist;
1786 1789          int             removeall = 0;
1787 1790  
1788 1791          pvp = (portfop_vp_t *)vp->v_fopdata;
1789 1792          mutex_enter(&pvp->pvp_mutex);
1790 1793  
1791 1794          /*
1792 1795           * Check if the list is empty.
1793 1796           *
1794 1797           * All entries have been removed by some other thread.
1795 1798           * The vnode may be still active and we got called,
1796 1799           * but some other thread is in the process of removing the hooks.
1797 1800           */
1798 1801          if (!list_head(&pvp->pvp_pfoplist)) {
1799 1802                  mutex_exit(&pvp->pvp_mutex);
1800 1803                  return;
1801 1804          }
1802 1805  
1803 1806          if ((events & (FILE_EXCEPTION))) {
1804 1807                  /*
1805 1808                   * If it is an event for which we are going to remove
1806 1809                   * the watches so just move it a temporary list and
1807 1810                   * release this vnode.
1808 1811                   */
1809 1812                  list_create(&tmplist, sizeof (portfop_t),
1810 1813                      offsetof(portfop_t, pfop_node));
1811 1814  
1812 1815                  /*
1813 1816                   * If it is an UNMOUNT, MOUNTEDOVER or no file name has been
1814 1817                   * passed for an exception event, all associations need to be
1815 1818                   * removed.
1816 1819                   */
1817 1820                  if (dvp == NULL || cname == NULL) {
1818 1821                          removeall = 1;
1819 1822                  }
1820 1823          }
1821 1824  
1822 1825          if (!removeall) {
1823 1826                  /*
1824 1827                   * All the active ones are in the beginning of the list.
1825 1828                   */
1826 1829                  for (pfp = (portfop_t *)list_head(&pvp->pvp_pfoplist);
1827 1830                      pfp && pfp->pfop_flags & PORT_FOP_ACTIVE; pfp = npfp) {
1828 1831                          int levents = events;
1829 1832  
1830 1833                          npfp = list_next(&pvp->pvp_pfoplist, pfp);
1831 1834                          /*
1832 1835                           * Hard links case - If the file is being
1833 1836                           * removed/renamed, and the name matches
1834 1837                           * the watched file, then it is an EXCEPTION
1835 1838                           * event or else it will be just a FILE_ATTRIB.
1836 1839                           */
1837 1840                          if ((events & (FILE_EXCEPTION))) {
1838 1841                                  ASSERT(dvp != NULL && cname != NULL);
1839 1842                                  if (pfp->pfop_dvp == NULL ||
1840 1843                                      (pfp->pfop_dvp == dvp &&
1841 1844                                      (strcmp(cname, pfp->pfop_cname) == 0))) {
1842 1845                                          /*
1843 1846                                           * It is an exception event, move it
1844 1847                                           * to temp list and process it later.
1845 1848                                           * Note we don't set the pfp->pfop_vp
1846 1849                                           * to NULL even thought it has been
1847 1850                                           * removed from the vnode's list. This
1848 1851                                           * pointer is referenced in
1849 1852                                           * port_remove_fop(). The vnode it
1850 1853                                           * self cannot disappear until this
1851 1854                                           * pfp gets removed and freed.
1852 1855                                           */
1853 1856                                          port_fop_listremove(pvp, pfp);
1854 1857                                          list_insert_tail(&tmplist, (void *)pfp);
1855 1858                                          pfp->pfop_flags  |= PORT_FOP_REMOVING;
1856 1859                                          continue;
1857 1860                                  } else {
1858 1861                                          levents = FILE_ATTRIB;
1859 1862                                  }
1860 1863  
1861 1864                          }
1862 1865  
1863 1866                          if (pfp->pfop_events & levents) {
1864 1867                                  /*
1865 1868                                   * deactivate and move it to the tail.
1866 1869                                   * If the pfp was active, it cannot be
1867 1870                                   * on the port's done queue.
1868 1871                                   */
1869 1872                                  pfp->pfop_flags &= ~PORT_FOP_ACTIVE;
1870 1873                                  port_fop_listremove(pvp, pfp);
1871 1874                                  port_fop_listinsert_tail(pvp, pfp);
1872 1875  
1873 1876                                  pkevp = pfp->pfop_pev;
1874 1877                                  pkevp->portkev_events |=
1875 1878                                      (levents & pfp->pfop_events);
1876 1879                                  port_send_event(pkevp);
1877 1880                                  pfp->pfop_flags |= PORT_FOP_KEV_ONQ;
1878 1881                          }
1879 1882                  }
1880 1883          }
1881 1884  
1882 1885  
1883 1886          if ((events & (FILE_EXCEPTION))) {
1884 1887                  if (!removeall) {
1885 1888                          /*
1886 1889                           * Check the inactive associations and remove them if
1887 1890                           * the file name matches.
1888 1891                           */
1889 1892                          for (; pfp; pfp = npfp) {
1890 1893                                  npfp = list_next(&pvp->pvp_pfoplist, pfp);
1891 1894                                  if (dvp == NULL || cname == NULL ||
1892 1895                                      pfp->pfop_dvp == NULL ||
1893 1896                                      (pfp->pfop_dvp == dvp &&
1894 1897                                      (strcmp(cname, pfp->pfop_cname) == 0))) {
1895 1898                                          port_fop_listremove(pvp, pfp);
1896 1899                                          list_insert_tail(&tmplist, (void *)pfp);
1897 1900                                          pfp->pfop_flags  |= PORT_FOP_REMOVING;
1898 1901                                  }
1899 1902                          }
1900 1903                  } else {
1901 1904                          /*
1902 1905                           * Can be optimized to avoid two pass over this list
1903 1906                           * by having a flag in the vnode's portfop_vp_t
1904 1907                           * structure to indicate that it is going away,
1905 1908                           * Or keep the list short by reusing inactive watches.
1906 1909                           */
1907 1910                          port_fop_listmove(pvp, &tmplist);
1908 1911                          for (pfp = (portfop_t *)list_head(&tmplist);
1909 1912                              pfp; pfp = list_next(&tmplist, pfp)) {
1910 1913                                  pfp->pfop_flags |= PORT_FOP_REMOVING;
1911 1914                          }
1912 1915                  }
1913 1916  
1914 1917                  /*
1915 1918                   * Uninstall the fem hooks if there are no more associations.
1916 1919                   * This will release the pvp mutex.
1917 1920                   *
1918 1921                   * Even thought all entries may have been removed,
1919 1922                   * the vnode itself cannot disappear as there will be a
1920 1923                   * hold on it due to this call to port_fop_sendevent. This is
1921 1924                   * important to syncronize with a port_dissociate_fop() call
1922 1925                   * that may be attempting to remove an object from the vnode's.
1923 1926                   */
1924 1927                  if (port_fop_femuninstall(vp))
1925 1928                          VN_RELE(vp);
1926 1929  
1927 1930                  /*
1928 1931                   * Send exception events and discard the watch entries.
1929 1932                   */
1930 1933                  port_fop_excep(&tmplist, events);
1931 1934                  list_destroy(&tmplist);
1932 1935  
1933 1936          } else {
1934 1937                  mutex_exit(&pvp->pvp_mutex);
1935 1938  
1936 1939                  /*
1937 1940                   * trim the list.
1938 1941                   */
1939 1942                  port_fop_trimpfplist(vp);
1940 1943          }
1941 1944  }
1942 1945  
1943 1946  /*
1944 1947   * Given the file operation, map it to the event types and send.
1945 1948   */
1946 1949  void
1947 1950  port_fop(vnode_t *vp, int op, int retval)
1948 1951  {
1949 1952          int event = 0;
1950 1953          /*
1951 1954           * deliver events only if the operation was successful.
1952 1955           */
1953 1956          if (retval)
1954 1957                  return;
1955 1958  
1956 1959          /*
1957 1960           * These events occurring on the watched file.
  
    | 
      ↓ open down ↓ | 
    1922 lines elided | 
    
      ↑ open up ↑ | 
  
1958 1961           */
1959 1962          if (op & FOP_MODIFIED_MASK) {
1960 1963                  event  = FILE_MODIFIED;
1961 1964          }
1962 1965          if (op & FOP_ACCESS_MASK) {
1963 1966                  event  |= FILE_ACCESS;
1964 1967          }
1965 1968          if (op & FOP_ATTRIB_MASK) {
1966 1969                  event  |= FILE_ATTRIB;
1967 1970          }
1968      -
     1971 +        if (op & FOP_TRUNC_MASK) {
     1972 +                event  |= FILE_TRUNC;
     1973 +        }
1969 1974          if (event) {
1970 1975                  port_fop_sendevent(vp,  event, NULL, NULL);
1971 1976          }
1972 1977  }
1973 1978  
1974 1979  static int port_forceunmount(vfs_t *vfsp)
1975 1980  {
1976 1981          char *fsname = vfssw[vfsp->vfs_fstype].vsw_name;
1977 1982  
1978 1983          if (fsname == NULL) {
1979 1984                  return (0);
1980 1985          }
1981 1986  
1982 1987          if (strcmp(fsname, MNTTYPE_NFS) == 0) {
1983 1988                  return (1);
1984 1989          }
1985 1990  
1986 1991          if (strcmp(fsname, MNTTYPE_NFS3) == 0) {
1987 1992                  return (1);
1988 1993          }
1989 1994  
1990 1995          if (strcmp(fsname, MNTTYPE_NFS4) == 0) {
1991 1996                  return (1);
1992 1997          }
1993 1998          return (0);
1994 1999  }
1995 2000  /*
1996 2001   * ----- the unmount filesystem op(fsem) hook.
1997 2002   */
1998 2003  int
1999 2004  port_fop_unmount(fsemarg_t *vf, int flag, cred_t *cr)
2000 2005  {
2001 2006          vfs_t   *vfsp = (vfs_t *)vf->fa_fnode->fn_available;
2002 2007          kmutex_t        *mtx;
2003 2008          portfop_vfs_t   *pvfsp, **ppvfsp;
2004 2009          portfop_vp_t    *pvp;
2005 2010          int error;
2006 2011          int fmfs;
2007 2012  
2008 2013          fmfs = port_forceunmount(vfsp);
2009 2014  
2010 2015          mtx = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_mutex);
2011 2016          ppvfsp = &(portvfs_hash[PORTFOP_PVFSHASH(vfsp)].pvfshash_pvfsp);
2012 2017          pvfsp = NULL;
2013 2018          mutex_enter(mtx);
2014 2019          /*
2015 2020           * since this fsem hook is triggered, the vfsp has to be on
2016 2021           * the hash list.
2017 2022           */
2018 2023          for (pvfsp = *ppvfsp; pvfsp->pvfs != vfsp; pvfsp = pvfsp->pvfs_next)
2019 2024          ;
2020 2025  
2021 2026          /*
2022 2027           * For some of the filesystems, allow unmounts to proceed only if
2023 2028           * there are no files being watched or it is a forced unmount.
2024 2029           */
2025 2030          if (fmfs && !(flag & MS_FORCE) &&
2026 2031              !list_is_empty(&pvfsp->pvfs_pvplist)) {
2027 2032                  mutex_exit(mtx);
2028 2033                  return (EBUSY);
2029 2034          }
2030 2035  
2031 2036          /*
2032 2037           * Indicate that the unmount is in process. Don't remove it yet.
2033 2038           * The underlying filesystem unmount routine sets the VFS_UNMOUNTED
2034 2039           * flag on the vfs_t structure. But we call the filesystem unmount
2035 2040           * routine after removing all the file watches for this filesystem,
2036 2041           * otherwise the unmount will fail due to active vnodes.
2037 2042           * Meanwhile setting pvfsp->unmount = 1 will prevent any thread
2038 2043           * attempting to add a file watch.
2039 2044           */
2040 2045          pvfsp->pvfs_unmount = 1;
2041 2046          mutex_exit(mtx);
2042 2047  
2043 2048          /*
2044 2049           * uninstall the fsem hooks.
2045 2050           */
2046 2051          (void) fsem_uninstall(vfsp, (fsem_t *)pvfsp->pvfs_fsemp, vfsp);
2047 2052  
2048 2053          while (pvp = list_head(&pvfsp->pvfs_pvplist)) {
2049 2054                  list_remove(&pvfsp->pvfs_pvplist, pvp);
2050 2055                  /*
2051 2056                   * This should send an UNMOUNTED event to all the
2052 2057                   * watched vnode of this filesystem and uninstall
2053 2058                   * the fem hooks. We release the hold on the vnode here
2054 2059                   * because port_fop_femuninstall() will not do it if
2055 2060                   * unmount is in process.
2056 2061                   */
2057 2062                  port_fop_sendevent(pvp->pvp_vp, UNMOUNTED, NULL, NULL);
2058 2063                  VN_RELE(pvp->pvp_vp);
2059 2064          }
2060 2065  
2061 2066          error = vfsnext_unmount(vf, flag, cr);
2062 2067  
2063 2068          /*
2064 2069           * we free the pvfsp after the unmount has been completed.
2065 2070           */
2066 2071          mutex_enter(mtx);
2067 2072          for (; *ppvfsp && (*ppvfsp)->pvfs != vfsp;
2068 2073              ppvfsp = &(*ppvfsp)->pvfs_next)
2069 2074          ;
2070 2075  
2071 2076          /*
2072 2077           * remove and free it.
2073 2078           */
2074 2079          ASSERT(list_head(&pvfsp->pvfs_pvplist) == NULL);
2075 2080          if (*ppvfsp) {
2076 2081                  pvfsp = *ppvfsp;
2077 2082                  *ppvfsp = pvfsp->pvfs_next;
2078 2083          }
2079 2084          mutex_exit(mtx);
2080 2085          kmem_free(pvfsp, sizeof (portfop_vfs_t));
2081 2086          return (error);
2082 2087  }
2083 2088  
2084 2089  /*
2085 2090   * ------------------------------file op hooks--------------------------
2086 2091   * The O_TRUNC operation is caught with the VOP_SETATTR(AT_SIZE) call.
2087 2092   */
2088 2093  static int
2089 2094  port_fop_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
2090 2095  {
2091 2096          int             retval;
2092 2097          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2093 2098  
2094 2099          retval = vnext_open(vf, mode, cr, ct);
2095 2100          port_fop(vp, FOP_FILE_OPEN, retval);
2096 2101          return (retval);
2097 2102  }
2098 2103  
2099 2104  static int
2100 2105  port_fop_write(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
2101 2106      caller_context_t *ct)
2102 2107  {
2103 2108          int             retval;
2104 2109          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2105 2110  
2106 2111          retval =  vnext_write(vf, uiop, ioflag, cr, ct);
2107 2112          port_fop(vp, FOP_FILE_WRITE, retval);
2108 2113          return (retval);
2109 2114  }
2110 2115  
2111 2116  static int
2112 2117  port_fop_map(femarg_t *vf, offset_t off, struct as *as, caddr_t *addrp,
2113 2118      size_t len, uchar_t prot, uchar_t maxport, uint_t flags, cred_t *cr,
2114 2119      caller_context_t *ct)
2115 2120  {
2116 2121          int             retval;
2117 2122          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2118 2123  
2119 2124          retval =  vnext_map(vf, off, as, addrp, len, prot, maxport,
2120 2125              flags, cr, ct);
2121 2126          port_fop(vp, FOP_FILE_MAP, retval);
2122 2127          return (retval);
2123 2128  }
2124 2129  
2125 2130  static int
2126 2131  port_fop_read(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
2127 2132      caller_context_t *ct)
2128 2133  {
2129 2134          int             retval;
2130 2135          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2131 2136  
2132 2137          retval =  vnext_read(vf, uiop, ioflag, cr, ct);
2133 2138          port_fop(vp, FOP_FILE_READ, retval);
2134 2139          return (retval);
2135 2140  }
2136 2141  
2137 2142  
2138 2143  /*
2139 2144   * AT_SIZE - is for the open(O_TRUNC) case.
  
    | 
      ↓ open down ↓ | 
    161 lines elided | 
    
      ↑ open up ↑ | 
  
2140 2145   */
2141 2146  int
2142 2147  port_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
2143 2148      caller_context_t *ct)
2144 2149  {
2145 2150          int             retval;
2146 2151          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2147 2152          int             events = 0;
2148 2153  
2149 2154          retval = vnext_setattr(vf, vap, flags, cr, ct);
     2155 +        if (vap->va_mask & AT_SIZE) {
     2156 +                events |= FOP_FILE_TRUNC;
     2157 +        }
2150 2158          if (vap->va_mask & (AT_SIZE|AT_MTIME)) {
2151 2159                  events |= FOP_FILE_SETATTR_MTIME;
2152 2160          }
2153 2161          if (vap->va_mask & AT_ATIME) {
2154 2162                  events |= FOP_FILE_SETATTR_ATIME;
2155 2163          }
2156 2164          events |= FOP_FILE_SETATTR_CTIME;
2157 2165  
2158 2166          port_fop(vp, events, retval);
2159 2167          return (retval);
2160 2168  }
2161 2169  
2162 2170  int
2163 2171  port_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
2164 2172      int mode, vnode_t **vpp, cred_t *cr, int flag,
2165 2173      caller_context_t *ct, vsecattr_t *vsecp)
2166 2174  {
2167 2175          int             retval, got = 1;
2168 2176          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2169 2177          vattr_t         vatt, vatt1;
2170 2178  
2171 2179          /*
2172 2180           * If the file already exists, then there will be no change
2173 2181           * to the directory. Therefore, we need to compare the
2174 2182           * modification time of the directory to determine if the
2175 2183           * file was actually created.
2176 2184           */
2177 2185          vatt.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
2178 2186          if (VOP_GETATTR(vp, &vatt, 0, CRED(), ct)) {
2179 2187                  got = 0;
2180 2188          }
2181 2189          retval = vnext_create(vf, name, vap, excl, mode, vpp, cr,
2182 2190              flag, ct, vsecp);
2183 2191  
2184 2192          vatt1.va_mask = AT_ATIME|AT_MTIME|AT_CTIME;
2185 2193          if (got && !VOP_GETATTR(vp, &vatt1, 0, CRED(), ct)) {
2186 2194                  if ((vatt1.va_mtime.tv_sec > vatt.va_mtime.tv_sec ||
2187 2195                      (vatt1.va_mtime.tv_sec = vatt.va_mtime.tv_sec &&
2188 2196                      vatt1.va_mtime.tv_nsec > vatt.va_mtime.tv_nsec))) {
2189 2197                          /*
2190 2198                           * File was created.
2191 2199                           */
2192 2200                          port_fop(vp, FOP_FILE_CREATE, retval);
2193 2201                  }
2194 2202          }
2195 2203          return (retval);
2196 2204  }
2197 2205  
2198 2206  int
2199 2207  port_fop_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
2200 2208      int flags)
2201 2209  {
2202 2210          int             retval;
2203 2211          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2204 2212  
2205 2213          retval = vnext_remove(vf, nm, cr, ct, flags);
2206 2214          port_fop(vp, FOP_FILE_REMOVE, retval);
2207 2215          return (retval);
2208 2216  }
2209 2217  
2210 2218  int
2211 2219  port_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
2212 2220      caller_context_t *ct, int flags)
2213 2221  {
2214 2222          int             retval;
2215 2223          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2216 2224  
2217 2225          retval = vnext_link(vf, svp, tnm, cr, ct, flags);
2218 2226          port_fop(vp, FOP_FILE_LINK, retval);
2219 2227          return (retval);
2220 2228  }
2221 2229  
2222 2230  /*
2223 2231   * Rename operation is allowed only when from and to directories are
2224 2232   * on the same filesystem. This is checked in vn_rename().
2225 2233   * The target directory is notified thru a VNEVENT by the filesystem
2226 2234   * if the source dir != target dir.
2227 2235   */
2228 2236  int
2229 2237  port_fop_rename(femarg_t *vf, char *snm, vnode_t *tdvp, char *tnm, cred_t *cr,
2230 2238      caller_context_t *ct, int flags)
2231 2239  {
2232 2240          int             retval;
2233 2241          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2234 2242  
2235 2243          retval = vnext_rename(vf, snm, tdvp, tnm, cr, ct, flags);
2236 2244          port_fop(vp, FOP_FILE_RENAMESRC, retval);
2237 2245          return (retval);
2238 2246  }
2239 2247  
2240 2248  int
2241 2249  port_fop_mkdir(femarg_t *vf, char *dirname, vattr_t *vap, vnode_t **vpp,
2242 2250      cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
2243 2251  {
2244 2252          int             retval;
2245 2253          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2246 2254  
2247 2255          retval = vnext_mkdir(vf, dirname, vap, vpp, cr, ct, flags, vsecp);
2248 2256          port_fop(vp, FOP_FILE_MKDIR, retval);
2249 2257          return (retval);
2250 2258  }
2251 2259  
2252 2260  int
2253 2261  port_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
2254 2262      caller_context_t *ct, int flags)
2255 2263  {
2256 2264          int             retval;
2257 2265          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2258 2266  
2259 2267          retval = vnext_rmdir(vf, nm, cdir, cr, ct, flags);
2260 2268          port_fop(vp, FOP_FILE_RMDIR, retval);
2261 2269          return (retval);
2262 2270  }
2263 2271  
2264 2272  int
2265 2273  port_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
2266 2274      caller_context_t *ct, int flags)
2267 2275  {
2268 2276          int             retval;
2269 2277          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2270 2278  
2271 2279          retval = vnext_readdir(vf, uiop, cr, eofp, ct, flags);
2272 2280          port_fop(vp, FOP_FILE_READDIR, retval);
2273 2281          return (retval);
2274 2282  }
2275 2283  
2276 2284  int
2277 2285  port_fop_symlink(femarg_t *vf, char *linkname, vattr_t *vap, char *target,
2278 2286      cred_t *cr, caller_context_t *ct, int flags)
2279 2287  {
2280 2288          int             retval;
2281 2289          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2282 2290  
2283 2291          retval = vnext_symlink(vf, linkname, vap, target, cr, ct, flags);
2284 2292          port_fop(vp, FOP_FILE_SYMLINK, retval);
2285 2293          return (retval);
2286 2294  }
2287 2295  
2288 2296  /*
2289 2297   * acl, facl call this.
2290 2298   */
2291 2299  int
2292 2300  port_fop_setsecattr(femarg_t *vf, vsecattr_t *vsap, int flags, cred_t *cr,
2293 2301      caller_context_t *ct)
2294 2302  {
2295 2303          int     retval;
2296 2304          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2297 2305          retval = vnext_setsecattr(vf, vsap, flags, cr, ct);
2298 2306          port_fop(vp, FOP_FILE_SETSECATTR, retval);
2299 2307          return (retval);
2300 2308  }
2301 2309  
2302 2310  /*
2303 2311   * these are events on the watched file/directory
2304 2312   */
2305 2313  int
2306 2314  port_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name,
2307 2315      caller_context_t *ct)
2308 2316  {
2309 2317          vnode_t         *vp = (vnode_t *)vf->fa_fnode->fn_available;
2310 2318  
2311 2319          switch (vnevent) {
2312 2320          case    VE_RENAME_SRC:
2313 2321                          port_fop_sendevent(vp, FILE_RENAME_FROM, dvp, name);
2314 2322                  break;
  
    | 
      ↓ open down ↓ | 
    155 lines elided | 
    
      ↑ open up ↑ | 
  
2315 2323          case    VE_RENAME_DEST:
2316 2324                          port_fop_sendevent(vp, FILE_RENAME_TO, dvp, name);
2317 2325                  break;
2318 2326          case    VE_REMOVE:
2319 2327                          port_fop_sendevent(vp, FILE_DELETE, dvp, name);
2320 2328                  break;
2321 2329          case    VE_RMDIR:
2322 2330                          port_fop_sendevent(vp, FILE_DELETE, dvp, name);
2323 2331                  break;
2324 2332          case    VE_CREATE:
2325      -                        port_fop_sendevent(vp, FILE_MODIFIED|FILE_ATTRIB,
2326      -                            NULL, NULL);
     2333 +                        port_fop_sendevent(vp,
     2334 +                            FILE_MODIFIED|FILE_ATTRIB|FILE_TRUNC, NULL, NULL);
2327 2335                  break;
2328 2336          case    VE_LINK:
2329 2337                          port_fop_sendevent(vp, FILE_ATTRIB, NULL, NULL);
2330 2338                  break;
2331 2339  
2332 2340          case    VE_RENAME_DEST_DIR:
2333 2341                          port_fop_sendevent(vp, FILE_MODIFIED|FILE_ATTRIB,
2334 2342                              NULL, NULL);
2335 2343                  break;
2336 2344  
2337 2345          case    VE_MOUNTEDOVER:
2338 2346                          port_fop_sendevent(vp, MOUNTEDOVER, NULL, NULL);
2339 2347                  break;
2340 2348          default:
2341 2349                  break;
2342 2350          }
2343 2351          return (vnext_vnevent(vf, vnevent, dvp, name, ct));
2344 2352  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX