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 if (nbytes > IOSIZE_MAX) 171 return (EINVAL); 172 173 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 174 if (error) 175 return (error); 176 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 177 178 aiov.iov_base = data; 179 aiov.iov_len = nbytes; 180 auio.uio_iov = &aiov; 181 auio.uio_iovcnt = 1; 182 auio.uio_offset = 0; 183 auio.uio_resid = nbytes; 184 auio.uio_rw = UIO_WRITE; 185 auio.uio_segflg = UIO_USERSPACE; 186 auio.uio_td = td; 187 cnt = nbytes; 188 189 #ifdef MAC 190 error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace, 191 attrname); 192 if (error) 193 goto done; 194 #endif 195 196 error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio, 197 td->td_ucred, td); 198 cnt -= auio.uio_resid; 199 td->td_retval[0] = cnt; 200 201 #ifdef MAC 202 done: 203 #endif 204 VOP_UNLOCK(vp, 0); 205 vn_finished_write(mp); 206 return (error); 207 } 208 209 int 210 sys_extattr_set_fd(td, uap) 211 struct thread *td; 212 struct extattr_set_fd_args /* { 213 int fd; 214 int attrnamespace; 215 const char *attrname; 216 void *data; 217 size_t nbytes; 218 } */ *uap; 219 { 220 struct file *fp; 221 char attrname[EXTATTR_MAXNAMELEN]; 222 cap_rights_t rights; 223 int error; 224 225 AUDIT_ARG_FD(uap->fd); 226 AUDIT_ARG_VALUE(uap->attrnamespace); 227 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 228 if (error) 229 return (error); 230 AUDIT_ARG_TEXT(attrname); 231 232 error = getvnode(td, uap->fd, 233 cap_rights_init(&rights, CAP_EXTATTR_SET), &fp); 234 if (error) 235 return (error); 236 237 error = extattr_set_vp(fp->f_vnode, uap->attrnamespace, 238 attrname, uap->data, uap->nbytes, td); 239 fdrop(fp, td); 240 241 return (error); 242 } 243 244 int 245 sys_extattr_set_file(td, uap) 246 struct thread *td; 247 struct extattr_set_file_args /* { 248 const char *path; 249 int attrnamespace; 250 const char *attrname; 251 void *data; 252 size_t nbytes; 253 } */ *uap; 254 { 255 struct nameidata nd; 256 char attrname[EXTATTR_MAXNAMELEN]; 257 int error; 258 259 AUDIT_ARG_VALUE(uap->attrnamespace); 260 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 261 if (error) 262 return (error); 263 AUDIT_ARG_TEXT(attrname); 264 265 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, 266 uap->path, td); 267 error = namei(&nd); 268 if (error) 269 return (error); 270 NDFREE(&nd, NDF_ONLY_PNBUF); 271 272 error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname, 273 uap->data, uap->nbytes, td); 274 275 vrele(nd.ni_vp); 276 return (error); 277 } 278 279 int 280 sys_extattr_set_link(td, uap) 281 struct thread *td; 282 struct extattr_set_link_args /* { 283 const char *path; 284 int attrnamespace; 285 const char *attrname; 286 void *data; 287 size_t nbytes; 288 } */ *uap; 289 { 290 struct nameidata nd; 291 char attrname[EXTATTR_MAXNAMELEN]; 292 int error; 293 294 AUDIT_ARG_VALUE(uap->attrnamespace); 295 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 296 if (error) 297 return (error); 298 AUDIT_ARG_TEXT(attrname); 299 300 NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, 301 uap->path, td); 302 error = namei(&nd); 303 if (error) 304 return (error); 305 NDFREE(&nd, NDF_ONLY_PNBUF); 306 307 error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname, 308 uap->data, uap->nbytes, td); 309 310 vrele(nd.ni_vp); 311 return (error); 312 } 313 314 /*- 315 * Get a named extended attribute on a file or directory 316 * 317 * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", 318 * kernelspace string pointer "attrname", userspace buffer 319 * pointer "data", buffer length "nbytes", thread "td". 320 * Returns: 0 on success, an error number otherwise 321 * Locks: none 322 * References: vp must be a valid reference for the duration of the call 323 */ 324 static int 325 extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname, 326 void *data, size_t nbytes, struct thread *td) 327 { 328 struct uio auio, *auiop; 329 struct iovec aiov; 330 ssize_t cnt; 331 size_t size, *sizep; 332 int error; 333 334 if (nbytes > IOSIZE_MAX) 335 return (EINVAL); 336 337 vn_lock(vp, LK_SHARED | LK_RETRY); 338 339 /* 340 * Slightly unusual semantics: if the user provides a NULL data 341 * pointer, they don't want to receive the data, just the maximum 342 * read length. 343 */ 344 auiop = NULL; 345 sizep = NULL; 346 cnt = 0; 347 if (data != NULL) { 348 aiov.iov_base = data; 349 aiov.iov_len = nbytes; 350 auio.uio_iov = &aiov; 351 auio.uio_iovcnt = 1; 352 auio.uio_offset = 0; 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 #ifdef MAC 378 done: 379 #endif 380 VOP_UNLOCK(vp, 0); 381 return (error); 382 } 383 384 int 385 sys_extattr_get_fd(td, uap) 386 struct thread *td; 387 struct extattr_get_fd_args /* { 388 int fd; 389 int attrnamespace; 390 const char *attrname; 391 void *data; 392 size_t nbytes; 393 } */ *uap; 394 { 395 struct file *fp; 396 char attrname[EXTATTR_MAXNAMELEN]; 397 cap_rights_t rights; 398 int error; 399 400 AUDIT_ARG_FD(uap->fd); 401 AUDIT_ARG_VALUE(uap->attrnamespace); 402 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 403 if (error) 404 return (error); 405 AUDIT_ARG_TEXT(attrname); 406 407 error = getvnode(td, uap->fd, 408 cap_rights_init(&rights, CAP_EXTATTR_GET), &fp); 409 if (error) 410 return (error); 411 412 error = extattr_get_vp(fp->f_vnode, uap->attrnamespace, 413 attrname, uap->data, uap->nbytes, td); 414 415 fdrop(fp, td); 416 return (error); 417 } 418 419 int 420 sys_extattr_get_file(td, uap) 421 struct thread *td; 422 struct extattr_get_file_args /* { 423 const char *path; 424 int attrnamespace; 425 const char *attrname; 426 void *data; 427 size_t nbytes; 428 } */ *uap; 429 { 430 struct nameidata nd; 431 char attrname[EXTATTR_MAXNAMELEN]; 432 int error; 433 434 AUDIT_ARG_VALUE(uap->attrnamespace); 435 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 436 if (error) 437 return (error); 438 AUDIT_ARG_TEXT(attrname); 439 440 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); 441 error = namei(&nd); 442 if (error) 443 return (error); 444 NDFREE(&nd, NDF_ONLY_PNBUF); 445 446 error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname, 447 uap->data, uap->nbytes, td); 448 449 vrele(nd.ni_vp); 450 return (error); 451 } 452 453 int 454 sys_extattr_get_link(td, uap) 455 struct thread *td; 456 struct extattr_get_link_args /* { 457 const char *path; 458 int attrnamespace; 459 const char *attrname; 460 void *data; 461 size_t nbytes; 462 } */ *uap; 463 { 464 struct nameidata nd; 465 char attrname[EXTATTR_MAXNAMELEN]; 466 int error; 467 468 AUDIT_ARG_VALUE(uap->attrnamespace); 469 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 470 if (error) 471 return (error); 472 AUDIT_ARG_TEXT(attrname); 473 474 NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, 475 td); 476 error = namei(&nd); 477 if (error) 478 return (error); 479 NDFREE(&nd, NDF_ONLY_PNBUF); 480 481 error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname, 482 uap->data, uap->nbytes, td); 483 484 vrele(nd.ni_vp); 485 return (error); 486 } 487 488 /* 489 * extattr_delete_vp(): Delete a named extended attribute on a file or 490 * directory 491 * 492 * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", 493 * kernelspace string pointer "attrname", proc "p" 494 * Returns: 0 on success, an error number otherwise 495 * Locks: none 496 * References: vp must be a valid reference for the duration of the call 497 */ 498 static int 499 extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname, 500 struct thread *td) 501 { 502 struct mount *mp; 503 int error; 504 505 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 506 if (error) 507 return (error); 508 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 509 510 #ifdef MAC 511 error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace, 512 attrname); 513 if (error) 514 goto done; 515 #endif 516 517 error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred, 518 td); 519 if (error == EOPNOTSUPP) 520 error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL, 521 td->td_ucred, td); 522 #ifdef MAC 523 done: 524 #endif 525 VOP_UNLOCK(vp, 0); 526 vn_finished_write(mp); 527 return (error); 528 } 529 530 int 531 sys_extattr_delete_fd(td, uap) 532 struct thread *td; 533 struct extattr_delete_fd_args /* { 534 int fd; 535 int attrnamespace; 536 const char *attrname; 537 } */ *uap; 538 { 539 struct file *fp; 540 char attrname[EXTATTR_MAXNAMELEN]; 541 cap_rights_t rights; 542 int error; 543 544 AUDIT_ARG_FD(uap->fd); 545 AUDIT_ARG_VALUE(uap->attrnamespace); 546 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 547 if (error) 548 return (error); 549 AUDIT_ARG_TEXT(attrname); 550 551 error = getvnode(td, uap->fd, 552 cap_rights_init(&rights, CAP_EXTATTR_DELETE), &fp); 553 if (error) 554 return (error); 555 556 error = extattr_delete_vp(fp->f_vnode, uap->attrnamespace, 557 attrname, td); 558 fdrop(fp, td); 559 return (error); 560 } 561 562 int 563 sys_extattr_delete_file(td, uap) 564 struct thread *td; 565 struct extattr_delete_file_args /* { 566 const char *path; 567 int attrnamespace; 568 const char *attrname; 569 } */ *uap; 570 { 571 struct nameidata nd; 572 char attrname[EXTATTR_MAXNAMELEN]; 573 int error; 574 575 AUDIT_ARG_VALUE(uap->attrnamespace); 576 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 577 if (error) 578 return(error); 579 AUDIT_ARG_TEXT(attrname); 580 581 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); 582 error = namei(&nd); 583 if (error) 584 return(error); 585 NDFREE(&nd, NDF_ONLY_PNBUF); 586 587 error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td); 588 vrele(nd.ni_vp); 589 return(error); 590 } 591 592 int 593 sys_extattr_delete_link(td, uap) 594 struct thread *td; 595 struct extattr_delete_link_args /* { 596 const char *path; 597 int attrnamespace; 598 const char *attrname; 599 } */ *uap; 600 { 601 struct nameidata nd; 602 char attrname[EXTATTR_MAXNAMELEN]; 603 int error; 604 605 AUDIT_ARG_VALUE(uap->attrnamespace); 606 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL); 607 if (error) 608 return(error); 609 AUDIT_ARG_TEXT(attrname); 610 611 NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); 612 error = namei(&nd); 613 if (error) 614 return(error); 615 NDFREE(&nd, NDF_ONLY_PNBUF); 616 617 error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td); 618 vrele(nd.ni_vp); 619 return(error); 620 } 621 622 /*- 623 * Retrieve a list of extended attributes on a file or directory. 624 * 625 * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace", 626 * userspace buffer pointer "data", buffer length "nbytes", 627 * thread "td". 628 * Returns: 0 on success, an error number otherwise 629 * Locks: none 630 * References: vp must be a valid reference for the duration of the call 631 */ 632 static int 633 extattr_list_vp(struct vnode *vp, int attrnamespace, void *data, 634 size_t nbytes, struct thread *td) 635 { 636 struct uio auio, *auiop; 637 size_t size, *sizep; 638 struct iovec aiov; 639 ssize_t cnt; 640 int error; 641 642 if (nbytes > IOSIZE_MAX) 643 return (EINVAL); 644 645 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 646 647 auiop = NULL; 648 sizep = NULL; 649 cnt = 0; 650 if (data != NULL) { 651 aiov.iov_base = data; 652 aiov.iov_len = nbytes; 653 auio.uio_iov = &aiov; 654 auio.uio_iovcnt = 1; 655 auio.uio_offset = 0; 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 #ifdef MAC 680 done: 681 #endif 682 VOP_UNLOCK(vp, 0); 683 return (error); 684 } 685 686 687 int 688 sys_extattr_list_fd(td, uap) 689 struct thread *td; 690 struct extattr_list_fd_args /* { 691 int fd; 692 int attrnamespace; 693 void *data; 694 size_t nbytes; 695 } */ *uap; 696 { 697 struct file *fp; 698 cap_rights_t rights; 699 int error; 700 701 AUDIT_ARG_FD(uap->fd); 702 AUDIT_ARG_VALUE(uap->attrnamespace); 703 error = getvnode(td, uap->fd, 704 cap_rights_init(&rights, CAP_EXTATTR_LIST), &fp); 705 if (error) 706 return (error); 707 708 error = extattr_list_vp(fp->f_vnode, uap->attrnamespace, uap->data, 709 uap->nbytes, td); 710 711 fdrop(fp, td); 712 return (error); 713 } 714 715 int 716 sys_extattr_list_file(td, uap) 717 struct thread*td; 718 struct extattr_list_file_args /* { 719 const char *path; 720 int attrnamespace; 721 void *data; 722 size_t nbytes; 723 } */ *uap; 724 { 725 struct nameidata nd; 726 int error; 727 728 AUDIT_ARG_VALUE(uap->attrnamespace); 729 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td); 730 error = namei(&nd); 731 if (error) 732 return (error); 733 NDFREE(&nd, NDF_ONLY_PNBUF); 734 735 error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data, 736 uap->nbytes, td); 737 738 vrele(nd.ni_vp); 739 return (error); 740 } 741 742 int 743 sys_extattr_list_link(td, uap) 744 struct thread*td; 745 struct extattr_list_link_args /* { 746 const char *path; 747 int attrnamespace; 748 void *data; 749 size_t nbytes; 750 } */ *uap; 751 { 752 struct nameidata nd; 753 int error; 754 755 AUDIT_ARG_VALUE(uap->attrnamespace); 756 NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, 757 td); 758 error = namei(&nd); 759 if (error) 760 return (error); 761 NDFREE(&nd, NDF_ONLY_PNBUF); 762 763 error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data, 764 uap->nbytes, td); 765 766 vrele(nd.ni_vp); 767 return (error); 768 } 769