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