Print this page
195 Need replacement for nfs/lockd+klm
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/os/share.c
+++ new/usr/src/uts/common/os/share.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 2008 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 25
26 +/*
27 + * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
28 + */
29 +
26 30 #include <sys/types.h>
27 31 #include <sys/sysmacros.h>
28 32 #include <sys/param.h>
29 33 #include <sys/systm.h>
30 34 #include <sys/fcntl.h>
31 35 #include <sys/vfs.h>
32 36 #include <sys/vnode.h>
33 37 #include <sys/share.h>
34 38 #include <sys/cmn_err.h>
35 39 #include <sys/kmem.h>
36 40 #include <sys/debug.h>
37 41 #include <sys/t_lock.h>
38 42 #include <sys/errno.h>
↓ open down ↓ |
3 lines elided |
↑ open up ↑ |
39 43 #include <sys/nbmlock.h>
40 44
41 45 int share_debug = 0;
42 46
43 47 #ifdef DEBUG
44 48 static void print_shares(struct vnode *);
45 49 static void print_share(struct shrlock *);
46 50 #endif
47 51
48 52 static int isreadonly(struct vnode *);
53 +static void do_cleanshares(struct vnode *, pid_t, int32_t);
49 54
55 +
50 56 /*
51 57 * Add the share reservation shr to vp.
52 58 */
53 59 int
54 60 add_share(struct vnode *vp, struct shrlock *shr)
55 61 {
56 62 struct shrlocklist *shrl;
57 63
58 64 /*
59 65 * An access of zero is not legal, however some older clients
60 66 * generate it anyways. Allow the request only if it is
61 67 * coming from a remote system. Be generous in what you
62 68 * accept and strict in what you send.
63 69 */
64 70 if ((shr->s_access == 0) && (GETSYSID(shr->s_sysid) == 0)) {
65 71 return (EINVAL);
66 72 }
67 73
68 74 /*
69 75 * Sanity check to make sure we have valid options.
70 76 * There is known overlap but it doesn't hurt to be careful.
71 77 */
72 78 if (shr->s_access & ~(F_RDACC|F_WRACC|F_RWACC|F_RMACC|F_MDACC)) {
73 79 return (EINVAL);
74 80 }
75 81 if (shr->s_deny & ~(F_NODNY|F_RDDNY|F_WRDNY|F_RWDNY|F_COMPAT|
76 82 F_MANDDNY|F_RMDNY)) {
77 83 return (EINVAL);
78 84 }
79 85
80 86 mutex_enter(&vp->v_lock);
81 87 for (shrl = vp->v_shrlocks; shrl != NULL; shrl = shrl->next) {
82 88 /*
83 89 * If the share owner matches previous request
84 90 * do special handling.
85 91 */
86 92 if ((shrl->shr->s_sysid == shr->s_sysid) &&
87 93 (shrl->shr->s_pid == shr->s_pid) &&
88 94 (shrl->shr->s_own_len == shr->s_own_len) &&
89 95 bcmp(shrl->shr->s_owner, shr->s_owner,
90 96 shr->s_own_len) == 0) {
91 97
92 98 /*
93 99 * If the existing request is F_COMPAT and
94 100 * is the first share then allow any F_COMPAT
95 101 * from the same process. Trick: If the existing
96 102 * F_COMPAT is write access then it must have
97 103 * the same owner as the first.
98 104 */
99 105 if ((shrl->shr->s_deny & F_COMPAT) &&
100 106 (shr->s_deny & F_COMPAT) &&
101 107 ((shrl->next == NULL) ||
102 108 (shrl->shr->s_access & F_WRACC)))
103 109 break;
104 110 }
105 111
106 112 /*
107 113 * If a first share has been done in compatibility mode
108 114 * handle the special cases.
109 115 */
110 116 if ((shrl->shr->s_deny & F_COMPAT) && (shrl->next == NULL)) {
111 117
112 118 if (!(shr->s_deny & F_COMPAT)) {
113 119 /*
114 120 * If not compat and want write access or
115 121 * want to deny read or
116 122 * write exists, fails
117 123 */
118 124 if ((shr->s_access & F_WRACC) ||
119 125 (shr->s_deny & F_RDDNY) ||
120 126 (shrl->shr->s_access & F_WRACC)) {
121 127 mutex_exit(&vp->v_lock);
122 128 return (EAGAIN);
123 129 }
124 130 /*
125 131 * If read only file allow, this may allow
126 132 * a deny write but that is meaningless on
127 133 * a read only file.
128 134 */
129 135 if (isreadonly(vp))
130 136 break;
131 137 mutex_exit(&vp->v_lock);
132 138 return (EAGAIN);
133 139 }
134 140 /*
135 141 * This is a compat request and read access
136 142 * and the first was also read access
137 143 * we always allow it, otherwise we reject because
138 144 * we have handled the only valid write case above.
139 145 */
140 146 if ((shr->s_access == F_RDACC) &&
141 147 (shrl->shr->s_access == F_RDACC))
142 148 break;
143 149 mutex_exit(&vp->v_lock);
144 150 return (EAGAIN);
145 151 }
146 152
147 153 /*
148 154 * If we are trying to share in compatibility mode
149 155 * and the current share is compat (and not the first)
150 156 * we don't know enough.
151 157 */
152 158 if ((shrl->shr->s_deny & F_COMPAT) && (shr->s_deny & F_COMPAT))
153 159 continue;
154 160
155 161 /*
156 162 * If this is a compat we check for what can't succeed.
157 163 */
158 164 if (shr->s_deny & F_COMPAT) {
159 165 /*
160 166 * If we want write access or
161 167 * if anyone is denying read or
162 168 * if anyone has write access we fail
163 169 */
164 170 if ((shr->s_access & F_WRACC) ||
165 171 (shrl->shr->s_deny & F_RDDNY) ||
166 172 (shrl->shr->s_access & F_WRACC)) {
167 173 mutex_exit(&vp->v_lock);
168 174 return (EAGAIN);
169 175 }
170 176 /*
171 177 * If the first was opened with only read access
172 178 * and is a read only file we allow.
173 179 */
174 180 if (shrl->next == NULL) {
175 181 if ((shrl->shr->s_access == F_RDACC) &&
176 182 isreadonly(vp)) {
177 183 break;
178 184 }
179 185 mutex_exit(&vp->v_lock);
180 186 return (EAGAIN);
181 187 }
182 188 /*
183 189 * We still can't determine our fate so continue
184 190 */
185 191 continue;
186 192 }
187 193
188 194 /*
189 195 * Simple bitwise test, if we are trying to access what
190 196 * someone else is denying or we are trying to deny
191 197 * what someone else is accessing we fail.
192 198 */
193 199 if ((shr->s_access & shrl->shr->s_deny) ||
194 200 (shr->s_deny & shrl->shr->s_access)) {
195 201 mutex_exit(&vp->v_lock);
196 202 return (EAGAIN);
197 203 }
198 204 }
199 205
200 206 shrl = kmem_alloc(sizeof (struct shrlocklist), KM_SLEEP);
201 207 shrl->shr = kmem_alloc(sizeof (struct shrlock), KM_SLEEP);
202 208 shrl->shr->s_access = shr->s_access;
203 209 shrl->shr->s_deny = shr->s_deny;
204 210
205 211 /*
206 212 * Make sure no other deny modes are also set with F_COMPAT
207 213 */
208 214 if (shrl->shr->s_deny & F_COMPAT)
209 215 shrl->shr->s_deny = F_COMPAT;
210 216 shrl->shr->s_sysid = shr->s_sysid; /* XXX ref cnt? */
211 217 shrl->shr->s_pid = shr->s_pid;
212 218 shrl->shr->s_own_len = shr->s_own_len;
213 219 shrl->shr->s_owner = kmem_alloc(shr->s_own_len, KM_SLEEP);
214 220 bcopy(shr->s_owner, shrl->shr->s_owner, shr->s_own_len);
215 221 shrl->next = vp->v_shrlocks;
216 222 vp->v_shrlocks = shrl;
217 223 #ifdef DEBUG
218 224 if (share_debug)
219 225 print_shares(vp);
220 226 #endif
221 227
222 228 mutex_exit(&vp->v_lock);
223 229
224 230 return (0);
225 231 }
226 232
227 233 /*
228 234 * nlmid sysid pid
229 235 * ===== ===== ===
230 236 * !=0 !=0 =0 in cluster; NLM lock
231 237 * !=0 =0 =0 in cluster; special case for NLM lock
232 238 * !=0 =0 !=0 in cluster; PXFS local lock
233 239 * !=0 !=0 !=0 cannot happen
234 240 * =0 !=0 =0 not in cluster; NLM lock
235 241 * =0 =0 !=0 not in cluster; local lock
236 242 * =0 =0 =0 cannot happen
237 243 * =0 !=0 !=0 cannot happen
238 244 */
239 245 static int
240 246 is_match_for_del(struct shrlock *shr, struct shrlock *element)
241 247 {
242 248 int nlmid1, nlmid2;
243 249 int result = 0;
244 250
245 251 nlmid1 = GETNLMID(shr->s_sysid);
246 252 nlmid2 = GETNLMID(element->s_sysid);
247 253
248 254 if (nlmid1 != 0) { /* in a cluster */
249 255 if (GETSYSID(shr->s_sysid) != 0 && shr->s_pid == 0) {
250 256 /*
251 257 * Lock obtained through nlm server. Just need to
252 258 * compare whole sysids. pid will always = 0.
253 259 */
254 260 result = shr->s_sysid == element->s_sysid;
255 261 } else if (GETSYSID(shr->s_sysid) == 0 && shr->s_pid == 0) {
256 262 /*
257 263 * This is a special case. The NLM server wishes to
258 264 * delete all share locks obtained through nlmid1.
259 265 */
260 266 result = (nlmid1 == nlmid2);
261 267 } else if (GETSYSID(shr->s_sysid) == 0 && shr->s_pid != 0) {
262 268 /*
263 269 * Lock obtained locally through PXFS. Match nlmids
264 270 * and pids.
265 271 */
266 272 result = (nlmid1 == nlmid2 &&
267 273 shr->s_pid == element->s_pid);
268 274 }
269 275 } else { /* not in a cluster */
270 276 result = ((shr->s_sysid == 0 &&
271 277 shr->s_pid == element->s_pid) ||
272 278 (shr->s_sysid != 0 &&
273 279 shr->s_sysid == element->s_sysid));
274 280 }
275 281 return (result);
276 282 }
277 283
278 284 /*
279 285 * Delete the given share reservation. Returns 0 if okay, EINVAL if the
280 286 * share could not be found. If the share reservation is an NBMAND share
281 287 * reservation, signal anyone waiting for the share to go away (e.g.,
282 288 * blocking lock requests).
283 289 */
284 290
285 291 int
286 292 del_share(struct vnode *vp, struct shrlock *shr)
287 293 {
288 294 struct shrlocklist *shrl;
289 295 struct shrlocklist **shrlp;
290 296 int found = 0;
291 297 int is_nbmand = 0;
292 298
293 299 mutex_enter(&vp->v_lock);
294 300 /*
295 301 * Delete the shares with the matching sysid and owner
296 302 * But if own_len == 0 and sysid == 0 delete all with matching pid
297 303 * But if own_len == 0 delete all with matching sysid.
298 304 */
299 305 shrlp = &vp->v_shrlocks;
300 306 while (*shrlp) {
301 307 if ((shr->s_own_len == (*shrlp)->shr->s_own_len &&
302 308 (bcmp(shr->s_owner, (*shrlp)->shr->s_owner,
303 309 shr->s_own_len) == 0)) ||
304 310
305 311 (shr->s_own_len == 0 &&
306 312 is_match_for_del(shr, (*shrlp)->shr))) {
307 313
308 314 shrl = *shrlp;
309 315 *shrlp = shrl->next;
310 316
311 317 if (shrl->shr->s_deny & F_MANDDNY)
312 318 is_nbmand = 1;
313 319
314 320 /* XXX deref sysid */
315 321 kmem_free(shrl->shr->s_owner, shrl->shr->s_own_len);
316 322 kmem_free(shrl->shr, sizeof (struct shrlock));
317 323 kmem_free(shrl, sizeof (struct shrlocklist));
318 324 found++;
319 325 continue;
320 326 }
321 327 shrlp = &(*shrlp)->next;
322 328 }
323 329
324 330 if (is_nbmand)
325 331 cv_broadcast(&vp->v_cv);
326 332
327 333 mutex_exit(&vp->v_lock);
↓ open down ↓ |
268 lines elided |
↑ open up ↑ |
328 334 return (found ? 0 : EINVAL);
329 335 }
330 336
331 337 /*
332 338 * Clean up all local share reservations that the given process has with
333 339 * the given file.
334 340 */
335 341 void
336 342 cleanshares(struct vnode *vp, pid_t pid)
337 343 {
344 + do_cleanshares(vp, pid, 0);
345 +}
346 +
347 +/*
348 + * Cleanup all remote share reservations that
349 + * were made by the given sysid on given vnode.
350 + */
351 +void
352 +cleanshares_by_sysid(struct vnode *vp, int32_t sysid)
353 +{
354 + if (sysid == 0)
355 + return;
356 +
357 + do_cleanshares(vp, 0, sysid);
358 +}
359 +
360 +/*
361 + * Cleanup share reservations on given vnode made
362 + * by the either given pid or sysid.
363 + * If sysid is 0, remove all shares made by given pid,
364 + * otherwise all shares made by the given sysid will
365 + * be removed.
366 + */
367 +static void
368 +do_cleanshares(struct vnode *vp, pid_t pid, int32_t sysid)
369 +{
338 370 struct shrlock shr;
339 371
340 372 if (vp->v_shrlocks == NULL)
341 373 return;
342 374
343 375 shr.s_access = 0;
344 376 shr.s_deny = 0;
345 377 shr.s_pid = pid;
346 - shr.s_sysid = 0;
378 + shr.s_sysid = sysid;
347 379 shr.s_own_len = 0;
348 380 shr.s_owner = NULL;
349 381
350 382 (void) del_share(vp, &shr);
351 383 }
352 384
353 385 static int
354 386 is_match_for_has_remote(int32_t sysid1, int32_t sysid2)
355 387 {
356 388 int result = 0;
357 389
358 390 if (GETNLMID(sysid1) != 0) { /* in a cluster */
359 391 if (GETSYSID(sysid1) != 0) {
360 392 /*
361 393 * Lock obtained through nlm server. Just need to
362 394 * compare whole sysids.
363 395 */
364 396 result = (sysid1 == sysid2);
365 397 } else if (GETSYSID(sysid1) == 0) {
366 398 /*
367 399 * This is a special case. The NLM server identified
368 400 * by nlmid1 wishes to find out if it has obtained
369 401 * any share locks on the vnode.
370 402 */
371 403 result = (GETNLMID(sysid1) == GETNLMID(sysid2));
372 404 }
373 405 } else { /* not in a cluster */
374 406 result = ((sysid1 != 0 && sysid1 == sysid2) ||
375 407 (sysid1 == 0 && sysid2 != 0));
376 408 }
377 409 return (result);
378 410 }
379 411
380 412
381 413 /*
382 414 * Determine whether there are any shares for the given vnode
383 415 * with a remote sysid. Returns zero if not, non-zero if there are.
384 416 * If sysid is non-zero then determine if this sysid has a share.
385 417 *
386 418 * Note that the return value from this function is potentially invalid
387 419 * once it has been returned. The caller is responsible for providing its
388 420 * own synchronization mechanism to ensure that the return value is useful.
389 421 */
390 422 int
391 423 shr_has_remote_shares(vnode_t *vp, int32_t sysid)
392 424 {
393 425 struct shrlocklist *shrl;
394 426 int result = 0;
395 427
396 428 mutex_enter(&vp->v_lock);
397 429 shrl = vp->v_shrlocks;
398 430 while (shrl) {
399 431 if (is_match_for_has_remote(sysid, shrl->shr->s_sysid)) {
400 432
401 433 result = 1;
402 434 break;
403 435 }
404 436 shrl = shrl->next;
405 437 }
406 438 mutex_exit(&vp->v_lock);
407 439 return (result);
408 440 }
409 441
410 442 static int
411 443 isreadonly(struct vnode *vp)
412 444 {
413 445 return (vp->v_type != VCHR && vp->v_type != VBLK &&
414 446 vp->v_type != VFIFO && vn_is_readonly(vp));
415 447 }
416 448
417 449 #ifdef DEBUG
418 450 static void
419 451 print_shares(struct vnode *vp)
420 452 {
421 453 struct shrlocklist *shrl;
422 454
423 455 if (vp->v_shrlocks == NULL) {
424 456 printf("<NULL>\n");
425 457 return;
426 458 }
427 459
428 460 shrl = vp->v_shrlocks;
429 461 while (shrl) {
430 462 print_share(shrl->shr);
431 463 shrl = shrl->next;
432 464 }
433 465 }
434 466
435 467 static void
436 468 print_share(struct shrlock *shr)
437 469 {
438 470 int i;
439 471
440 472 if (shr == NULL) {
441 473 printf("<NULL>\n");
442 474 return;
443 475 }
444 476
445 477 printf(" access(%d): ", shr->s_access);
446 478 if (shr->s_access & F_RDACC)
447 479 printf("R");
448 480 if (shr->s_access & F_WRACC)
449 481 printf("W");
450 482 if ((shr->s_access & (F_RDACC|F_WRACC)) == 0)
451 483 printf("N");
452 484 printf("\n");
453 485 printf(" deny: ");
454 486 if (shr->s_deny & F_COMPAT)
455 487 printf("C");
456 488 if (shr->s_deny & F_RDDNY)
457 489 printf("R");
458 490 if (shr->s_deny & F_WRDNY)
459 491 printf("W");
460 492 if (shr->s_deny == F_NODNY)
461 493 printf("N");
462 494 printf("\n");
463 495 printf(" sysid: %d\n", shr->s_sysid);
464 496 printf(" pid: %d\n", shr->s_pid);
465 497 printf(" owner: [%d]", shr->s_own_len);
466 498 printf("'");
467 499 for (i = 0; i < shr->s_own_len; i++)
468 500 printf("%02x", (unsigned)shr->s_owner[i]);
469 501 printf("'\n");
470 502 }
471 503 #endif
472 504
473 505 /*
474 506 * Return non-zero if the given I/O request conflicts with a registered
475 507 * share reservation.
476 508 *
477 509 * A process is identified by the tuple (sysid, pid). When the caller
478 510 * context is passed to nbl_share_conflict, the sysid and pid in the
479 511 * caller context are used. Otherwise the sysid is zero, and the pid is
480 512 * taken from the current process.
481 513 *
482 514 * Conflict Algorithm:
483 515 * 1. An op request of NBL_READ will fail if a different
484 516 * process has a mandatory share reservation with deny read.
485 517 *
486 518 * 2. An op request of NBL_WRITE will fail if a different
487 519 * process has a mandatory share reservation with deny write.
488 520 *
489 521 * 3. An op request of NBL_READWRITE will fail if a different
490 522 * process has a mandatory share reservation with deny read
491 523 * or deny write.
492 524 *
493 525 * 4. An op request of NBL_REMOVE will fail if there is
494 526 * a mandatory share reservation with an access of read,
495 527 * write, or remove. (Anything other than meta data access).
496 528 *
497 529 * 5. An op request of NBL_RENAME will fail if there is
498 530 * a mandatory share reservation with:
499 531 * a) access write or access remove
500 532 * or
501 533 * b) access read and deny remove
502 534 *
503 535 * Otherwise there is no conflict and the op request succeeds.
504 536 *
505 537 * This behavior is required for interoperability between
506 538 * the nfs server, cifs server, and local access.
507 539 * This behavior can result in non-posix semantics.
508 540 *
509 541 * When mandatory share reservations are enabled, a process
510 542 * should call nbl_share_conflict to determine if the
511 543 * desired operation would conflict with an existing share
512 544 * reservation.
513 545 *
514 546 * The call to nbl_share_conflict may be skipped if the
515 547 * process has an existing share reservation and the operation
516 548 * is being performed in the context of that existing share
517 549 * reservation.
518 550 */
519 551 int
520 552 nbl_share_conflict(vnode_t *vp, nbl_op_t op, caller_context_t *ct)
521 553 {
522 554 struct shrlocklist *shrl;
523 555 int conflict = 0;
524 556 pid_t pid;
525 557 int sysid;
526 558
527 559 ASSERT(nbl_in_crit(vp));
528 560
529 561 if (ct == NULL) {
530 562 pid = curproc->p_pid;
531 563 sysid = 0;
532 564 } else {
533 565 pid = ct->cc_pid;
534 566 sysid = ct->cc_sysid;
535 567 }
536 568
537 569 mutex_enter(&vp->v_lock);
538 570 for (shrl = vp->v_shrlocks; shrl != NULL; shrl = shrl->next) {
539 571 if (!(shrl->shr->s_deny & F_MANDDNY))
540 572 continue;
541 573 /*
542 574 * NBL_READ, NBL_WRITE, and NBL_READWRITE need to
543 575 * check if the share reservation being examined
544 576 * belongs to the current process.
545 577 * NBL_REMOVE and NBL_RENAME do not.
546 578 * This behavior is required by the conflict
547 579 * algorithm described above.
548 580 */
549 581 switch (op) {
550 582 case NBL_READ:
551 583 if ((shrl->shr->s_deny & F_RDDNY) &&
552 584 (shrl->shr->s_sysid != sysid ||
553 585 shrl->shr->s_pid != pid))
554 586 conflict = 1;
555 587 break;
556 588 case NBL_WRITE:
557 589 if ((shrl->shr->s_deny & F_WRDNY) &&
558 590 (shrl->shr->s_sysid != sysid ||
559 591 shrl->shr->s_pid != pid))
560 592 conflict = 1;
561 593 break;
562 594 case NBL_READWRITE:
563 595 if ((shrl->shr->s_deny & F_RWDNY) &&
564 596 (shrl->shr->s_sysid != sysid ||
565 597 shrl->shr->s_pid != pid))
566 598 conflict = 1;
567 599 break;
568 600 case NBL_REMOVE:
569 601 if (shrl->shr->s_access & (F_RWACC|F_RMACC))
570 602 conflict = 1;
571 603 break;
572 604 case NBL_RENAME:
573 605 if (shrl->shr->s_access & (F_WRACC|F_RMACC))
574 606 conflict = 1;
575 607
576 608 else if ((shrl->shr->s_access & F_RDACC) &&
577 609 (shrl->shr->s_deny & F_RMDNY))
578 610 conflict = 1;
579 611 break;
580 612 #ifdef DEBUG
581 613 default:
582 614 cmn_err(CE_PANIC,
583 615 "nbl_share_conflict: bogus op (%d)",
584 616 op);
585 617 break;
586 618 #endif
587 619 }
588 620 if (conflict)
589 621 break;
590 622 }
591 623
592 624 mutex_exit(&vp->v_lock);
593 625 return (conflict);
594 626 }
595 627
596 628 /*
597 629 * Determine if the given process has a NBMAND share reservation on the
598 630 * given vnode. Returns 1 if the process has such a share reservation,
599 631 * returns 0 otherwise.
600 632 */
601 633 int
602 634 proc_has_nbmand_share_on_vp(vnode_t *vp, pid_t pid)
603 635 {
604 636 struct shrlocklist *shrl;
605 637
606 638 /*
607 639 * Any NBMAND share reservation on the vp for this process?
608 640 */
609 641 mutex_enter(&vp->v_lock);
610 642 for (shrl = vp->v_shrlocks; shrl != NULL; shrl = shrl->next) {
611 643 if (shrl->shr->s_sysid == 0 &&
612 644 (shrl->shr->s_deny & F_MANDDNY) &&
613 645 (shrl->shr->s_pid == pid)) {
614 646 mutex_exit(&vp->v_lock);
615 647 return (1);
616 648 }
617 649 }
618 650 mutex_exit(&vp->v_lock);
619 651
620 652 return (0);
621 653 }
↓ open down ↓ |
265 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX