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