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