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