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