Print this page
6975745 xattr directories should be more transparent
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/uts/common/fs/xattr.c
+++ new/usr/src/uts/common/fs/xattr.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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 23 */
24 24
25 25 #include <sys/param.h>
26 26 #include <sys/isa_defs.h>
27 27 #include <sys/types.h>
28 28 #include <sys/sysmacros.h>
29 29 #include <sys/cred.h>
30 30 #include <sys/systm.h>
31 31 #include <sys/errno.h>
32 32 #include <sys/fcntl.h>
33 33 #include <sys/pathname.h>
34 34 #include <sys/stat.h>
35 35 #include <sys/vfs.h>
36 36 #include <sys/acl.h>
37 37 #include <sys/file.h>
38 38 #include <sys/sunddi.h>
39 39 #include <sys/debug.h>
40 40 #include <sys/cmn_err.h>
41 41 #include <sys/vnode.h>
42 42 #include <sys/mode.h>
43 43 #include <sys/nvpair.h>
44 44 #include <sys/attr.h>
45 45 #include <sys/gfs.h>
46 46 #include <sys/mutex.h>
↓ open down ↓ |
46 lines elided |
↑ open up ↑ |
47 47 #include <fs/fs_subr.h>
48 48 #include <sys/kidmap.h>
49 49
50 50 typedef struct {
51 51 gfs_file_t xattr_gfs_private;
52 52 xattr_view_t xattr_view;
53 53 } xattr_file_t;
54 54
55 55 typedef struct {
56 56 gfs_dir_t xattr_gfs_private;
57 - vnode_t *xattr_realvp; /* Only used for VOP_REALVP */
57 + vnode_t *xattr_realvp;
58 58 } xattr_dir_t;
59 59
60 -/*
61 - * xattr_realvp is only used for VOP_REALVP, this is so we don't
62 - * keep an unnecessary hold on the *real* xattr dir unless we have
63 - * no other choice.
64 - */
65 -
66 60 /* ARGSUSED */
67 61 static int
68 62 xattr_file_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
69 63 {
70 64 xattr_file_t *np = (*vpp)->v_data;
71 65
72 66 if ((np->xattr_view == XATTR_VIEW_READONLY) && (flags & FWRITE))
73 67 return (EACCES);
74 68
75 69 return (0);
76 70 }
77 71
78 72 /* ARGSUSED */
79 73 static int
80 74 xattr_file_access(vnode_t *vp, int mode, int flags, cred_t *cr,
81 75 caller_context_t *ct)
82 76 {
83 77 xattr_file_t *np = vp->v_data;
84 78
85 79 if ((np->xattr_view == XATTR_VIEW_READONLY) && (mode & VWRITE))
86 80 return (EACCES);
87 81
88 82 return (0);
89 83 }
90 84
91 85 /* ARGSUSED */
92 86 static int
93 87 xattr_file_close(vnode_t *vp, int flags, int count, offset_t off,
94 88 cred_t *cr, caller_context_t *ct)
95 89 {
96 90 cleanlocks(vp, ddi_get_pid(), 0);
97 91 cleanshares(vp, ddi_get_pid());
98 92 return (0);
99 93 }
100 94
101 95 static int
102 96 xattr_common_fid(vnode_t *vp, fid_t *fidp, caller_context_t *ct)
103 97 {
104 98 xattr_fid_t *xfidp;
105 99 vnode_t *pvp, *savevp;
106 100 int error;
107 101 uint16_t orig_len;
108 102
109 103 if (fidp->fid_len < XATTR_FIDSZ) {
110 104 fidp->fid_len = XATTR_FIDSZ;
111 105 return (ENOSPC);
112 106 }
113 107
114 108 savevp = pvp = gfs_file_parent(vp);
115 109 mutex_enter(&savevp->v_lock);
116 110 if (pvp->v_flag & V_XATTRDIR) {
117 111 pvp = gfs_file_parent(pvp);
118 112 }
119 113 mutex_exit(&savevp->v_lock);
120 114
121 115 xfidp = (xattr_fid_t *)fidp;
122 116 orig_len = fidp->fid_len;
123 117 fidp->fid_len = sizeof (xfidp->parent_fid);
124 118
125 119 error = VOP_FID(pvp, fidp, ct);
126 120 if (error) {
127 121 fidp->fid_len = orig_len;
128 122 return (error);
129 123 }
130 124
131 125 xfidp->parent_len = fidp->fid_len;
132 126 fidp->fid_len = XATTR_FIDSZ;
133 127 xfidp->dir_offset = gfs_file_inode(vp);
134 128
135 129 return (0);
136 130 }
137 131
138 132 /* ARGSUSED */
139 133 static int
140 134 xattr_fill_nvlist(vnode_t *vp, xattr_view_t xattr_view, nvlist_t *nvlp,
141 135 cred_t *cr, caller_context_t *ct)
142 136 {
143 137 int error;
144 138 f_attr_t attr;
145 139 uint64_t fsid;
146 140 xvattr_t xvattr;
147 141 xoptattr_t *xoap; /* Pointer to optional attributes */
148 142 vnode_t *ppvp;
149 143 const char *domain;
150 144 uint32_t rid;
151 145
152 146 xva_init(&xvattr);
153 147
154 148 if ((xoap = xva_getxoptattr(&xvattr)) == NULL)
155 149 return (EINVAL);
156 150
157 151 /*
158 152 * For detecting ephemeral uid/gid
159 153 */
160 154 xvattr.xva_vattr.va_mask |= (AT_UID|AT_GID);
161 155
162 156 /*
163 157 * We need to access the real fs object.
164 158 * vp points to a GFS file; ppvp points to the real object.
165 159 */
166 160 ppvp = gfs_file_parent(gfs_file_parent(vp));
167 161
168 162 /*
169 163 * Iterate through the attrs associated with this view
170 164 */
171 165
172 166 for (attr = 0; attr < F_ATTR_ALL; attr++) {
173 167 if (xattr_view != attr_to_xattr_view(attr)) {
174 168 continue;
175 169 }
176 170
177 171 switch (attr) {
178 172 case F_SYSTEM:
179 173 XVA_SET_REQ(&xvattr, XAT_SYSTEM);
180 174 break;
181 175 case F_READONLY:
182 176 XVA_SET_REQ(&xvattr, XAT_READONLY);
183 177 break;
184 178 case F_HIDDEN:
185 179 XVA_SET_REQ(&xvattr, XAT_HIDDEN);
186 180 break;
187 181 case F_ARCHIVE:
188 182 XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
189 183 break;
190 184 case F_IMMUTABLE:
191 185 XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
192 186 break;
193 187 case F_APPENDONLY:
194 188 XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
195 189 break;
196 190 case F_NOUNLINK:
197 191 XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
198 192 break;
199 193 case F_OPAQUE:
200 194 XVA_SET_REQ(&xvattr, XAT_OPAQUE);
201 195 break;
202 196 case F_NODUMP:
203 197 XVA_SET_REQ(&xvattr, XAT_NODUMP);
204 198 break;
205 199 case F_AV_QUARANTINED:
206 200 XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
207 201 break;
208 202 case F_AV_MODIFIED:
209 203 XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
210 204 break;
211 205 case F_AV_SCANSTAMP:
212 206 if (ppvp->v_type == VREG)
213 207 XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
214 208 break;
215 209 case F_CRTIME:
216 210 XVA_SET_REQ(&xvattr, XAT_CREATETIME);
217 211 break;
218 212 case F_FSID:
219 213 fsid = (((uint64_t)vp->v_vfsp->vfs_fsid.val[0] << 32) |
220 214 (uint64_t)(vp->v_vfsp->vfs_fsid.val[1] &
221 215 0xffffffff));
222 216 VERIFY(nvlist_add_uint64(nvlp, attr_to_name(attr),
223 217 fsid) == 0);
224 218 break;
225 219 case F_REPARSE:
226 220 XVA_SET_REQ(&xvattr, XAT_REPARSE);
227 221 break;
228 222 case F_GEN:
229 223 XVA_SET_REQ(&xvattr, XAT_GEN);
230 224 break;
231 225 case F_OFFLINE:
232 226 XVA_SET_REQ(&xvattr, XAT_OFFLINE);
233 227 break;
234 228 case F_SPARSE:
235 229 XVA_SET_REQ(&xvattr, XAT_SPARSE);
236 230 break;
237 231 default:
238 232 break;
239 233 }
240 234 }
241 235
242 236 error = VOP_GETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
243 237 if (error)
244 238 return (error);
245 239
246 240 /*
247 241 * Process all the optional attributes together here. Notice that
248 242 * xoap was set when the optional attribute bits were set above.
249 243 */
250 244 if ((xvattr.xva_vattr.va_mask & AT_XVATTR) && xoap) {
251 245 if (XVA_ISSET_RTN(&xvattr, XAT_READONLY)) {
252 246 VERIFY(nvlist_add_boolean_value(nvlp,
253 247 attr_to_name(F_READONLY),
254 248 xoap->xoa_readonly) == 0);
255 249 }
256 250 if (XVA_ISSET_RTN(&xvattr, XAT_HIDDEN)) {
257 251 VERIFY(nvlist_add_boolean_value(nvlp,
258 252 attr_to_name(F_HIDDEN),
259 253 xoap->xoa_hidden) == 0);
260 254 }
261 255 if (XVA_ISSET_RTN(&xvattr, XAT_SYSTEM)) {
262 256 VERIFY(nvlist_add_boolean_value(nvlp,
263 257 attr_to_name(F_SYSTEM),
264 258 xoap->xoa_system) == 0);
265 259 }
266 260 if (XVA_ISSET_RTN(&xvattr, XAT_ARCHIVE)) {
267 261 VERIFY(nvlist_add_boolean_value(nvlp,
268 262 attr_to_name(F_ARCHIVE),
269 263 xoap->xoa_archive) == 0);
270 264 }
271 265 if (XVA_ISSET_RTN(&xvattr, XAT_IMMUTABLE)) {
272 266 VERIFY(nvlist_add_boolean_value(nvlp,
273 267 attr_to_name(F_IMMUTABLE),
274 268 xoap->xoa_immutable) == 0);
275 269 }
276 270 if (XVA_ISSET_RTN(&xvattr, XAT_NOUNLINK)) {
277 271 VERIFY(nvlist_add_boolean_value(nvlp,
278 272 attr_to_name(F_NOUNLINK),
279 273 xoap->xoa_nounlink) == 0);
280 274 }
281 275 if (XVA_ISSET_RTN(&xvattr, XAT_APPENDONLY)) {
282 276 VERIFY(nvlist_add_boolean_value(nvlp,
283 277 attr_to_name(F_APPENDONLY),
284 278 xoap->xoa_appendonly) == 0);
285 279 }
286 280 if (XVA_ISSET_RTN(&xvattr, XAT_NODUMP)) {
287 281 VERIFY(nvlist_add_boolean_value(nvlp,
288 282 attr_to_name(F_NODUMP),
289 283 xoap->xoa_nodump) == 0);
290 284 }
291 285 if (XVA_ISSET_RTN(&xvattr, XAT_OPAQUE)) {
292 286 VERIFY(nvlist_add_boolean_value(nvlp,
293 287 attr_to_name(F_OPAQUE),
294 288 xoap->xoa_opaque) == 0);
295 289 }
296 290 if (XVA_ISSET_RTN(&xvattr, XAT_AV_QUARANTINED)) {
297 291 VERIFY(nvlist_add_boolean_value(nvlp,
298 292 attr_to_name(F_AV_QUARANTINED),
299 293 xoap->xoa_av_quarantined) == 0);
300 294 }
301 295 if (XVA_ISSET_RTN(&xvattr, XAT_AV_MODIFIED)) {
302 296 VERIFY(nvlist_add_boolean_value(nvlp,
303 297 attr_to_name(F_AV_MODIFIED),
304 298 xoap->xoa_av_modified) == 0);
305 299 }
306 300 if (XVA_ISSET_RTN(&xvattr, XAT_AV_SCANSTAMP)) {
307 301 VERIFY(nvlist_add_uint8_array(nvlp,
308 302 attr_to_name(F_AV_SCANSTAMP),
309 303 xoap->xoa_av_scanstamp,
310 304 sizeof (xoap->xoa_av_scanstamp)) == 0);
311 305 }
312 306 if (XVA_ISSET_RTN(&xvattr, XAT_CREATETIME)) {
313 307 VERIFY(nvlist_add_uint64_array(nvlp,
314 308 attr_to_name(F_CRTIME),
315 309 (uint64_t *)&(xoap->xoa_createtime),
316 310 sizeof (xoap->xoa_createtime) /
317 311 sizeof (uint64_t)) == 0);
318 312 }
319 313 if (XVA_ISSET_RTN(&xvattr, XAT_REPARSE)) {
320 314 VERIFY(nvlist_add_boolean_value(nvlp,
321 315 attr_to_name(F_REPARSE),
322 316 xoap->xoa_reparse) == 0);
323 317 }
324 318 if (XVA_ISSET_RTN(&xvattr, XAT_GEN)) {
325 319 VERIFY(nvlist_add_uint64(nvlp,
326 320 attr_to_name(F_GEN),
327 321 xoap->xoa_generation) == 0);
328 322 }
329 323 if (XVA_ISSET_RTN(&xvattr, XAT_OFFLINE)) {
330 324 VERIFY(nvlist_add_boolean_value(nvlp,
331 325 attr_to_name(F_OFFLINE),
332 326 xoap->xoa_offline) == 0);
333 327 }
334 328 if (XVA_ISSET_RTN(&xvattr, XAT_SPARSE)) {
335 329 VERIFY(nvlist_add_boolean_value(nvlp,
336 330 attr_to_name(F_SPARSE),
337 331 xoap->xoa_sparse) == 0);
338 332 }
339 333 }
340 334 /*
341 335 * Check for optional ownersid/groupsid
342 336 */
343 337
344 338 if (xvattr.xva_vattr.va_uid > MAXUID) {
345 339 nvlist_t *nvl_sid;
346 340
347 341 if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
348 342 return (ENOMEM);
349 343
350 344 if (kidmap_getsidbyuid(crgetzone(cr), xvattr.xva_vattr.va_uid,
351 345 &domain, &rid) == 0) {
352 346 VERIFY(nvlist_add_string(nvl_sid,
353 347 SID_DOMAIN, domain) == 0);
354 348 VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
355 349 VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_OWNERSID),
356 350 nvl_sid) == 0);
357 351 }
358 352 nvlist_free(nvl_sid);
359 353 }
360 354 if (xvattr.xva_vattr.va_gid > MAXUID) {
361 355 nvlist_t *nvl_sid;
362 356
363 357 if (nvlist_alloc(&nvl_sid, NV_UNIQUE_NAME, KM_SLEEP))
364 358 return (ENOMEM);
365 359
366 360 if (kidmap_getsidbygid(crgetzone(cr), xvattr.xva_vattr.va_gid,
367 361 &domain, &rid) == 0) {
368 362 VERIFY(nvlist_add_string(nvl_sid,
369 363 SID_DOMAIN, domain) == 0);
370 364 VERIFY(nvlist_add_uint32(nvl_sid, SID_RID, rid) == 0);
371 365 VERIFY(nvlist_add_nvlist(nvlp, attr_to_name(F_GROUPSID),
372 366 nvl_sid) == 0);
373 367 }
374 368 nvlist_free(nvl_sid);
375 369 }
376 370
377 371 return (0);
378 372 }
379 373
380 374 /*
381 375 * The size of a sysattr file is the size of the nvlist that will be
382 376 * returned by xattr_file_read(). A call to xattr_file_write() could
383 377 * change the size of that nvlist. That size is not stored persistently
384 378 * so xattr_fill_nvlist() calls VOP_GETATTR so that it can be calculated.
385 379 */
386 380 static int
387 381 xattr_file_size(vnode_t *vp, xattr_view_t xattr_view, size_t *size,
388 382 cred_t *cr, caller_context_t *ct)
389 383 {
390 384 nvlist_t *nvl;
391 385
392 386 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP)) {
393 387 return (ENOMEM);
394 388 }
395 389
396 390 if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
397 391 nvlist_free(nvl);
398 392 return (EFAULT);
399 393 }
400 394
401 395 VERIFY(nvlist_size(nvl, size, NV_ENCODE_XDR) == 0);
402 396 nvlist_free(nvl);
403 397 return (0);
404 398 }
405 399
406 400 /* ARGSUSED */
407 401 static int
408 402 xattr_file_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
409 403 caller_context_t *ct)
410 404 {
411 405 xattr_file_t *np = vp->v_data;
412 406 timestruc_t now;
413 407 size_t size;
414 408 int error;
415 409 vnode_t *pvp;
416 410 vattr_t pvattr;
417 411
418 412 vap->va_type = VREG;
419 413 vap->va_mode = MAKEIMODE(vap->va_type,
420 414 (np->xattr_view == XATTR_VIEW_READONLY ? 0444 : 0644));
421 415 vap->va_nodeid = gfs_file_inode(vp);
422 416 vap->va_nlink = 1;
423 417 pvp = gfs_file_parent(vp);
424 418 (void) memset(&pvattr, 0, sizeof (pvattr));
425 419 pvattr.va_mask = AT_CTIME|AT_MTIME;
426 420 error = VOP_GETATTR(pvp, &pvattr, flags, cr, ct);
427 421 if (error) {
428 422 return (error);
429 423 }
430 424 vap->va_ctime = pvattr.va_ctime;
431 425 vap->va_mtime = pvattr.va_mtime;
432 426 gethrestime(&now);
433 427 vap->va_atime = now;
434 428 vap->va_uid = 0;
435 429 vap->va_gid = 0;
436 430 vap->va_rdev = 0;
437 431 vap->va_blksize = DEV_BSIZE;
438 432 vap->va_seq = 0;
439 433 vap->va_fsid = vp->v_vfsp->vfs_dev;
440 434 error = xattr_file_size(vp, np->xattr_view, &size, cr, ct);
441 435 vap->va_size = size;
442 436 vap->va_nblocks = howmany(vap->va_size, vap->va_blksize);
443 437 return (error);
444 438 }
445 439
446 440 /* ARGSUSED */
447 441 static int
448 442 xattr_file_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
449 443 caller_context_t *ct)
450 444 {
451 445 xattr_file_t *np = vp->v_data;
452 446 xattr_view_t xattr_view = np->xattr_view;
453 447 char *buf;
454 448 size_t filesize;
455 449 nvlist_t *nvl;
456 450 int error;
457 451
458 452 /*
459 453 * Validate file offset and fasttrack empty reads
460 454 */
461 455 if (uiop->uio_loffset < (offset_t)0)
462 456 return (EINVAL);
463 457
464 458 if (uiop->uio_resid == 0)
465 459 return (0);
466 460
467 461 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP))
468 462 return (ENOMEM);
469 463
470 464 if (xattr_fill_nvlist(vp, xattr_view, nvl, cr, ct)) {
471 465 nvlist_free(nvl);
472 466 return (EFAULT);
473 467 }
474 468
475 469 VERIFY(nvlist_size(nvl, &filesize, NV_ENCODE_XDR) == 0);
476 470
477 471 if (uiop->uio_loffset >= filesize) {
478 472 nvlist_free(nvl);
479 473 return (0);
480 474 }
481 475
482 476 buf = kmem_alloc(filesize, KM_SLEEP);
483 477 VERIFY(nvlist_pack(nvl, &buf, &filesize, NV_ENCODE_XDR,
484 478 KM_SLEEP) == 0);
485 479
486 480 error = uiomove((caddr_t)buf, filesize, UIO_READ, uiop);
487 481 kmem_free(buf, filesize);
488 482 nvlist_free(nvl);
489 483 return (error);
490 484 }
491 485
492 486 /* ARGSUSED */
493 487 static int
494 488 xattr_file_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
495 489 caller_context_t *ct)
496 490 {
497 491 int error = 0;
498 492 char *buf;
499 493 char *domain;
500 494 uint32_t rid;
501 495 ssize_t size = uiop->uio_resid;
502 496 nvlist_t *nvp;
503 497 nvpair_t *pair = NULL;
504 498 vnode_t *ppvp;
505 499 xvattr_t xvattr;
506 500 xoptattr_t *xoap = NULL; /* Pointer to optional attributes */
507 501
508 502 if (vfs_has_feature(vp->v_vfsp, VFSFT_XVATTR) == 0)
509 503 return (EINVAL);
510 504
511 505 /*
512 506 * Validate file offset and size.
513 507 */
514 508 if (uiop->uio_loffset < (offset_t)0)
515 509 return (EINVAL);
516 510
517 511 if (size == 0)
518 512 return (EINVAL);
519 513
520 514 xva_init(&xvattr);
521 515
522 516 if ((xoap = xva_getxoptattr(&xvattr)) == NULL) {
523 517 return (EINVAL);
524 518 }
525 519
526 520 /*
527 521 * Copy and unpack the nvlist
528 522 */
529 523 buf = kmem_alloc(size, KM_SLEEP);
530 524 if (uiomove((caddr_t)buf, size, UIO_WRITE, uiop)) {
531 525 return (EFAULT);
532 526 }
533 527
534 528 if (nvlist_unpack(buf, size, &nvp, KM_SLEEP) != 0) {
535 529 kmem_free(buf, size);
536 530 uiop->uio_resid = size;
537 531 return (EINVAL);
538 532 }
539 533 kmem_free(buf, size);
540 534
541 535 /*
542 536 * Fasttrack empty writes (nvlist with no nvpairs)
543 537 */
544 538 if (nvlist_next_nvpair(nvp, NULL) == 0)
545 539 return (0);
546 540
547 541 ppvp = gfs_file_parent(gfs_file_parent(vp));
548 542
549 543 while (pair = nvlist_next_nvpair(nvp, pair)) {
550 544 data_type_t type;
551 545 f_attr_t attr;
552 546 boolean_t value;
553 547 uint64_t *time, *times;
554 548 uint_t elem, nelems;
555 549 nvlist_t *nvp_sid;
556 550 uint8_t *scanstamp;
557 551
558 552 /*
559 553 * Validate the name and type of each attribute.
560 554 * Log any unknown names and continue. This will
561 555 * help if additional attributes are added later.
562 556 */
563 557 type = nvpair_type(pair);
564 558 if ((attr = name_to_attr(nvpair_name(pair))) == F_ATTR_INVAL) {
565 559 cmn_err(CE_WARN, "Unknown attribute %s",
566 560 nvpair_name(pair));
567 561 continue;
568 562 }
569 563
570 564 /*
571 565 * Verify nvlist type matches required type and view is OK
572 566 */
573 567
574 568 if (type != attr_to_data_type(attr) ||
575 569 (attr_to_xattr_view(attr) == XATTR_VIEW_READONLY)) {
576 570 nvlist_free(nvp);
577 571 return (EINVAL);
578 572 }
579 573
580 574 /*
581 575 * For OWNERSID/GROUPSID make sure the target
582 576 * file system support ephemeral ID's
583 577 */
584 578 if ((attr == F_OWNERSID || attr == F_GROUPSID) &&
585 579 (!(vp->v_vfsp->vfs_flag & VFS_XID))) {
586 580 nvlist_free(nvp);
587 581 return (EINVAL);
588 582 }
589 583
590 584 /*
591 585 * Retrieve data from nvpair
592 586 */
593 587 switch (type) {
594 588 case DATA_TYPE_BOOLEAN_VALUE:
595 589 if (nvpair_value_boolean_value(pair, &value)) {
596 590 nvlist_free(nvp);
597 591 return (EINVAL);
598 592 }
599 593 break;
600 594 case DATA_TYPE_UINT64_ARRAY:
601 595 if (nvpair_value_uint64_array(pair, ×, &nelems)) {
602 596 nvlist_free(nvp);
603 597 return (EINVAL);
604 598 }
605 599 break;
606 600 case DATA_TYPE_NVLIST:
607 601 if (nvpair_value_nvlist(pair, &nvp_sid)) {
608 602 nvlist_free(nvp);
609 603 return (EINVAL);
610 604 }
611 605 break;
612 606 case DATA_TYPE_UINT8_ARRAY:
613 607 if (nvpair_value_uint8_array(pair,
614 608 &scanstamp, &nelems)) {
615 609 nvlist_free(nvp);
616 610 return (EINVAL);
617 611 }
618 612 break;
619 613 default:
620 614 nvlist_free(nvp);
621 615 return (EINVAL);
622 616 }
623 617
624 618 switch (attr) {
625 619 /*
626 620 * If we have several similar optional attributes to
627 621 * process then we should do it all together here so that
628 622 * xoap and the requested bitmap can be set in one place.
629 623 */
630 624 case F_READONLY:
631 625 XVA_SET_REQ(&xvattr, XAT_READONLY);
632 626 xoap->xoa_readonly = value;
633 627 break;
634 628 case F_HIDDEN:
635 629 XVA_SET_REQ(&xvattr, XAT_HIDDEN);
636 630 xoap->xoa_hidden = value;
637 631 break;
638 632 case F_SYSTEM:
639 633 XVA_SET_REQ(&xvattr, XAT_SYSTEM);
640 634 xoap->xoa_system = value;
641 635 break;
642 636 case F_ARCHIVE:
643 637 XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
644 638 xoap->xoa_archive = value;
645 639 break;
646 640 case F_IMMUTABLE:
647 641 XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
648 642 xoap->xoa_immutable = value;
649 643 break;
650 644 case F_NOUNLINK:
651 645 XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
652 646 xoap->xoa_nounlink = value;
653 647 break;
654 648 case F_APPENDONLY:
655 649 XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
656 650 xoap->xoa_appendonly = value;
657 651 break;
658 652 case F_NODUMP:
659 653 XVA_SET_REQ(&xvattr, XAT_NODUMP);
660 654 xoap->xoa_nodump = value;
661 655 break;
662 656 case F_AV_QUARANTINED:
663 657 XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
664 658 xoap->xoa_av_quarantined = value;
665 659 break;
666 660 case F_AV_MODIFIED:
667 661 XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
668 662 xoap->xoa_av_modified = value;
669 663 break;
670 664 case F_CRTIME:
671 665 XVA_SET_REQ(&xvattr, XAT_CREATETIME);
672 666 time = (uint64_t *)&(xoap->xoa_createtime);
673 667 for (elem = 0; elem < nelems; elem++)
674 668 *time++ = times[elem];
675 669 break;
676 670 case F_OWNERSID:
677 671 case F_GROUPSID:
678 672 if (nvlist_lookup_string(nvp_sid, SID_DOMAIN,
679 673 &domain) || nvlist_lookup_uint32(nvp_sid, SID_RID,
680 674 &rid)) {
681 675 nvlist_free(nvp);
682 676 return (EINVAL);
683 677 }
684 678
685 679 /*
686 680 * Now map domain+rid to ephemeral id's
687 681 *
688 682 * If mapping fails, then the uid/gid will
689 683 * be set to UID_NOBODY by Winchester.
690 684 */
691 685
692 686 if (attr == F_OWNERSID) {
693 687 (void) kidmap_getuidbysid(crgetzone(cr), domain,
694 688 rid, &xvattr.xva_vattr.va_uid);
695 689 xvattr.xva_vattr.va_mask |= AT_UID;
696 690 } else {
697 691 (void) kidmap_getgidbysid(crgetzone(cr), domain,
698 692 rid, &xvattr.xva_vattr.va_gid);
699 693 xvattr.xva_vattr.va_mask |= AT_GID;
700 694 }
701 695 break;
702 696 case F_AV_SCANSTAMP:
703 697 if (ppvp->v_type == VREG) {
704 698 XVA_SET_REQ(&xvattr, XAT_AV_SCANSTAMP);
705 699 (void) memcpy(xoap->xoa_av_scanstamp,
706 700 scanstamp, nelems);
707 701 } else {
708 702 nvlist_free(nvp);
709 703 return (EINVAL);
710 704 }
711 705 break;
712 706 case F_REPARSE:
713 707 XVA_SET_REQ(&xvattr, XAT_REPARSE);
714 708 xoap->xoa_reparse = value;
715 709 break;
716 710 case F_OFFLINE:
717 711 XVA_SET_REQ(&xvattr, XAT_OFFLINE);
718 712 xoap->xoa_offline = value;
719 713 break;
720 714 case F_SPARSE:
721 715 XVA_SET_REQ(&xvattr, XAT_SPARSE);
722 716 xoap->xoa_sparse = value;
723 717 break;
724 718 default:
725 719 break;
726 720 }
727 721 }
728 722
729 723 ppvp = gfs_file_parent(gfs_file_parent(vp));
730 724 error = VOP_SETATTR(ppvp, &xvattr.xva_vattr, 0, cr, ct);
731 725 if (error)
732 726 uiop->uio_resid = size;
733 727
734 728 nvlist_free(nvp);
735 729 return (error);
736 730 }
737 731
738 732 static int
739 733 xattr_file_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
740 734 caller_context_t *ct)
741 735 {
742 736 switch (cmd) {
743 737 case _PC_XATTR_EXISTS:
744 738 case _PC_SATTR_ENABLED:
745 739 case _PC_SATTR_EXISTS:
746 740 *valp = 0;
747 741 return (0);
748 742 default:
749 743 return (fs_pathconf(vp, cmd, valp, cr, ct));
750 744 }
751 745 }
752 746
753 747 vnodeops_t *xattr_file_ops;
754 748
755 749 static const fs_operation_def_t xattr_file_tops[] = {
756 750 { VOPNAME_OPEN, { .vop_open = xattr_file_open } },
757 751 { VOPNAME_CLOSE, { .vop_close = xattr_file_close } },
758 752 { VOPNAME_READ, { .vop_read = xattr_file_read } },
759 753 { VOPNAME_WRITE, { .vop_write = xattr_file_write } },
760 754 { VOPNAME_IOCTL, { .error = fs_ioctl } },
761 755 { VOPNAME_GETATTR, { .vop_getattr = xattr_file_getattr } },
762 756 { VOPNAME_ACCESS, { .vop_access = xattr_file_access } },
763 757 { VOPNAME_READDIR, { .error = fs_notdir } },
764 758 { VOPNAME_SEEK, { .vop_seek = fs_seek } },
765 759 { VOPNAME_INACTIVE, { .vop_inactive = gfs_vop_inactive } },
766 760 { VOPNAME_FID, { .vop_fid = xattr_common_fid } },
767 761 { VOPNAME_PATHCONF, { .vop_pathconf = xattr_file_pathconf } },
768 762 { VOPNAME_PUTPAGE, { .error = fs_putpage } },
769 763 { VOPNAME_FSYNC, { .error = fs_fsync } },
770 764 { NULL }
771 765 };
772 766
773 767 vnode_t *
774 768 xattr_mkfile(vnode_t *pvp, xattr_view_t xattr_view)
775 769 {
776 770 vnode_t *vp;
777 771 xattr_file_t *np;
778 772
779 773 vp = gfs_file_create(sizeof (xattr_file_t), pvp, xattr_file_ops);
780 774 np = vp->v_data;
781 775 np->xattr_view = xattr_view;
782 776 vp->v_flag |= V_SYSATTR;
783 777 return (vp);
784 778 }
785 779
786 780 vnode_t *
787 781 xattr_mkfile_ro(vnode_t *pvp)
788 782 {
789 783 return (xattr_mkfile(pvp, XATTR_VIEW_READONLY));
790 784 }
791 785
792 786 vnode_t *
793 787 xattr_mkfile_rw(vnode_t *pvp)
794 788 {
795 789 return (xattr_mkfile(pvp, XATTR_VIEW_READWRITE));
796 790 }
797 791
798 792 vnodeops_t *xattr_dir_ops;
799 793
800 794 static gfs_dirent_t xattr_dirents[] = {
801 795 { VIEW_READONLY, xattr_mkfile_ro, GFS_CACHE_VNODE, },
802 796 { VIEW_READWRITE, xattr_mkfile_rw, GFS_CACHE_VNODE, },
803 797 { NULL },
804 798 };
805 799
806 800 #define XATTRDIR_NENTS ((sizeof (xattr_dirents) / sizeof (gfs_dirent_t)) - 1)
807 801
808 802 static int
809 803 is_sattr_name(char *s)
810 804 {
811 805 int i;
812 806
813 807 for (i = 0; i < XATTRDIR_NENTS; ++i) {
814 808 if (strcmp(s, xattr_dirents[i].gfse_name) == 0) {
815 809 return (1);
816 810 }
817 811 }
818 812 return (0);
819 813 }
820 814
821 815 /*
822 816 * Given the name of an extended attribute file, determine if there is a
823 817 * normalization conflict with a sysattr view name.
824 818 */
825 819 int
826 820 xattr_sysattr_casechk(char *s)
827 821 {
828 822 int i;
829 823
830 824 for (i = 0; i < XATTRDIR_NENTS; ++i) {
831 825 if (strcasecmp(s, xattr_dirents[i].gfse_name) == 0)
832 826 return (1);
833 827 }
834 828 return (0);
835 829 }
836 830
837 831 static int
838 832 xattr_copy(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
839 833 cred_t *cr, caller_context_t *ct)
840 834 {
841 835 xvattr_t xvattr;
842 836 vnode_t *pdvp;
843 837 int error;
844 838
845 839 /*
846 840 * Only copy system attrs if the views are the same
847 841 */
848 842 if (strcmp(snm, tnm) != 0)
849 843 return (EINVAL);
850 844
851 845 xva_init(&xvattr);
852 846
853 847 XVA_SET_REQ(&xvattr, XAT_SYSTEM);
854 848 XVA_SET_REQ(&xvattr, XAT_READONLY);
855 849 XVA_SET_REQ(&xvattr, XAT_HIDDEN);
856 850 XVA_SET_REQ(&xvattr, XAT_ARCHIVE);
857 851 XVA_SET_REQ(&xvattr, XAT_APPENDONLY);
858 852 XVA_SET_REQ(&xvattr, XAT_NOUNLINK);
859 853 XVA_SET_REQ(&xvattr, XAT_IMMUTABLE);
860 854 XVA_SET_REQ(&xvattr, XAT_NODUMP);
861 855 XVA_SET_REQ(&xvattr, XAT_AV_MODIFIED);
862 856 XVA_SET_REQ(&xvattr, XAT_AV_QUARANTINED);
863 857 XVA_SET_REQ(&xvattr, XAT_CREATETIME);
864 858 XVA_SET_REQ(&xvattr, XAT_REPARSE);
865 859 XVA_SET_REQ(&xvattr, XAT_OFFLINE);
866 860 XVA_SET_REQ(&xvattr, XAT_SPARSE);
867 861
868 862 pdvp = gfs_file_parent(sdvp);
869 863 error = VOP_GETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
870 864 if (error)
871 865 return (error);
↓ open down ↓ |
796 lines elided |
↑ open up ↑ |
872 866
873 867 pdvp = gfs_file_parent(tdvp);
874 868 error = VOP_SETATTR(pdvp, &xvattr.xva_vattr, 0, cr, ct);
875 869 return (error);
876 870 }
877 871
878 872 static int
879 873 xattr_dir_realdir(vnode_t *dvp, vnode_t **realdvp, int lookup_flags,
880 874 cred_t *cr, caller_context_t *ct)
881 875 {
882 - vnode_t *pvp;
876 + xattr_dir_t *xattr_dir;
883 877 int error;
884 - struct pathname pn;
885 - char *startnm = "";
886 878
887 879 *realdvp = NULL;
888 880
889 - pvp = gfs_file_parent(dvp);
881 + if (dvp->v_type != VDIR)
882 + return (EINVAL);
890 883
891 - error = pn_get(startnm, UIO_SYSSPACE, &pn);
892 - if (error) {
893 - VN_RELE(pvp);
894 - return (error);
895 - }
884 + mutex_enter(&dvp->v_lock);
885 + xattr_dir = dvp->v_data;
886 + *realdvp = xattr_dir->xattr_realvp;
887 + mutex_exit(&dvp->v_lock);
896 888
897 - /*
898 - * Set the LOOKUP_HAVE_SYSATTR_DIR flag so that we don't get into an
899 - * infinite loop with fop_lookup calling back to xattr_dir_lookup.
900 - */
901 - lookup_flags |= LOOKUP_HAVE_SYSATTR_DIR;
902 - error = VOP_LOOKUP(pvp, startnm, realdvp, &pn, lookup_flags,
903 - rootvp, cr, ct, NULL, NULL);
904 - pn_free(&pn);
889 + if (*realdvp != NULL) {
890 + VN_HOLD(*realdvp);
891 + error = 0;
892 + } else
893 + error = ENOENT;
905 894
906 895 return (error);
907 896 }
908 897
909 898 /* ARGSUSED */
910 899 static int
911 900 xattr_dir_open(vnode_t **vpp, int flags, cred_t *cr, caller_context_t *ct)
912 901 {
902 + vnode_t *realvp;
903 + int error;
904 +
913 905 if (flags & FWRITE) {
914 906 return (EACCES);
915 907 }
916 908
909 + /*
910 + * The underlying FS may need this VOP call.
911 + */
912 + error = xattr_dir_realdir(*vpp, &realvp, LOOKUP_XATTR, cr, ct);
913 + if (error == 0) {
914 + error = VOP_OPEN(&realvp, flags, cr, ct);
915 + VN_RELE(realvp);
916 + if (error)
917 + return (error);
918 + } /* else ignore this error */
919 +
917 920 return (0);
918 921 }
919 922
920 923 /* ARGSUSED */
921 924 static int
922 -xattr_dir_close(vnode_t *vpp, int flags, int count, offset_t off, cred_t *cr,
925 +xattr_dir_close(vnode_t *vp, int flags, int count, offset_t off, cred_t *cr,
923 926 caller_context_t *ct)
924 927 {
928 + vnode_t *realvp;
929 + int error;
930 +
931 + /*
932 + * The underlying FS may need this VOP call.
933 + */
934 + error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
935 + if (error == 0) {
936 + error = VOP_CLOSE(realvp, flags, count, off, cr, ct);
937 + VN_RELE(realvp);
938 + if (error)
939 + return (error);
940 + } /* else ignore this error */
941 +
925 942 return (0);
926 943 }
927 944
928 945 /*
929 946 * Retrieve the attributes on an xattr directory. If there is a "real"
930 947 * xattr directory, use that. Otherwise, get the attributes (represented
931 948 * by PARENT_ATTRMASK) from the "parent" node and fill in the rest. Note
932 949 * that VOP_GETATTR() could turn off bits in the va_mask.
933 950 */
934 951
935 952 #define PARENT_ATTRMASK (AT_UID|AT_GID|AT_RDEV|AT_CTIME|AT_MTIME)
936 953
937 954 /* ARGSUSED */
938 955 static int
939 956 xattr_dir_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
940 957 caller_context_t *ct)
941 958 {
942 959 timestruc_t now;
943 960 vnode_t *pvp;
944 961 int error;
945 962
946 963 error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR, cr, ct);
947 964 if (error == 0) {
948 965 error = VOP_GETATTR(pvp, vap, 0, cr, ct);
949 966 VN_RELE(pvp);
950 967 if (error) {
951 968 return (error);
952 969 }
953 970 vap->va_nlink += XATTRDIR_NENTS;
954 971 vap->va_size += XATTRDIR_NENTS;
955 972 return (0);
956 973 }
957 974
958 975 /*
959 976 * There is no real xattr directory. Cobble together
960 977 * an entry using info from the parent object (if needed)
961 978 * plus information common to all xattrs.
962 979 */
963 980 if (vap->va_mask & PARENT_ATTRMASK) {
964 981 vattr_t pvattr;
965 982 uint_t off_bits;
966 983
967 984 pvp = gfs_file_parent(vp);
968 985 (void) memset(&pvattr, 0, sizeof (pvattr));
969 986 pvattr.va_mask = PARENT_ATTRMASK;
970 987 error = VOP_GETATTR(pvp, &pvattr, 0, cr, ct);
971 988 if (error) {
972 989 return (error);
973 990 }
974 991
975 992 /*
976 993 * VOP_GETATTR() might have turned off some bits in
977 994 * pvattr.va_mask. This means that the underlying
978 995 * file system couldn't process those attributes.
979 996 * We need to make sure those bits get turned off
980 997 * in the vattr_t structure that gets passed back
981 998 * to the caller. Figure out which bits were turned
982 999 * off (if any) then set pvattr.va_mask before it
983 1000 * gets copied to the vattr_t that the caller sees.
984 1001 */
985 1002 off_bits = (pvattr.va_mask ^ PARENT_ATTRMASK) & PARENT_ATTRMASK;
986 1003 pvattr.va_mask = vap->va_mask & ~off_bits;
987 1004 *vap = pvattr;
988 1005 }
989 1006
990 1007 vap->va_type = VDIR;
991 1008 vap->va_mode = MAKEIMODE(vap->va_type, S_ISVTX | 0777);
992 1009 vap->va_fsid = vp->v_vfsp->vfs_dev;
993 1010 vap->va_nodeid = gfs_file_inode(vp);
994 1011 vap->va_nlink = XATTRDIR_NENTS+2;
995 1012 vap->va_size = vap->va_nlink;
996 1013 gethrestime(&now);
997 1014 vap->va_atime = now;
998 1015 vap->va_blksize = 0;
999 1016 vap->va_nblocks = 0;
1000 1017 vap->va_seq = 0;
1001 1018 return (0);
1002 1019 }
1003 1020
1004 1021 static int
1005 1022 xattr_dir_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
1006 1023 caller_context_t *ct)
1007 1024 {
1008 1025 vnode_t *realvp;
1009 1026 int error;
1010 1027
1011 1028 /*
1012 1029 * If there is a real xattr directory, do the setattr there.
1013 1030 * Otherwise, just return success. The GFS directory is transient,
1014 1031 * and any setattr changes can disappear anyway.
1015 1032 */
1016 1033 error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
1017 1034 if (error == 0) {
1018 1035 error = VOP_SETATTR(realvp, vap, flags, cr, ct);
1019 1036 VN_RELE(realvp);
1020 1037 }
1021 1038 if (error == ENOENT) {
1022 1039 error = 0;
1023 1040 }
1024 1041 return (error);
1025 1042 }
1026 1043
1027 1044 /* ARGSUSED */
1028 1045 static int
1029 1046 xattr_dir_access(vnode_t *vp, int mode, int flags, cred_t *cr,
1030 1047 caller_context_t *ct)
1031 1048 {
1032 1049 int error;
1033 1050 vnode_t *realvp = NULL;
1034 1051
1035 1052 if (mode & VWRITE) {
1036 1053 return (EACCES);
1037 1054 }
1038 1055
1039 1056 error = xattr_dir_realdir(vp, &realvp, LOOKUP_XATTR, cr, ct);
1040 1057
1041 1058 if (realvp)
1042 1059 VN_RELE(realvp);
1043 1060
1044 1061 /*
1045 1062 * No real xattr dir isn't an error
1046 1063 * an error of EINVAL indicates attributes on attributes
1047 1064 * are not supported. In that case just allow access to the
1048 1065 * transient directory.
1049 1066 */
1050 1067 return ((error == ENOENT || error == EINVAL) ? 0 : error);
1051 1068 }
1052 1069
1053 1070 static int
1054 1071 xattr_dir_create(vnode_t *dvp, char *name, vattr_t *vap, vcexcl_t excl,
1055 1072 int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
1056 1073 vsecattr_t *vsecp)
1057 1074 {
1058 1075 vnode_t *pvp;
1059 1076 int error;
1060 1077
1061 1078 *vpp = NULL;
1062 1079
1063 1080 /*
1064 1081 * Don't allow creation of extended attributes with sysattr names.
1065 1082 */
1066 1083 if (is_sattr_name(name)) {
1067 1084 return (gfs_dir_lookup(dvp, name, vpp, cr, 0, NULL, NULL));
1068 1085 }
1069 1086
1070 1087 error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
1071 1088 cr, ct);
1072 1089 if (error == 0) {
1073 1090 error = VOP_CREATE(pvp, name, vap, excl, mode, vpp, cr, flag,
1074 1091 ct, vsecp);
1075 1092 VN_RELE(pvp);
1076 1093 }
1077 1094 return (error);
1078 1095 }
1079 1096
1080 1097 static int
1081 1098 xattr_dir_remove(vnode_t *dvp, char *name, cred_t *cr, caller_context_t *ct,
1082 1099 int flags)
1083 1100 {
1084 1101 vnode_t *pvp;
1085 1102 int error;
1086 1103
1087 1104 if (is_sattr_name(name)) {
1088 1105 return (EACCES);
1089 1106 }
1090 1107
1091 1108 error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
1092 1109 if (error == 0) {
1093 1110 error = VOP_REMOVE(pvp, name, cr, ct, flags);
1094 1111 VN_RELE(pvp);
1095 1112 }
1096 1113 return (error);
1097 1114 }
1098 1115
1099 1116 static int
1100 1117 xattr_dir_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr,
1101 1118 caller_context_t *ct, int flags)
1102 1119 {
1103 1120 vnode_t *pvp;
1104 1121 int error;
1105 1122
1106 1123 if (svp->v_flag & V_SYSATTR) {
1107 1124 return (EINVAL);
1108 1125 }
1109 1126
1110 1127 error = xattr_dir_realdir(tdvp, &pvp, LOOKUP_XATTR, cr, ct);
1111 1128 if (error == 0) {
1112 1129 error = VOP_LINK(pvp, svp, name, cr, ct, flags);
1113 1130 VN_RELE(pvp);
1114 1131 }
1115 1132 return (error);
1116 1133 }
1117 1134
1118 1135 static int
1119 1136 xattr_dir_rename(vnode_t *sdvp, char *snm, vnode_t *tdvp, char *tnm,
1120 1137 cred_t *cr, caller_context_t *ct, int flags)
1121 1138 {
1122 1139 vnode_t *spvp, *tpvp;
1123 1140 int error;
1124 1141 int held_tgt;
1125 1142
1126 1143 if (is_sattr_name(snm) || is_sattr_name(tnm))
1127 1144 return (xattr_copy(sdvp, snm, tdvp, tnm, cr, ct));
1128 1145 /*
1129 1146 * We know that sdvp is a GFS dir, or we wouldn't be here.
1130 1147 * Get the real unnamed directory.
1131 1148 */
1132 1149 error = xattr_dir_realdir(sdvp, &spvp, LOOKUP_XATTR, cr, ct);
1133 1150 if (error) {
1134 1151 return (error);
1135 1152 }
1136 1153
1137 1154 if (sdvp == tdvp) {
1138 1155 /*
1139 1156 * If the source and target are the same GFS directory, the
1140 1157 * underlying unnamed source and target dir will be the same.
1141 1158 */
1142 1159 tpvp = spvp;
1143 1160 VN_HOLD(tpvp);
1144 1161 held_tgt = 1;
1145 1162 } else if (tdvp->v_flag & V_SYSATTR) {
1146 1163 /*
1147 1164 * If the target dir is a different GFS directory,
1148 1165 * find its underlying unnamed dir.
1149 1166 */
1150 1167 error = xattr_dir_realdir(tdvp, &tpvp, LOOKUP_XATTR, cr, ct);
1151 1168 if (error) {
1152 1169 VN_RELE(spvp);
1153 1170 return (error);
1154 1171 }
1155 1172 held_tgt = 1;
1156 1173 } else {
1157 1174 /*
1158 1175 * Target dir is outside of GFS, pass it on through.
1159 1176 */
1160 1177 tpvp = tdvp;
1161 1178 held_tgt = 0;
1162 1179 }
1163 1180
1164 1181 error = VOP_RENAME(spvp, snm, tpvp, tnm, cr, ct, flags);
1165 1182
1166 1183 if (held_tgt) {
1167 1184 VN_RELE(tpvp);
1168 1185 }
1169 1186 VN_RELE(spvp);
1170 1187
1171 1188 return (error);
1172 1189 }
1173 1190
1174 1191 /*
1175 1192 * readdir_xattr_casecmp: given a system attribute name, see if there
1176 1193 * is a real xattr with the same normalized name.
1177 1194 */
1178 1195 static int
1179 1196 readdir_xattr_casecmp(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct,
1180 1197 int *eflags)
1181 1198 {
1182 1199 int error;
1183 1200 vnode_t *vp;
1184 1201 struct pathname pn;
1185 1202
1186 1203 *eflags = 0;
1187 1204
1188 1205 error = pn_get(nm, UIO_SYSSPACE, &pn);
1189 1206 if (error == 0) {
1190 1207 error = VOP_LOOKUP(dvp, nm, &vp, &pn,
1191 1208 FIGNORECASE, rootvp, cr, ct, NULL, NULL);
1192 1209 if (error == 0) {
1193 1210 *eflags = ED_CASE_CONFLICT;
1194 1211 VN_RELE(vp);
1195 1212 } else if (error == ENOENT) {
1196 1213 error = 0;
1197 1214 }
1198 1215 pn_free(&pn);
1199 1216 }
1200 1217
1201 1218 return (error);
1202 1219 }
1203 1220
1204 1221 static int
1205 1222 xattr_dir_readdir(vnode_t *dvp, uio_t *uiop, cred_t *cr, int *eofp,
1206 1223 caller_context_t *ct, int flags)
1207 1224 {
1208 1225 vnode_t *pvp;
1209 1226 int error;
1210 1227 int local_eof;
1211 1228 int reset_off = 0;
1212 1229 int has_xattrs = 0;
1213 1230
1214 1231 if (eofp == NULL) {
1215 1232 eofp = &local_eof;
1216 1233 }
1217 1234 *eofp = 0;
1218 1235
1219 1236 /*
1220 1237 * See if there is a real extended attribute directory.
1221 1238 */
1222 1239 error = xattr_dir_realdir(dvp, &pvp, LOOKUP_XATTR, cr, ct);
1223 1240 if (error == 0) {
1224 1241 has_xattrs = 1;
1225 1242 }
1226 1243
1227 1244 /*
1228 1245 * Start by reading up the static entries.
1229 1246 */
1230 1247 if (uiop->uio_loffset == 0) {
1231 1248 ino64_t pino, ino;
1232 1249 offset_t off;
1233 1250 gfs_dir_t *dp = dvp->v_data;
1234 1251 gfs_readdir_state_t gstate;
1235 1252
1236 1253 if (has_xattrs) {
1237 1254 /*
1238 1255 * If there is a real xattr dir, skip . and ..
1239 1256 * in the GFS dir. We'll pick them up below
1240 1257 * when we call into the underlying fs.
1241 1258 */
1242 1259 uiop->uio_loffset = GFS_STATIC_ENTRY_OFFSET;
1243 1260 }
1244 1261 error = gfs_get_parent_ino(dvp, cr, ct, &pino, &ino);
1245 1262 if (error == 0) {
1246 1263 error = gfs_readdir_init(&gstate, dp->gfsd_maxlen, 1,
1247 1264 uiop, pino, ino, flags);
1248 1265 }
1249 1266 if (error) {
1250 1267 if (has_xattrs)
1251 1268 VN_RELE(pvp);
1252 1269 return (error);
1253 1270 }
1254 1271
1255 1272 while ((error = gfs_readdir_pred(&gstate, uiop, &off)) == 0 &&
1256 1273 !*eofp) {
1257 1274 if (off >= 0 && off < dp->gfsd_nstatic) {
1258 1275 int eflags;
1259 1276
1260 1277 /*
1261 1278 * Check to see if this sysattr set name has a
1262 1279 * case-insensitive conflict with a real xattr
1263 1280 * name.
1264 1281 */
1265 1282 eflags = 0;
1266 1283 if ((flags & V_RDDIR_ENTFLAGS) && has_xattrs) {
1267 1284 error = readdir_xattr_casecmp(pvp,
1268 1285 dp->gfsd_static[off].gfse_name,
1269 1286 cr, ct, &eflags);
1270 1287 if (error)
1271 1288 break;
1272 1289 }
1273 1290 ino = dp->gfsd_inode(dvp, off);
1274 1291
1275 1292 error = gfs_readdir_emit(&gstate, uiop, off,
1276 1293 ino, dp->gfsd_static[off].gfse_name,
1277 1294 eflags);
1278 1295 if (error)
1279 1296 break;
1280 1297 } else {
1281 1298 *eofp = 1;
1282 1299 }
1283 1300 }
1284 1301
1285 1302 error = gfs_readdir_fini(&gstate, error, eofp, *eofp);
1286 1303 if (error) {
1287 1304 if (has_xattrs)
1288 1305 VN_RELE(pvp);
1289 1306 return (error);
1290 1307 }
1291 1308
1292 1309 /*
1293 1310 * We must read all of the static entries in the first
1294 1311 * call. Otherwise we won't know if uio_loffset in a
1295 1312 * subsequent call refers to the static entries or to those
1296 1313 * in an underlying fs.
1297 1314 */
1298 1315 if (*eofp == 0)
1299 1316 return (EINVAL);
1300 1317 reset_off = 1;
1301 1318 }
1302 1319
1303 1320 if (!has_xattrs) {
1304 1321 *eofp = 1;
1305 1322 return (0);
1306 1323 }
1307 1324
1308 1325 *eofp = 0;
1309 1326 if (reset_off) {
1310 1327 uiop->uio_loffset = 0;
1311 1328 }
1312 1329 (void) VOP_RWLOCK(pvp, V_WRITELOCK_FALSE, NULL);
1313 1330 error = VOP_READDIR(pvp, uiop, cr, eofp, ct, flags);
1314 1331 VOP_RWUNLOCK(pvp, V_WRITELOCK_FALSE, NULL);
1315 1332 VN_RELE(pvp);
1316 1333
1317 1334 return (error);
1318 1335 }
1319 1336
1320 1337 /* ARGSUSED */
1321 1338 static void
1322 1339 xattr_dir_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
1323 1340 {
1324 1341 gfs_file_t *fp;
1325 1342 xattr_dir_t *xattr_dir;
1326 1343
1327 1344 mutex_enter(&vp->v_lock);
1328 1345 xattr_dir = vp->v_data;
1329 1346 if (xattr_dir->xattr_realvp) {
1330 1347 VN_RELE(xattr_dir->xattr_realvp);
1331 1348 xattr_dir->xattr_realvp = NULL;
1332 1349 }
1333 1350 mutex_exit(&vp->v_lock);
1334 1351 fp = gfs_dir_inactive(vp);
1335 1352 if (fp != NULL) {
1336 1353 kmem_free(fp, fp->gfs_size);
1337 1354 }
1338 1355 }
1339 1356
1340 1357 static int
1341 1358 xattr_dir_pathconf(vnode_t *vp, int cmd, ulong_t *valp, cred_t *cr,
1342 1359 caller_context_t *ct)
1343 1360 {
1344 1361 switch (cmd) {
1345 1362 case _PC_XATTR_EXISTS:
1346 1363 case _PC_SATTR_ENABLED:
1347 1364 case _PC_SATTR_EXISTS:
1348 1365 *valp = 0;
↓ open down ↓ |
414 lines elided |
↑ open up ↑ |
1349 1366 return (0);
1350 1367 default:
1351 1368 return (fs_pathconf(vp, cmd, valp, cr, ct));
1352 1369 }
1353 1370 }
1354 1371
1355 1372 /* ARGSUSED */
1356 1373 static int
1357 1374 xattr_dir_realvp(vnode_t *vp, vnode_t **realvp, caller_context_t *ct)
1358 1375 {
1359 - xattr_dir_t *xattr_dir;
1376 + int error;
1360 1377
1361 - mutex_enter(&vp->v_lock);
1362 - xattr_dir = vp->v_data;
1363 - if (xattr_dir->xattr_realvp) {
1364 - *realvp = xattr_dir->xattr_realvp;
1365 - mutex_exit(&vp->v_lock);
1366 - return (0);
1367 - } else {
1368 - vnode_t *xdvp;
1369 - int error;
1378 + error = xattr_dir_realdir(vp, realvp, LOOKUP_XATTR, kcred, NULL);
1379 + return (error);
1370 1380
1371 - mutex_exit(&vp->v_lock);
1372 - if ((error = xattr_dir_realdir(vp, &xdvp,
1373 - LOOKUP_XATTR, kcred, NULL)) == 0) {
1374 - /*
1375 - * verify we aren't racing with another thread
1376 - * to find the xattr_realvp
1377 - */
1378 - mutex_enter(&vp->v_lock);
1379 - if (xattr_dir->xattr_realvp == NULL) {
1380 - xattr_dir->xattr_realvp = xdvp;
1381 - *realvp = xdvp;
1382 - mutex_exit(&vp->v_lock);
1383 - } else {
1384 - *realvp = xattr_dir->xattr_realvp;
1385 - mutex_exit(&vp->v_lock);
1386 - VN_RELE(xdvp);
1387 - }
1388 - }
1389 - return (error);
1390 - }
1391 1381 }
1392 1382
1393 1383 static const fs_operation_def_t xattr_dir_tops[] = {
1394 1384 { VOPNAME_OPEN, { .vop_open = xattr_dir_open } },
1395 1385 { VOPNAME_CLOSE, { .vop_close = xattr_dir_close } },
1396 1386 { VOPNAME_IOCTL, { .error = fs_inval } },
1397 1387 { VOPNAME_GETATTR, { .vop_getattr = xattr_dir_getattr } },
1398 1388 { VOPNAME_SETATTR, { .vop_setattr = xattr_dir_setattr } },
1399 1389 { VOPNAME_ACCESS, { .vop_access = xattr_dir_access } },
1400 1390 { VOPNAME_READDIR, { .vop_readdir = xattr_dir_readdir } },
1401 1391 { VOPNAME_LOOKUP, { .vop_lookup = gfs_vop_lookup } },
1402 1392 { VOPNAME_CREATE, { .vop_create = xattr_dir_create } },
1403 1393 { VOPNAME_REMOVE, { .vop_remove = xattr_dir_remove } },
1404 1394 { VOPNAME_LINK, { .vop_link = xattr_dir_link } },
1405 1395 { VOPNAME_RENAME, { .vop_rename = xattr_dir_rename } },
1406 1396 { VOPNAME_MKDIR, { .error = fs_inval } },
1407 1397 { VOPNAME_SEEK, { .vop_seek = fs_seek } },
1408 1398 { VOPNAME_INACTIVE, { .vop_inactive = xattr_dir_inactive } },
1409 1399 { VOPNAME_FID, { .vop_fid = xattr_common_fid } },
1410 1400 { VOPNAME_PATHCONF, { .vop_pathconf = xattr_dir_pathconf } },
1411 1401 { VOPNAME_REALVP, { .vop_realvp = xattr_dir_realvp } },
1412 1402 { NULL, NULL }
1413 1403 };
1414 1404
1415 1405 static gfs_opsvec_t xattr_opsvec[] = {
1416 1406 { "xattr dir", xattr_dir_tops, &xattr_dir_ops },
1417 1407 { "system attributes", xattr_file_tops, &xattr_file_ops },
1418 1408 { NULL, NULL, NULL }
1419 1409 };
1420 1410
1421 1411 static int
1422 1412 xattr_lookup_cb(vnode_t *vp, const char *nm, vnode_t **vpp, ino64_t *inop,
1423 1413 cred_t *cr, int flags, int *deflags, pathname_t *rpnp)
1424 1414 {
1425 1415 vnode_t *pvp;
1426 1416 struct pathname pn;
1427 1417 int error;
1428 1418
1429 1419 *vpp = NULL;
1430 1420 *inop = 0;
1431 1421
1432 1422 error = xattr_dir_realdir(vp, &pvp, LOOKUP_XATTR|CREATE_XATTR_DIR,
1433 1423 cr, NULL);
1434 1424
1435 1425 /*
1436 1426 * Return ENOENT for EACCES requests during lookup. Once an
1437 1427 * attribute create is attempted EACCES will be returned.
1438 1428 */
1439 1429 if (error) {
1440 1430 if (error == EACCES)
1441 1431 return (ENOENT);
1442 1432 return (error);
1443 1433 }
1444 1434
1445 1435 error = pn_get((char *)nm, UIO_SYSSPACE, &pn);
1446 1436 if (error == 0) {
1447 1437 error = VOP_LOOKUP(pvp, (char *)nm, vpp, &pn, flags, rootvp,
1448 1438 cr, NULL, deflags, rpnp);
1449 1439 pn_free(&pn);
1450 1440 }
1451 1441 VN_RELE(pvp);
1452 1442
1453 1443 return (error);
1454 1444 }
1455 1445
1456 1446 /* ARGSUSED */
1457 1447 static ino64_t
1458 1448 xattrdir_do_ino(vnode_t *vp, int index)
1459 1449 {
1460 1450 /*
1461 1451 * We use index 0 for the directory fid. Start
1462 1452 * the file numbering at 1.
↓ open down ↓ |
62 lines elided |
↑ open up ↑ |
1463 1453 */
1464 1454 return ((ino64_t)index+1);
1465 1455 }
1466 1456
1467 1457 void
1468 1458 xattr_init(void)
1469 1459 {
1470 1460 VERIFY(gfs_make_opsvec(xattr_opsvec) == 0);
1471 1461 }
1472 1462
1463 +/* See vnode.c: fop_lookup() */
1473 1464 int
1474 1465 xattr_dir_lookup(vnode_t *dvp, vnode_t **vpp, int flags, cred_t *cr)
1475 1466 {
1476 1467 int error = 0;
1468 + vnode_t *gfs_vp = NULL;
1469 + vnode_t *real_vp = NULL;
1470 + xattr_dir_t *xattr_dir;
1471 + struct pathname pn;
1472 + char *nm = "";
1477 1473
1478 1474 *vpp = NULL;
1479 1475
1480 1476 if (dvp->v_type != VDIR && dvp->v_type != VREG)
1481 1477 return (EINVAL);
1482 1478
1483 1479 mutex_enter(&dvp->v_lock);
1484 1480
1485 1481 /*
1486 1482 * If we're already in sysattr space, don't allow creation
1487 1483 * of another level of sysattrs.
1488 1484 */
1489 1485 if (dvp->v_flag & V_SYSATTR) {
1490 1486 mutex_exit(&dvp->v_lock);
1491 1487 return (EINVAL);
1492 1488 }
1493 1489
1494 1490 if (dvp->v_xattrdir != NULL) {
1495 - *vpp = dvp->v_xattrdir;
1496 - VN_HOLD(*vpp);
1491 + gfs_vp = dvp->v_xattrdir;
1492 + VN_HOLD(gfs_vp);
1497 1493 } else {
1498 1494 ulong_t val;
1499 1495 int xattrs_allowed = dvp->v_vfsp->vfs_flag & VFS_XATTR;
1500 1496 int sysattrs_allowed = 1;
1501 1497
1502 1498 /*
1503 1499 * We have to drop the lock on dvp. gfs_dir_create will
1504 1500 * grab it for a VN_HOLD.
1505 1501 */
1506 1502 mutex_exit(&dvp->v_lock);
1507 1503
1508 1504 /*
1509 1505 * If dvp allows xattr creation, but not sysattr
1510 1506 * creation, return the real xattr dir vp. We can't
1511 1507 * use the vfs feature mask here because _PC_SATTR_ENABLED
↓ open down ↓ |
5 lines elided |
↑ open up ↑ |
1512 1508 * has vnode-level granularity (e.g. .zfs).
1513 1509 */
1514 1510 error = VOP_PATHCONF(dvp, _PC_SATTR_ENABLED, &val, cr, NULL);
1515 1511 if (error != 0 || val == 0)
1516 1512 sysattrs_allowed = 0;
1517 1513
1518 1514 if (!xattrs_allowed && !sysattrs_allowed)
1519 1515 return (EINVAL);
1520 1516
1521 1517 if (!sysattrs_allowed) {
1522 - struct pathname pn;
1523 - char *nm = "";
1524 -
1525 1518 error = pn_get(nm, UIO_SYSSPACE, &pn);
1526 1519 if (error)
1527 1520 return (error);
1528 1521 error = VOP_LOOKUP(dvp, nm, vpp, &pn,
1529 1522 flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL,
1530 1523 NULL, NULL);
1531 1524 pn_free(&pn);
1532 1525 return (error);
1533 1526 }
1534 1527
1535 1528 /*
1536 1529 * Note that we act as if we were given CREATE_XATTR_DIR,
1537 1530 * but only for creation of the GFS directory.
1538 1531 */
1539 - *vpp = gfs_dir_create(
1532 + gfs_vp = gfs_dir_create(
1540 1533 sizeof (xattr_dir_t), dvp, xattr_dir_ops, xattr_dirents,
1541 1534 xattrdir_do_ino, MAXNAMELEN, NULL, xattr_lookup_cb);
1542 1535 mutex_enter(&dvp->v_lock);
1543 1536 if (dvp->v_xattrdir != NULL) {
1544 1537 /*
1545 1538 * We lost the race to create the xattr dir.
1546 1539 * Destroy this one, use the winner. We can't
1547 1540 * just call VN_RELE(*vpp), because the vnode
1548 1541 * is only partially initialized.
1549 1542 */
1550 - gfs_dir_t *dp = (*vpp)->v_data;
1543 + gfs_dir_t *dp = gfs_vp->v_data;
1551 1544
1552 - ASSERT((*vpp)->v_count == 1);
1553 - vn_free(*vpp);
1545 + ASSERT(gfs_vp->v_count == 1);
1546 + vn_free(gfs_vp);
1554 1547
1555 1548 mutex_destroy(&dp->gfsd_lock);
1556 1549 kmem_free(dp->gfsd_static,
1557 1550 dp->gfsd_nstatic * sizeof (gfs_dirent_t));
1558 1551 kmem_free(dp, dp->gfsd_file.gfs_size);
1559 1552
1560 1553 /*
1561 1554 * There is an implied VN_HOLD(dvp) here. We should
1562 1555 * be doing a VN_RELE(dvp) to clean up the reference
1563 - * from *vpp, and then a VN_HOLD(dvp) for the new
1556 + * from gfs_vp, and then a VN_HOLD(dvp) for the new
1564 1557 * reference. Instead, we just leave the count alone.
1565 1558 */
1566 1559
1567 - *vpp = dvp->v_xattrdir;
1568 - VN_HOLD(*vpp);
1560 + gfs_vp = dvp->v_xattrdir;
1561 + VN_HOLD(gfs_vp);
1569 1562 } else {
1570 - (*vpp)->v_flag |= (V_XATTRDIR|V_SYSATTR);
1571 - dvp->v_xattrdir = *vpp;
1563 + gfs_vp->v_flag |= (V_XATTRDIR|V_SYSATTR);
1564 + dvp->v_xattrdir = gfs_vp;
1572 1565 }
1573 1566 }
1574 1567 mutex_exit(&dvp->v_lock);
1575 1568
1576 - return (error);
1569 + /*
1570 + * In order to make this module relatively transparent
1571 + * to the underlying filesystem, we need to lookup the
1572 + * xattr dir in the lower filesystem and (if found)
1573 + * keep a hold on it for as long as there is a hold
1574 + * on the gfs_vp we're about to return. This hold is
1575 + * released in xattr_dir_inactive.
1576 + */
1577 + xattr_dir = gfs_vp->v_data;
1578 + if ((dvp->v_vfsp->vfs_flag & VFS_XATTR) &&
1579 + (xattr_dir->xattr_realvp == NULL)) {
1580 + error = pn_get(nm, UIO_SYSSPACE, &pn);
1581 + if (error == 0) {
1582 + error = VOP_LOOKUP(dvp, nm, &real_vp, &pn,
1583 + flags|LOOKUP_HAVE_SYSATTR_DIR, rootvp, cr, NULL,
1584 + NULL, NULL);
1585 + pn_free(&pn);
1586 + }
1587 + if (error == 0) {
1588 + mutex_enter(&gfs_vp->v_lock);
1589 + if (xattr_dir->xattr_realvp == NULL)
1590 + xattr_dir->xattr_realvp = real_vp;
1591 + else
1592 + VN_RELE(real_vp);
1593 + mutex_exit(&gfs_vp->v_lock);
1594 + }
1595 + }
1596 +
1597 + *vpp = gfs_vp;
1598 + return (0);
1577 1599 }
1578 1600
1579 1601 int
1580 1602 xattr_dir_vget(vfs_t *vfsp, vnode_t **vpp, fid_t *fidp)
1581 1603 {
1582 1604 int error;
1583 1605 vnode_t *pvp, *dvp;
1584 1606 xattr_fid_t *xfidp;
1585 1607 struct pathname pn;
1586 1608 char *nm;
1587 1609 uint16_t orig_len;
1588 1610
1589 1611 *vpp = NULL;
1590 1612
1591 1613 if (fidp->fid_len < XATTR_FIDSZ)
1592 1614 return (EINVAL);
1593 1615
1594 1616 xfidp = (xattr_fid_t *)fidp;
1595 1617 orig_len = fidp->fid_len;
1596 1618 fidp->fid_len = xfidp->parent_len;
1597 1619
1598 1620 error = VFS_VGET(vfsp, &pvp, fidp);
1599 1621 fidp->fid_len = orig_len;
1600 1622 if (error)
1601 1623 return (error);
1602 1624
1603 1625 /*
1604 1626 * Start by getting the GFS sysattr directory. We might need
1605 1627 * to recreate it during the VOP_LOOKUP.
1606 1628 */
1607 1629 nm = "";
1608 1630 error = pn_get(nm, UIO_SYSSPACE, &pn);
1609 1631 if (error) {
1610 1632 VN_RELE(pvp);
1611 1633 return (EINVAL);
1612 1634 }
1613 1635
1614 1636 error = VOP_LOOKUP(pvp, nm, &dvp, &pn, LOOKUP_XATTR|CREATE_XATTR_DIR,
1615 1637 rootvp, CRED(), NULL, NULL, NULL);
1616 1638 pn_free(&pn);
1617 1639 VN_RELE(pvp);
1618 1640 if (error)
1619 1641 return (error);
1620 1642
1621 1643 if (xfidp->dir_offset == 0) {
1622 1644 /*
1623 1645 * If we were looking for the directory, we're done.
1624 1646 */
1625 1647 *vpp = dvp;
1626 1648 return (0);
1627 1649 }
1628 1650
1629 1651 if (xfidp->dir_offset > XATTRDIR_NENTS) {
1630 1652 VN_RELE(dvp);
1631 1653 return (EINVAL);
1632 1654 }
1633 1655
1634 1656 nm = xattr_dirents[xfidp->dir_offset - 1].gfse_name;
1635 1657
1636 1658 error = pn_get(nm, UIO_SYSSPACE, &pn);
1637 1659 if (error) {
1638 1660 VN_RELE(dvp);
1639 1661 return (EINVAL);
1640 1662 }
1641 1663
1642 1664 error = VOP_LOOKUP(dvp, nm, vpp, &pn, 0, rootvp, CRED(), NULL,
1643 1665 NULL, NULL);
1644 1666
1645 1667 pn_free(&pn);
1646 1668 VN_RELE(dvp);
1647 1669
1648 1670 return (error);
1649 1671 }
↓ open down ↓ |
63 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX