1 /*- 2 * Copyright (c) 1999-2001 Robert N. M. Watson 3 * All rights reserved. 4 * 5 * This software was developed by Robert Watson for the TrustedBSD Project. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/capsicum.h> 35 #include <sys/lock.h> 36 #include <sys/mount.h> 37 #include <sys/mutex.h> 38 #include <sys/sysproto.h> 39 #include <sys/fcntl.h> 40 #include <sys/namei.h> 41 #include <sys/filedesc.h> 42 #include <sys/limits.h> 43 #include <sys/vnode.h> 44 #include <sys/proc.h> 45 #include <sys/extattr.h> 46 47 #include <security/audit/audit.h> 48 #include <security/mac/mac_framework.h> 49 50 /* 51 * Syscall to push extended attribute configuration information into the VFS. 52 * Accepts a path, which it converts to a mountpoint, as well as a command 53 * (int cmd), and attribute name and misc data. 54 * 55 * Currently this is used only by UFS1 extended attributes. 56 */ 57 int 58 sys_extattrctl(td, uap) 59 struct thread *td; 60 struct extattrctl_args /* { 61 const char *path; 62 int cmd; 63 const char *filename; 64 int attrnamespace; 65 const char *attrname; 66 } */ *uap; 67 { 68 struct vnode *filename_vp; 69 struct nameidata nd; 70 struct mount *mp, *mp_writable; 71 char attrname[EXTATTR_MAXNAMELEN]; 72 int error; 73 74 AUDIT_ARG_CMD(uap->cmd); 75 AUDIT_ARG_VALUE(uap->attrnamespace); 76 /* 77 * uap->attrname is not always defined. We check again later when we 78 * invoke the VFS call so as to pass in NULL there if needed. 79 */ 80 if (uap->attrname != NULL) { 81 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, 82 NULL); 83 if (error) 84 return (error); 85 } 86 AUDIT_ARG_TEXT(attrname); 87 88 mp = NULL; 89 filename_vp = NULL; 90 if (uap->filename != NULL) { 91 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE2, 92 UIO_USERSPACE, uap->filename, td); 93 error = namei(&nd); 94 if (error) 95 return (error); 96 filename_vp = nd.ni_vp; 97 NDFREE(&nd, NDF_NO_VP_RELE); 98 } 99 100 /* uap->path is always defined. */ 101 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, 102 UIO_USERSPACE, uap->path, td); 103 error = namei(&nd); 104 if (error) 105 goto out; 106 mp = nd.ni_vp->v_mount; 107 error = vfs_busy(mp, 0); 108 if (error) { 109 NDFREE(&nd, 0); 110 mp = NULL; 111 goto out; 112 } 113 VOP_UNLOCK(nd.ni_vp, 0); 114 error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | PCATCH); 115 NDFREE(&nd, NDF_NO_VP_UNLOCK); 116 if (error) 117 goto out; 118 if (filename_vp != NULL) { 119 /* 120 * uap->filename is not always defined. If it is, 121 * grab a vnode lock, which VFS_EXTATTRCTL() will 122 * later release. 123 */ 124 error = vn_lock(filename_vp, LK_EXCLUSIVE); 125 if (error) { 126 vn_finished_write(mp_writable); 127 goto out; 128 } 129 } 130 131 error = VFS_EXTATTRCTL(mp, uap->cmd, filename_vp, uap->attrnamespace, 132 uap->attrname != NULL ? attrname : NULL); 133 134 vn_finished_write(mp_writable); 135 out: 136 if (mp != NULL) 137 vfs_unbusy(mp); 138 139 /* 140 * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, filename_vp, 141 * so vrele it if it is defined. 142 */ 143 if (filename_vp != NULL) 144 vrele(filename_vp); 145 return (error); 146 } 147 148 /*- 149 * Set a named extended attribute on a file or directory 150 * 151 * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", 152 * kernelspace string pointer "attrname", userspace buffer 153 * pointer "data", buffer length "nbytes", thread "td". 154 * Returns: 0 on success, an error number otherwise 155 * Locks: none 156 * References: vp must be a valid reference for the duration of the call 157 */ 158 static int 159 extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname, 160 void *data, size_t nbytes, struct thread *td) 161 { 162 struct mount *mp; 163 struct uio auio; 164 struct iovec aiov; 165 ssize_t cnt; 166 int error; 167 168 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 169 if (error) 170 return (error); 171 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 172 173 aiov.iov_base = data; 174 aiov.iov_len = nbytes; 175 auio.uio_iov = &aiov; 176 auio.uio_iovcnt = 1; 177 auio.uio_offset = 0; 178 if (nbytes > IOSIZE_MAX) { 179 error = EINVAL; 180 goto done; 181 } 182 auio.uio_resid = nbytes; 183 auio.uio_rw = UIO_WRITE; 184 auio.uio_segflg = UIO_USERSPACE; 185 auio.uio_td = td; 186 cnt = nbytes; 187 188 #ifdef MAC 189 error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace, 190 attrname); 191 if (error) 192 goto done; 193 #endif 194 195 error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio, 196 td->td_ucred, td); 197 cnt -= auio.uio_resid; 198 td->td_retval[0] = cnt; 199 200 done: 201 VOP_UNLOCK(vp, 0); 202 vn_finished_write(mp); 203 return (error); 204 } 205 206 int 207 sys_extattr_set_fd(td, uap) 208 struct thread *td; 209 struct extattr_set_fd_args /* { 210 int fd; 211 int attrnamespace; 212 const char *attrname; 213 void *data; 214 size_t nbytes; 215 } */ *uap; 216 { 217 struct file *fp; 218 char attrname[EXTATTR_MAXNAMELEN]; 219 cap_rights_t rights; 220 int error; 221 222 AUDIT_ARG_FD(uap->fd); 223 AUDIT_ARG_VALUE(uap->attrnamespace); 224 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 225 if (error) 226 return (error); 227 AUDIT_ARG_TEXT(attrname); 228 229 error = getvnode(td->td_proc->p_fd, uap->fd, 230 cap_rights_init(&rights, CAP_EXTATTR_SET), &fp); 231 if (error) 232 return (error); 233 234 error = extattr_set_vp(fp->f_vnode, uap->attrnamespace, 235 attrname, uap->data, uap->nbytes, td); 236 fdrop(fp, td); 237 238 return (error); 239 } 240 241 int 242 sys_extattr_set_file(td, uap) 243 struct thread *td; 244 struct extattr_set_file_args /* { 245 const char *path; 246 int attrnamespace; 247 const char *attrname; 248 void *data; 249 size_t nbytes; 250 } */ *uap; 251 { 252 struct nameidata nd; 253 char attrname[EXTATTR_MAXNAMELEN]; 254 int error; 255 256 AUDIT_ARG_VALUE(uap->attrnamespace); 257 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 258 if (error) 259 return (error); 260 AUDIT_ARG_TEXT(attrname); 261 262 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, 263 uap->path, td); 264 error = namei(&nd); 265 if (error) 266 return (error); 267 NDFREE(&nd, NDF_ONLY_PNBUF); 268 269 error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname, 270 uap->data, uap->nbytes, td); 271 272 vrele(nd.ni_vp); 273 return (error); 274 } 275 276 int 277 sys_extattr_set_link(td, uap) 278 struct thread *td; 279 struct extattr_set_link_args /* { 280 const char *path; 281 int attrnamespace; 282 const char *attrname; 283 void *data; 284 size_t nbytes; 285 } */ *uap; 286 { 287 struct nameidata nd; 288 char attrname[EXTATTR_MAXNAMELEN]; 289 int error; 290 291 AUDIT_ARG_VALUE(uap->attrnamespace); 292 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 293 if (error) 294 return (error); 295 AUDIT_ARG_TEXT(attrname); 296 297 NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, 298 uap->path, td); 299 error = namei(&nd); 300 if (error) 301 return (error); 302 NDFREE(&nd, NDF_ONLY_PNBUF); 303 304 error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname, 305 uap->data, uap->nbytes, td); 306 307 vrele(nd.ni_vp); 308 return (error); 309 } 310 311 /*- 312 * Get a named extended attribute on a file or directory 313 * 314 * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", 315 * kernelspace string pointer "attrname", userspace buffer 316 * pointer "data", buffer length "nbytes", thread "td". 317 * Returns: 0 on success, an error number otherwise 318 * Locks: none 319 * References: vp must be a valid reference for the duration of the call 320 */ 321 static int 322 extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname, 323 void *data, size_t nbytes, struct thread *td) 324 { 325 struct uio auio, *auiop; 326 struct iovec aiov; 327 ssize_t cnt; 328 size_t size, *sizep; 329 int error; 330 331 vn_lock(vp, LK_SHARED | LK_RETRY); 332 333 /* 334 * Slightly unusual semantics: if the user provides a NULL data 335 * pointer, they don't want to receive the data, just the maximum 336 * read length. 337 */ 338 auiop = NULL; 339 sizep = NULL; 340 cnt = 0; 341 if (data != NULL) { 342 aiov.iov_base = data; 343 aiov.iov_len = nbytes; 344 auio.uio_iov = &aiov; 345 auio.uio_iovcnt = 1; 346 auio.uio_offset = 0; 347 if (nbytes > IOSIZE_MAX) { 348 error = EINVAL; 349 goto done; 350 } 351 auio.uio_resid = nbytes; 352 auio.uio_rw = UIO_READ; 353 auio.uio_segflg = UIO_USERSPACE; 354 auio.uio_td = td; 355 auiop = &auio; 356 cnt = nbytes; 357 } else 358 sizep = &size; 359 360 #ifdef MAC 361 error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace, 362 attrname); 363 if (error) 364 goto done; 365 #endif 366 367 error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep, 368 td->td_ucred, td); 369 370 if (auiop != NULL) { 371 cnt -= auio.uio_resid; 372 td->td_retval[0] = cnt; 373 } else 374 td->td_retval[0] = size; 375 376 done: 377 VOP_UNLOCK(vp, 0); 378 return (error); 379 } 380 381 int 382 sys_extattr_get_fd(td, uap) 383 struct thread *td; 384 struct extattr_get_fd_args /* { 385 int fd; 386 int attrnamespace; 387 const char *attrname; 388 void *data; 389 size_t nbytes; 390 } */ *uap; 391 { 392 struct file *fp; 393 char attrname[EXTATTR_MAXNAMELEN]; 394 cap_rights_t rights; 395 int error; 396 397 AUDIT_ARG_FD(uap->fd); 398 AUDIT_ARG_VALUE(uap->attrnamespace); 399 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 400 if (error) 401 return (error); 402 AUDIT_ARG_TEXT(attrname); 403 404 error = getvnode(td->td_proc->p_fd, uap->fd, 405 cap_rights_init(&rights, CAP_EXTATTR_GET), &fp); 406 if (error) 407 return (error); 408 409 error = extattr_get_vp(fp->f_vnode, uap->attrnamespace, 410 attrname, uap->data, uap->nbytes, td); 411 412 fdrop(fp, td); 413 return (error); 414 } 415 416 int 417 sys_extattr_get_file(td, uap) 418 struct thread *td; 419 struct extattr_get_file_args /* { 420 const char *path; 421 int attrnamespace; 422 const char *attrname; 423 void *data; 424 size_t nbytes; 425 } */ *uap; 426 { 427 struct nameidata nd; 428 char attrname[EXTATTR_MAXNAMELEN]; 429 int error; 430 431 AUDIT_ARG_VALUE(uap->attrnamespace); 432 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 433 if (error) 434 return (error); 435 AUDIT_ARG_TEXT(attrname); 436 437 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); 438 error = namei(&nd); 439 if (error) 440 return (error); 441 NDFREE(&nd, NDF_ONLY_PNBUF); 442 443 error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname, 444 uap->data, uap->nbytes, td); 445 446 vrele(nd.ni_vp); 447 return (error); 448 } 449 450 int 451 sys_extattr_get_link(td, uap) 452 struct thread *td; 453 struct extattr_get_link_args /* { 454 const char *path; 455 int attrnamespace; 456 const char *attrname; 457 void *data; 458 size_t nbytes; 459 } */ *uap; 460 { 461 struct nameidata nd; 462 char attrname[EXTATTR_MAXNAMELEN]; 463 int error; 464 465 AUDIT_ARG_VALUE(uap->attrnamespace); 466 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 467 if (error) 468 return (error); 469 AUDIT_ARG_TEXT(attrname); 470 471 NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, 472 td); 473 error = namei(&nd); 474 if (error) 475 return (error); 476 NDFREE(&nd, NDF_ONLY_PNBUF); 477 478 error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname, 479 uap->data, uap->nbytes, td); 480 481 vrele(nd.ni_vp); 482 return (error); 483 } 484 485 /* 486 * extattr_delete_vp(): Delete a named extended attribute on a file or 487 * directory 488 * 489 * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", 490 * kernelspace string pointer "attrname", proc "p" 491 * Returns: 0 on success, an error number otherwise 492 * Locks: none 493 * References: vp must be a valid reference for the duration of the call 494 */ 495 static int 496 extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname, 497 struct thread *td) 498 { 499 struct mount *mp; 500 int error; 501 502 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 503 if (error) 504 return (error); 505 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 506 507 #ifdef MAC 508 error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace, 509 attrname); 510 if (error) 511 goto done; 512 #endif 513 514 error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred, 515 td); 516 if (error == EOPNOTSUPP) 517 error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL, 518 td->td_ucred, td); 519 #ifdef MAC 520 done: 521 #endif 522 VOP_UNLOCK(vp, 0); 523 vn_finished_write(mp); 524 return (error); 525 } 526 527 int 528 sys_extattr_delete_fd(td, uap) 529 struct thread *td; 530 struct extattr_delete_fd_args /* { 531 int fd; 532 int attrnamespace; 533 const char *attrname; 534 } */ *uap; 535 { 536 struct file *fp; 537 char attrname[EXTATTR_MAXNAMELEN]; 538 cap_rights_t rights; 539 int error; 540 541 AUDIT_ARG_FD(uap->fd); 542 AUDIT_ARG_VALUE(uap->attrnamespace); 543 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 544 if (error) 545 return (error); 546 AUDIT_ARG_TEXT(attrname); 547 548 error = getvnode(td->td_proc->p_fd, uap->fd, 549 cap_rights_init(&rights, CAP_EXTATTR_DELETE), &fp); 550 if (error) 551 return (error); 552 553 error = extattr_delete_vp(fp->f_vnode, uap->attrnamespace, 554 attrname, td); 555 fdrop(fp, td); 556 return (error); 557 } 558 559 int 560 sys_extattr_delete_file(td, uap) 561 struct thread *td; 562 struct extattr_delete_file_args /* { 563 const char *path; 564 int attrnamespace; 565 const char *attrname; 566 } */ *uap; 567 { 568 struct nameidata nd; 569 char attrname[EXTATTR_MAXNAMELEN]; 570 int error; 571 572 AUDIT_ARG_VALUE(uap->attrnamespace); 573 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 574 if (error) 575 return(error); 576 AUDIT_ARG_TEXT(attrname); 577 578 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); 579 error = namei(&nd); 580 if (error) 581 return(error); 582 NDFREE(&nd, NDF_ONLY_PNBUF); 583 584 error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td); 585 vrele(nd.ni_vp); 586 return(error); 587 } 588 589 int 590 sys_extattr_delete_link(td, uap) 591 struct thread *td; 592 struct extattr_delete_link_args /* { 593 const char *path; 594 int attrnamespace; 595 const char *attrname; 596 } */ *uap; 597 { 598 struct nameidata nd; 599 char attrname[EXTATTR_MAXNAMELEN]; 600 int error; 601 602 AUDIT_ARG_VALUE(uap->attrnamespace); 603 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 604 if (error) 605 return(error); 606 AUDIT_ARG_TEXT(attrname); 607 608 NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); 609 error = namei(&nd); 610 if (error) 611 return(error); 612 NDFREE(&nd, NDF_ONLY_PNBUF); 613 614 error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td); 615 vrele(nd.ni_vp); 616 return(error); 617 } 618 619 /*- 620 * Retrieve a list of extended attributes on a file or directory. 621 * 622 * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace", 623 * userspace buffer pointer "data", buffer length "nbytes", 624 * thread "td". 625 * Returns: 0 on success, an error number otherwise 626 * Locks: none 627 * References: vp must be a valid reference for the duration of the call 628 */ 629 static int 630 extattr_list_vp(struct vnode *vp, int attrnamespace, void *data, 631 size_t nbytes, struct thread *td) 632 { 633 struct uio auio, *auiop; 634 size_t size, *sizep; 635 struct iovec aiov; 636 ssize_t cnt; 637 int error; 638 639 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 640 641 auiop = NULL; 642 sizep = NULL; 643 cnt = 0; 644 if (data != NULL) { 645 aiov.iov_base = data; 646 aiov.iov_len = nbytes; 647 auio.uio_iov = &aiov; 648 auio.uio_iovcnt = 1; 649 auio.uio_offset = 0; 650 if (nbytes > IOSIZE_MAX) { 651 error = EINVAL; 652 goto done; 653 } 654 auio.uio_resid = nbytes; 655 auio.uio_rw = UIO_READ; 656 auio.uio_segflg = UIO_USERSPACE; 657 auio.uio_td = td; 658 auiop = &auio; 659 cnt = nbytes; 660 } else 661 sizep = &size; 662 663 #ifdef MAC 664 error = mac_vnode_check_listextattr(td->td_ucred, vp, attrnamespace); 665 if (error) 666 goto done; 667 #endif 668 669 error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep, 670 td->td_ucred, td); 671 672 if (auiop != NULL) { 673 cnt -= auio.uio_resid; 674 td->td_retval[0] = cnt; 675 } else 676 td->td_retval[0] = size; 677 678 done: 679 VOP_UNLOCK(vp, 0); 680 return (error); 681 } 682 683 684 int 685 sys_extattr_list_fd(td, uap) 686 struct thread *td; 687 struct extattr_list_fd_args /* { 688 int fd; 689 int attrnamespace; 690 void *data; 691 size_t nbytes; 692 } */ *uap; 693 { 694 struct file *fp; 695 cap_rights_t rights; 696 int error; 697 698 AUDIT_ARG_FD(uap->fd); 699 AUDIT_ARG_VALUE(uap->attrnamespace); 700 error = getvnode(td->td_proc->p_fd, uap->fd, 701 cap_rights_init(&rights, CAP_EXTATTR_LIST), &fp); 702 if (error) 703 return (error); 704 705 error = extattr_list_vp(fp->f_vnode, uap->attrnamespace, uap->data, 706 uap->nbytes, td); 707 708 fdrop(fp, td); 709 return (error); 710 } 711 712 int 713 sys_extattr_list_file(td, uap) 714 struct thread*td; 715 struct extattr_list_file_args /* { 716 const char *path; 717 int attrnamespace; 718 void *data; 719 size_t nbytes; 720 } */ *uap; 721 { 722 struct nameidata nd; 723 int error; 724 725 AUDIT_ARG_VALUE(uap->attrnamespace); 726 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); 727 error = namei(&nd); 728 if (error) 729 return (error); 730 NDFREE(&nd, NDF_ONLY_PNBUF); 731 732 error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data, 733 uap->nbytes, td); 734 735 vrele(nd.ni_vp); 736 return (error); 737 } 738 739 int 740 sys_extattr_list_link(td, uap) 741 struct thread*td; 742 struct extattr_list_link_args /* { 743 const char *path; 744 int attrnamespace; 745 void *data; 746 size_t nbytes; 747 } */ *uap; 748 { 749 struct nameidata nd; 750 int error; 751 752 AUDIT_ARG_VALUE(uap->attrnamespace); 753 NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, 754 td); 755 error = namei(&nd); 756 if (error) 757 return (error); 758 NDFREE(&nd, NDF_ONLY_PNBUF); 759 760 error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data, 761 uap->nbytes, td); 762 763 vrele(nd.ni_vp); 764 return (error); 765 } 766