1 /*- 2 * Copyright (c) 1999-2002 Robert N. M. Watson 3 * Copyright (c) 2002-2003 Networks Associates Technology, Inc. 4 * All rights reserved. 5 * 6 * This software was developed by Robert Watson for the TrustedBSD Project. 7 * 8 * This software was developed for the FreeBSD Project in part by Network 9 * Associates Laboratories, the Security Research Division of Network 10 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), 11 * as part of the DARPA CHATS research program. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 */ 35 36 /* 37 * Support for filesystem extended attribute: UFS-specific support functions. 38 */ 39 40 #include <sys/cdefs.h> 41 __FBSDID("$FreeBSD$"); 42 43 #include "opt_ufs.h" 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/kernel.h> 48 #include <sys/namei.h> 49 #include <sys/malloc.h> 50 #include <sys/fcntl.h> 51 #include <sys/priv.h> 52 #include <sys/proc.h> 53 #include <sys/vnode.h> 54 #include <sys/mount.h> 55 #include <sys/lock.h> 56 #include <sys/dirent.h> 57 #include <sys/extattr.h> 58 #include <sys/sx.h> 59 #include <sys/sysctl.h> 60 61 #include <vm/uma.h> 62 63 #include <ufs/ufs/dir.h> 64 #include <ufs/ufs/extattr.h> 65 #include <ufs/ufs/quota.h> 66 #include <ufs/ufs/ufsmount.h> 67 #include <ufs/ufs/inode.h> 68 #include <ufs/ufs/ufs_extern.h> 69 70 #ifdef UFS_EXTATTR 71 72 static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute"); 73 74 static int ufs_extattr_sync = 0; 75 SYSCTL_INT(_debug, OID_AUTO, ufs_extattr_sync, CTLFLAG_RW, &ufs_extattr_sync, 76 0, ""); 77 78 static int ufs_extattr_valid_attrname(int attrnamespace, 79 const char *attrname); 80 static int ufs_extattr_enable_with_open(struct ufsmount *ump, 81 struct vnode *vp, int attrnamespace, const char *attrname, 82 struct thread *td); 83 static int ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 84 const char *attrname, struct vnode *backing_vnode, 85 struct thread *td); 86 static int ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, 87 const char *attrname, struct thread *td); 88 static int ufs_extattr_get(struct vnode *vp, int attrnamespace, 89 const char *name, struct uio *uio, size_t *size, 90 struct ucred *cred, struct thread *td); 91 static int ufs_extattr_set(struct vnode *vp, int attrnamespace, 92 const char *name, struct uio *uio, struct ucred *cred, 93 struct thread *td); 94 static int ufs_extattr_rm(struct vnode *vp, int attrnamespace, 95 const char *name, struct ucred *cred, struct thread *td); 96 #ifdef UFS_EXTATTR_AUTOSTART 97 static int ufs_extattr_autostart_locked(struct mount *mp, 98 struct thread *td); 99 #endif 100 static int ufs_extattr_start_locked(struct ufsmount *ump, 101 struct thread *td); 102 103 /* 104 * Per-FS attribute lock protecting attribute operations. 105 * 106 * XXXRW: Perhaps something more fine-grained would be appropriate, but at 107 * the end of the day we're going to contend on the vnode lock for the 108 * backing file anyway. 109 */ 110 static void 111 ufs_extattr_uepm_lock(struct ufsmount *ump, struct thread *td) 112 { 113 114 sx_xlock(&ump->um_extattr.uepm_lock); 115 } 116 117 static void 118 ufs_extattr_uepm_unlock(struct ufsmount *ump, struct thread *td) 119 { 120 121 sx_xunlock(&ump->um_extattr.uepm_lock); 122 } 123 124 /*- 125 * Determine whether the name passed is a valid name for an actual 126 * attribute. 127 * 128 * Invalid currently consists of: 129 * NULL pointer for attrname 130 * zero-length attrname (used to retrieve application attribute list) 131 */ 132 static int 133 ufs_extattr_valid_attrname(int attrnamespace, const char *attrname) 134 { 135 136 if (attrname == NULL) 137 return (0); 138 if (strlen(attrname) == 0) 139 return (0); 140 return (1); 141 } 142 143 /* 144 * Locate an attribute given a name and mountpoint. 145 * Must be holding uepm lock for the mount point. 146 */ 147 static struct ufs_extattr_list_entry * 148 ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace, 149 const char *attrname) 150 { 151 struct ufs_extattr_list_entry *search_attribute; 152 153 sx_assert(&ump->um_extattr.uepm_lock, SA_XLOCKED); 154 155 for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list); 156 search_attribute != NULL; 157 search_attribute = LIST_NEXT(search_attribute, uele_entries)) { 158 if (!(strncmp(attrname, search_attribute->uele_attrname, 159 UFS_EXTATTR_MAXEXTATTRNAME)) && 160 (attrnamespace == search_attribute->uele_attrnamespace)) { 161 return (search_attribute); 162 } 163 } 164 165 return (0); 166 } 167 168 /* 169 * Initialize per-FS structures supporting extended attributes. Do not 170 * start extended attributes yet. 171 */ 172 void 173 ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm) 174 { 175 176 uepm->uepm_flags = 0; 177 LIST_INIT(&uepm->uepm_list); 178 sx_init(&uepm->uepm_lock, "ufs_extattr_sx"); 179 uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED; 180 } 181 182 /* 183 * Destroy per-FS structures supporting extended attributes. Assumes 184 * that EAs have already been stopped, and will panic if not. 185 */ 186 void 187 ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm) 188 { 189 190 if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 191 panic("ufs_extattr_uepm_destroy: not initialized"); 192 193 if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 194 panic("ufs_extattr_uepm_destroy: called while still started"); 195 196 /* 197 * It's not clear that either order for the next two lines is 198 * ideal, and it should never be a problem if this is only called 199 * during unmount, and with vfs_busy(). 200 */ 201 uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED; 202 sx_destroy(&uepm->uepm_lock); 203 } 204 205 /* 206 * Start extended attribute support on an FS. 207 */ 208 int 209 ufs_extattr_start(struct mount *mp, struct thread *td) 210 { 211 struct ufsmount *ump; 212 int error = 0; 213 214 ump = VFSTOUFS(mp); 215 216 ufs_extattr_uepm_lock(ump, td); 217 error = ufs_extattr_start_locked(ump, td); 218 ufs_extattr_uepm_unlock(ump, td); 219 return (error); 220 } 221 222 static int 223 ufs_extattr_start_locked(struct ufsmount *ump, struct thread *td) 224 { 225 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 226 return (EOPNOTSUPP); 227 if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) 228 return (EBUSY); 229 230 ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED; 231 ump->um_extattr.uepm_ucred = crhold(td->td_ucred); 232 return (0); 233 } 234 235 #ifdef UFS_EXTATTR_AUTOSTART 236 /* 237 * Helper routine: given a locked parent directory and filename, return 238 * the locked vnode of the inode associated with the name. Will not 239 * follow symlinks, may return any type of vnode. Lock on parent will 240 * be released even in the event of a failure. In the event that the 241 * target is the parent (i.e., "."), there will be two references and 242 * one lock, requiring the caller to possibly special-case. 243 */ 244 #define UE_GETDIR_LOCKPARENT 1 245 #define UE_GETDIR_LOCKPARENT_DONT 2 246 static int 247 ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname, 248 struct vnode **vp, struct thread *td) 249 { 250 struct vop_cachedlookup_args vargs; 251 struct componentname cnp; 252 struct vnode *target_vp; 253 int error; 254 255 bzero(&cnp, sizeof(cnp)); 256 cnp.cn_nameiop = LOOKUP; 257 cnp.cn_flags = ISLASTCN; 258 if (lockparent == UE_GETDIR_LOCKPARENT) 259 cnp.cn_flags |= LOCKPARENT; 260 cnp.cn_lkflags = LK_EXCLUSIVE; 261 cnp.cn_thread = td; 262 cnp.cn_cred = td->td_ucred; 263 cnp.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); 264 cnp.cn_nameptr = cnp.cn_pnbuf; 265 error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN, 266 (size_t *) &cnp.cn_namelen); 267 if (error) { 268 if (lockparent == UE_GETDIR_LOCKPARENT_DONT) { 269 VOP_UNLOCK(start_dvp, 0); 270 } 271 uma_zfree(namei_zone, cnp.cn_pnbuf); 272 printf("ufs_extattr_lookup: copystr failed\n"); 273 return (error); 274 } 275 cnp.cn_namelen--; /* trim nul termination */ 276 vargs.a_gen.a_desc = NULL; 277 vargs.a_dvp = start_dvp; 278 vargs.a_vpp = &target_vp; 279 vargs.a_cnp = &cnp; 280 error = ufs_lookup(&vargs); 281 uma_zfree(namei_zone, cnp.cn_pnbuf); 282 if (error) { 283 /* 284 * Error condition, may have to release the lock on the parent 285 * if ufs_lookup() didn't. 286 */ 287 if (lockparent == UE_GETDIR_LOCKPARENT_DONT) 288 VOP_UNLOCK(start_dvp, 0); 289 290 /* 291 * Check that ufs_lookup() didn't release the lock when we 292 * didn't want it to. 293 */ 294 if (lockparent == UE_GETDIR_LOCKPARENT) 295 ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup"); 296 297 return (error); 298 } 299 /* 300 if (target_vp == start_dvp) 301 panic("ufs_extattr_lookup: target_vp == start_dvp"); 302 */ 303 304 if (target_vp != start_dvp && lockparent == UE_GETDIR_LOCKPARENT_DONT) 305 VOP_UNLOCK(start_dvp, 0); 306 307 if (lockparent == UE_GETDIR_LOCKPARENT) 308 ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup"); 309 310 /* printf("ufs_extattr_lookup: success\n"); */ 311 *vp = target_vp; 312 return (0); 313 } 314 #endif /* !UFS_EXTATTR_AUTOSTART */ 315 316 /* 317 * Enable an EA using the passed filesystem, backing vnode, attribute name, 318 * namespace, and proc. Will perform a VOP_OPEN() on the vp, so expects vp 319 * to be locked when passed in. The vnode will be returned unlocked, 320 * regardless of success/failure of the function. As a result, the caller 321 * will always need to vrele(), but not vput(). 322 */ 323 static int 324 ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp, 325 int attrnamespace, const char *attrname, struct thread *td) 326 { 327 int error; 328 329 error = VOP_OPEN(vp, FREAD|FWRITE, td->td_ucred, td, NULL); 330 if (error) { 331 printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed " 332 "with %d\n", error); 333 VOP_UNLOCK(vp, 0); 334 return (error); 335 } 336 337 vp->v_writecount++; 338 339 vref(vp); 340 341 VOP_UNLOCK(vp, 0); 342 343 error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, td); 344 if (error != 0) 345 vn_close(vp, FREAD|FWRITE, td->td_ucred, td); 346 return (error); 347 } 348 349 #ifdef UFS_EXTATTR_AUTOSTART 350 /* 351 * Given a locked directory vnode, iterate over the names in the directory 352 * and use ufs_extattr_lookup() to retrieve locked vnodes of potential 353 * attribute files. Then invoke ufs_extattr_enable_with_open() on each 354 * to attempt to start the attribute. Leaves the directory locked on 355 * exit. 356 */ 357 static int 358 ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp, 359 int attrnamespace, struct thread *td) 360 { 361 struct vop_readdir_args vargs; 362 struct dirent *dp, *edp; 363 struct vnode *attr_vp; 364 struct uio auio; 365 struct iovec aiov; 366 char *dirbuf; 367 int error, eofflag = 0; 368 369 if (dvp->v_type != VDIR) 370 return (ENOTDIR); 371 372 dirbuf = malloc(DIRBLKSIZ, M_TEMP, M_WAITOK); 373 374 auio.uio_iov = &aiov; 375 auio.uio_iovcnt = 1; 376 auio.uio_rw = UIO_READ; 377 auio.uio_segflg = UIO_SYSSPACE; 378 auio.uio_td = td; 379 auio.uio_offset = 0; 380 381 vargs.a_gen.a_desc = NULL; 382 vargs.a_vp = dvp; 383 vargs.a_uio = &auio; 384 vargs.a_cred = td->td_ucred; 385 vargs.a_eofflag = &eofflag; 386 vargs.a_ncookies = NULL; 387 vargs.a_cookies = NULL; 388 389 while (!eofflag) { 390 auio.uio_resid = DIRBLKSIZ; 391 aiov.iov_base = dirbuf; 392 aiov.iov_len = DIRBLKSIZ; 393 error = ufs_readdir(&vargs); 394 if (error) { 395 printf("ufs_extattr_iterate_directory: ufs_readdir " 396 "%d\n", error); 397 return (error); 398 } 399 400 /* 401 * XXXRW: While in UFS, we always get DIRBLKSIZ returns from 402 * the directory code on success, on other file systems this 403 * may not be the case. For portability, we should check the 404 * read length on return from ufs_readdir(). 405 */ 406 edp = (struct dirent *)&dirbuf[DIRBLKSIZ]; 407 for (dp = (struct dirent *)dirbuf; dp < edp; ) { 408 #if (BYTE_ORDER == LITTLE_ENDIAN) 409 dp->d_type = dp->d_namlen; 410 dp->d_namlen = 0; 411 #else 412 dp->d_type = 0; 413 #endif 414 if (dp->d_reclen == 0) 415 break; 416 error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT, 417 dp->d_name, &attr_vp, td); 418 if (error) { 419 printf("ufs_extattr_iterate_directory: lookup " 420 "%s %d\n", dp->d_name, error); 421 } else if (attr_vp == dvp) { 422 vrele(attr_vp); 423 } else if (attr_vp->v_type != VREG) { 424 vput(attr_vp); 425 } else { 426 error = ufs_extattr_enable_with_open(ump, 427 attr_vp, attrnamespace, dp->d_name, td); 428 vrele(attr_vp); 429 if (error) { 430 printf("ufs_extattr_iterate_directory: " 431 "enable %s %d\n", dp->d_name, 432 error); 433 } else if (bootverbose) { 434 printf("UFS autostarted EA %s\n", 435 dp->d_name); 436 } 437 } 438 dp = (struct dirent *) ((char *)dp + dp->d_reclen); 439 if (dp >= edp) 440 break; 441 } 442 } 443 free(dirbuf, M_TEMP); 444 445 return (0); 446 } 447 448 /* 449 * Auto-start of extended attributes, to be executed (optionally) at 450 * mount-time. 451 */ 452 int 453 ufs_extattr_autostart(struct mount *mp, struct thread *td) 454 { 455 struct ufsmount *ump; 456 int error; 457 458 ump = VFSTOUFS(mp); 459 ufs_extattr_uepm_lock(ump, td); 460 error = ufs_extattr_autostart_locked(mp, td); 461 ufs_extattr_uepm_unlock(ump, td); 462 return (error); 463 } 464 465 static int 466 ufs_extattr_autostart_locked(struct mount *mp, struct thread *td) 467 { 468 struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp; 469 struct ufsmount *ump = VFSTOUFS(mp); 470 int error; 471 472 /* 473 * UFS_EXTATTR applies only to UFS1, as UFS2 uses native extended 474 * attributes, so don't autostart. 475 */ 476 if (ump->um_fstype != UFS1) 477 return (0); 478 479 /* 480 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root? 481 * If so, automatically start EA's. 482 */ 483 error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp); 484 if (error) { 485 printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n", 486 error); 487 return (error); 488 } 489 490 error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT, 491 UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, td); 492 if (error) { 493 /* rvp ref'd but now unlocked */ 494 vrele(rvp); 495 return (error); 496 } 497 if (rvp == attr_dvp) { 498 /* Should never happen. */ 499 vput(rvp); 500 vrele(attr_dvp); 501 return (EINVAL); 502 } 503 vrele(rvp); 504 505 if (attr_dvp->v_type != VDIR) { 506 printf("ufs_extattr_autostart: %s != VDIR\n", 507 UFS_EXTATTR_FSROOTSUBDIR); 508 goto return_vput_attr_dvp; 509 } 510 511 error = ufs_extattr_start_locked(ump, td); 512 if (error) { 513 printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n", 514 error); 515 goto return_vput_attr_dvp; 516 } 517 518 /* 519 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM, 520 * UFS_EXTATTR_SUBDIR_USER. For each, iterate over the sub-directory, 521 * and start with appropriate type. Failures in either don't 522 * result in an over-all failure. attr_dvp is left locked to 523 * be cleaned up on exit. 524 */ 525 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 526 UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, td); 527 if (!error) { 528 error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 529 attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, td); 530 if (error) 531 printf("ufs_extattr_iterate_directory returned %d\n", 532 error); 533 vput(attr_system_dvp); 534 } 535 536 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 537 UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, td); 538 if (!error) { 539 error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 540 attr_user_dvp, EXTATTR_NAMESPACE_USER, td); 541 if (error) 542 printf("ufs_extattr_iterate_directory returned %d\n", 543 error); 544 vput(attr_user_dvp); 545 } 546 547 /* Mask startup failures in sub-directories. */ 548 error = 0; 549 550 return_vput_attr_dvp: 551 vput(attr_dvp); 552 553 return (error); 554 } 555 #endif /* !UFS_EXTATTR_AUTOSTART */ 556 557 /* 558 * Stop extended attribute support on an FS. 559 */ 560 int 561 ufs_extattr_stop(struct mount *mp, struct thread *td) 562 { 563 struct ufs_extattr_list_entry *uele; 564 struct ufsmount *ump = VFSTOUFS(mp); 565 int error = 0; 566 567 ufs_extattr_uepm_lock(ump, td); 568 569 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 570 error = EOPNOTSUPP; 571 goto unlock; 572 } 573 574 while ((uele = LIST_FIRST(&ump->um_extattr.uepm_list)) != NULL) { 575 ufs_extattr_disable(ump, uele->uele_attrnamespace, 576 uele->uele_attrname, td); 577 } 578 579 ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED; 580 581 crfree(ump->um_extattr.uepm_ucred); 582 ump->um_extattr.uepm_ucred = NULL; 583 584 unlock: 585 ufs_extattr_uepm_unlock(ump, td); 586 587 return (error); 588 } 589 590 /* 591 * Enable a named attribute on the specified filesystem; provide an 592 * unlocked backing vnode to hold the attribute data. 593 */ 594 static int 595 ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 596 const char *attrname, struct vnode *backing_vnode, struct thread *td) 597 { 598 struct ufs_extattr_list_entry *attribute; 599 struct iovec aiov; 600 struct uio auio; 601 int error = 0; 602 603 if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 604 return (EINVAL); 605 if (backing_vnode->v_type != VREG) 606 return (EINVAL); 607 608 attribute = malloc(sizeof(struct ufs_extattr_list_entry), 609 M_UFS_EXTATTR, M_WAITOK); 610 if (attribute == NULL) 611 return (ENOMEM); 612 613 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 614 error = EOPNOTSUPP; 615 goto free_exit; 616 } 617 618 if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) { 619 error = EEXIST; 620 goto free_exit; 621 } 622 623 strncpy(attribute->uele_attrname, attrname, 624 UFS_EXTATTR_MAXEXTATTRNAME); 625 attribute->uele_attrnamespace = attrnamespace; 626 bzero(&attribute->uele_fileheader, 627 sizeof(struct ufs_extattr_fileheader)); 628 629 attribute->uele_backing_vnode = backing_vnode; 630 631 auio.uio_iov = &aiov; 632 auio.uio_iovcnt = 1; 633 aiov.iov_base = (caddr_t) &attribute->uele_fileheader; 634 aiov.iov_len = sizeof(struct ufs_extattr_fileheader); 635 auio.uio_resid = sizeof(struct ufs_extattr_fileheader); 636 auio.uio_offset = (off_t) 0; 637 auio.uio_segflg = UIO_SYSSPACE; 638 auio.uio_rw = UIO_READ; 639 auio.uio_td = td; 640 641 vn_lock(backing_vnode, LK_SHARED | LK_RETRY); 642 error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED, 643 ump->um_extattr.uepm_ucred); 644 645 if (error) 646 goto unlock_free_exit; 647 648 if (auio.uio_resid != 0) { 649 printf("ufs_extattr_enable: malformed attribute header\n"); 650 error = EINVAL; 651 goto unlock_free_exit; 652 } 653 654 if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) { 655 printf("ufs_extattr_enable: invalid attribute header magic\n"); 656 error = EINVAL; 657 goto unlock_free_exit; 658 } 659 660 if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) { 661 printf("ufs_extattr_enable: incorrect attribute header " 662 "version\n"); 663 error = EINVAL; 664 goto unlock_free_exit; 665 } 666 667 ASSERT_VOP_LOCKED(backing_vnode, "ufs_extattr_enable"); 668 LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, 669 uele_entries); 670 671 VOP_UNLOCK(backing_vnode, 0); 672 return (0); 673 674 unlock_free_exit: 675 VOP_UNLOCK(backing_vnode, 0); 676 677 free_exit: 678 free(attribute, M_UFS_EXTATTR); 679 return (error); 680 } 681 682 /* 683 * Disable extended attribute support on an FS. 684 */ 685 static int 686 ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, 687 const char *attrname, struct thread *td) 688 { 689 struct ufs_extattr_list_entry *uele; 690 int error = 0; 691 692 if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 693 return (EINVAL); 694 695 uele = ufs_extattr_find_attr(ump, attrnamespace, attrname); 696 if (!uele) 697 return (ENOATTR); 698 699 LIST_REMOVE(uele, uele_entries); 700 701 vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY); 702 ASSERT_VOP_LOCKED(uele->uele_backing_vnode, "ufs_extattr_disable"); 703 VOP_UNLOCK(uele->uele_backing_vnode, 0); 704 error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, 705 td->td_ucred, td); 706 707 free(uele, M_UFS_EXTATTR); 708 709 return (error); 710 } 711 712 /* 713 * VFS call to manage extended attributes in UFS. If filename_vp is 714 * non-NULL, it must be passed in locked, and regardless of errors in 715 * processing, will be unlocked. 716 */ 717 int 718 ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, 719 int attrnamespace, const char *attrname) 720 { 721 struct ufsmount *ump = VFSTOUFS(mp); 722 struct thread *td = curthread; 723 int error; 724 725 /* 726 * Processes with privilege, but in jail, are not allowed to 727 * configure extended attributes. 728 */ 729 error = priv_check(td, PRIV_UFS_EXTATTRCTL); 730 if (error) { 731 if (filename_vp != NULL) 732 VOP_UNLOCK(filename_vp, 0); 733 return (error); 734 } 735 736 /* 737 * We only allow extattrctl(2) on UFS1 file systems, as UFS2 uses 738 * native extended attributes. 739 */ 740 if (ump->um_fstype != UFS1) { 741 if (filename_vp != NULL) 742 VOP_UNLOCK(filename_vp, 0); 743 return (EOPNOTSUPP); 744 } 745 746 switch(cmd) { 747 case UFS_EXTATTR_CMD_START: 748 if (filename_vp != NULL) { 749 VOP_UNLOCK(filename_vp, 0); 750 return (EINVAL); 751 } 752 if (attrname != NULL) 753 return (EINVAL); 754 755 error = ufs_extattr_start(mp, td); 756 757 return (error); 758 759 case UFS_EXTATTR_CMD_STOP: 760 if (filename_vp != NULL) { 761 VOP_UNLOCK(filename_vp, 0); 762 return (EINVAL); 763 } 764 if (attrname != NULL) 765 return (EINVAL); 766 767 error = ufs_extattr_stop(mp, td); 768 769 return (error); 770 771 case UFS_EXTATTR_CMD_ENABLE: 772 773 if (filename_vp == NULL) 774 return (EINVAL); 775 if (attrname == NULL) { 776 VOP_UNLOCK(filename_vp, 0); 777 return (EINVAL); 778 } 779 780 /* 781 * ufs_extattr_enable_with_open() will always unlock the 782 * vnode, regardless of failure. 783 */ 784 ufs_extattr_uepm_lock(ump, td); 785 error = ufs_extattr_enable_with_open(ump, filename_vp, 786 attrnamespace, attrname, td); 787 ufs_extattr_uepm_unlock(ump, td); 788 789 return (error); 790 791 case UFS_EXTATTR_CMD_DISABLE: 792 793 if (filename_vp != NULL) { 794 VOP_UNLOCK(filename_vp, 0); 795 return (EINVAL); 796 } 797 if (attrname == NULL) 798 return (EINVAL); 799 800 ufs_extattr_uepm_lock(ump, td); 801 error = ufs_extattr_disable(ump, attrnamespace, attrname, 802 td); 803 ufs_extattr_uepm_unlock(ump, td); 804 805 return (error); 806 807 default: 808 return (EINVAL); 809 } 810 } 811 812 /* 813 * Vnode operating to retrieve a named extended attribute. 814 */ 815 int 816 ufs_getextattr(struct vop_getextattr_args *ap) 817 /* 818 vop_getextattr { 819 IN struct vnode *a_vp; 820 IN int a_attrnamespace; 821 IN const char *a_name; 822 INOUT struct uio *a_uio; 823 OUT size_t *a_size; 824 IN struct ucred *a_cred; 825 IN struct thread *a_td; 826 }; 827 */ 828 { 829 struct mount *mp = ap->a_vp->v_mount; 830 struct ufsmount *ump = VFSTOUFS(mp); 831 int error; 832 833 ufs_extattr_uepm_lock(ump, ap->a_td); 834 835 error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name, 836 ap->a_uio, ap->a_size, ap->a_cred, ap->a_td); 837 838 ufs_extattr_uepm_unlock(ump, ap->a_td); 839 840 return (error); 841 } 842 843 /* 844 * Real work associated with retrieving a named attribute--assumes that 845 * the attribute lock has already been grabbed. 846 */ 847 static int 848 ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name, 849 struct uio *uio, size_t *size, struct ucred *cred, struct thread *td) 850 { 851 struct ufs_extattr_list_entry *attribute; 852 struct ufs_extattr_header ueh; 853 struct iovec local_aiov; 854 struct uio local_aio; 855 struct mount *mp = vp->v_mount; 856 struct ufsmount *ump = VFSTOUFS(mp); 857 struct inode *ip = VTOI(vp); 858 off_t base_offset; 859 size_t len, old_len; 860 int error = 0; 861 862 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 863 return (EOPNOTSUPP); 864 865 if (strlen(name) == 0) 866 return (EINVAL); 867 868 error = extattr_check_cred(vp, attrnamespace, cred, td, VREAD); 869 if (error) 870 return (error); 871 872 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 873 if (!attribute) 874 return (ENOATTR); 875 876 /* 877 * Allow only offsets of zero to encourage the read/replace 878 * extended attribute semantic. Otherwise we can't guarantee 879 * atomicity, as we don't provide locks for extended attributes. 880 */ 881 if (uio != NULL && uio->uio_offset != 0) 882 return (ENXIO); 883 884 /* 885 * Find base offset of header in file based on file header size, and 886 * data header size + maximum data size, indexed by inode number. 887 */ 888 base_offset = sizeof(struct ufs_extattr_fileheader) + 889 ip->i_number * (sizeof(struct ufs_extattr_header) + 890 attribute->uele_fileheader.uef_size); 891 892 /* 893 * Read in the data header to see if the data is defined, and if so 894 * how much. 895 */ 896 bzero(&ueh, sizeof(struct ufs_extattr_header)); 897 local_aiov.iov_base = (caddr_t) &ueh; 898 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 899 local_aio.uio_iov = &local_aiov; 900 local_aio.uio_iovcnt = 1; 901 local_aio.uio_rw = UIO_READ; 902 local_aio.uio_segflg = UIO_SYSSPACE; 903 local_aio.uio_td = td; 904 local_aio.uio_offset = base_offset; 905 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 906 907 /* 908 * Acquire locks. 909 * 910 * Don't need to get a lock on the backing file if the getattr is 911 * being applied to the backing file, as the lock is already held. 912 */ 913 if (attribute->uele_backing_vnode != vp) 914 vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_RETRY); 915 916 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 917 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 918 if (error) 919 goto vopunlock_exit; 920 921 /* Defined? */ 922 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 923 error = ENOATTR; 924 goto vopunlock_exit; 925 } 926 927 /* Valid for the current inode generation? */ 928 if (ueh.ueh_i_gen != ip->i_gen) { 929 /* 930 * The inode itself has a different generation number 931 * than the attribute data. For now, the best solution 932 * is to coerce this to undefined, and let it get cleaned 933 * up by the next write or extattrctl clean. 934 */ 935 printf("ufs_extattr_get (%s): inode number inconsistency (%d, %jd)\n", 936 mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen); 937 error = ENOATTR; 938 goto vopunlock_exit; 939 } 940 941 /* Local size consistency check. */ 942 if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { 943 error = ENXIO; 944 goto vopunlock_exit; 945 } 946 947 /* Return full data size if caller requested it. */ 948 if (size != NULL) 949 *size = ueh.ueh_len; 950 951 /* Return data if the caller requested it. */ 952 if (uio != NULL) { 953 /* Allow for offset into the attribute data. */ 954 uio->uio_offset = base_offset + sizeof(struct 955 ufs_extattr_header); 956 957 /* 958 * Figure out maximum to transfer -- use buffer size and 959 * local data limit. 960 */ 961 len = MIN(uio->uio_resid, ueh.ueh_len); 962 old_len = uio->uio_resid; 963 uio->uio_resid = len; 964 965 error = VOP_READ(attribute->uele_backing_vnode, uio, 966 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 967 if (error) 968 goto vopunlock_exit; 969 970 uio->uio_resid = old_len - (len - uio->uio_resid); 971 } 972 973 vopunlock_exit: 974 975 if (uio != NULL) 976 uio->uio_offset = 0; 977 978 if (attribute->uele_backing_vnode != vp) 979 VOP_UNLOCK(attribute->uele_backing_vnode, 0); 980 981 return (error); 982 } 983 984 /* 985 * Vnode operation to remove a named attribute. 986 */ 987 int 988 ufs_deleteextattr(struct vop_deleteextattr_args *ap) 989 /* 990 vop_deleteextattr { 991 IN struct vnode *a_vp; 992 IN int a_attrnamespace; 993 IN const char *a_name; 994 IN struct ucred *a_cred; 995 IN struct thread *a_td; 996 }; 997 */ 998 { 999 struct mount *mp = ap->a_vp->v_mount; 1000 struct ufsmount *ump = VFSTOUFS(mp); 1001 int error; 1002 1003 ufs_extattr_uepm_lock(ump, ap->a_td); 1004 1005 error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name, 1006 ap->a_cred, ap->a_td); 1007 1008 1009 ufs_extattr_uepm_unlock(ump, ap->a_td); 1010 1011 return (error); 1012 } 1013 1014 /* 1015 * Vnode operation to set a named attribute. 1016 */ 1017 int 1018 ufs_setextattr(struct vop_setextattr_args *ap) 1019 /* 1020 vop_setextattr { 1021 IN struct vnode *a_vp; 1022 IN int a_attrnamespace; 1023 IN const char *a_name; 1024 INOUT struct uio *a_uio; 1025 IN struct ucred *a_cred; 1026 IN struct thread *a_td; 1027 }; 1028 */ 1029 { 1030 struct mount *mp = ap->a_vp->v_mount; 1031 struct ufsmount *ump = VFSTOUFS(mp); 1032 int error; 1033 1034 ufs_extattr_uepm_lock(ump, ap->a_td); 1035 1036 /* 1037 * XXX: No longer a supported way to delete extended attributes. 1038 */ 1039 if (ap->a_uio == NULL) 1040 return (EINVAL); 1041 1042 error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name, 1043 ap->a_uio, ap->a_cred, ap->a_td); 1044 1045 ufs_extattr_uepm_unlock(ump, ap->a_td); 1046 1047 return (error); 1048 } 1049 1050 /* 1051 * Real work associated with setting a vnode's extended attributes; 1052 * assumes that the attribute lock has already been grabbed. 1053 */ 1054 static int 1055 ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name, 1056 struct uio *uio, struct ucred *cred, struct thread *td) 1057 { 1058 struct ufs_extattr_list_entry *attribute; 1059 struct ufs_extattr_header ueh; 1060 struct iovec local_aiov; 1061 struct uio local_aio; 1062 struct mount *mp = vp->v_mount; 1063 struct ufsmount *ump = VFSTOUFS(mp); 1064 struct inode *ip = VTOI(vp); 1065 off_t base_offset; 1066 int error = 0, ioflag; 1067 1068 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1069 return (EROFS); 1070 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 1071 return (EOPNOTSUPP); 1072 if (!ufs_extattr_valid_attrname(attrnamespace, name)) 1073 return (EINVAL); 1074 1075 error = extattr_check_cred(vp, attrnamespace, cred, td, VWRITE); 1076 if (error) 1077 return (error); 1078 1079 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 1080 if (!attribute) 1081 return (ENOATTR); 1082 1083 /* 1084 * Early rejection of invalid offsets/length. 1085 * Reject: any offset but 0 (replace) 1086 * Any size greater than attribute size limit 1087 */ 1088 if (uio->uio_offset != 0 || 1089 uio->uio_resid > attribute->uele_fileheader.uef_size) 1090 return (ENXIO); 1091 1092 /* 1093 * Find base offset of header in file based on file header size, and 1094 * data header size + maximum data size, indexed by inode number. 1095 */ 1096 base_offset = sizeof(struct ufs_extattr_fileheader) + 1097 ip->i_number * (sizeof(struct ufs_extattr_header) + 1098 attribute->uele_fileheader.uef_size); 1099 1100 /* 1101 * Write out a data header for the data. 1102 */ 1103 ueh.ueh_len = uio->uio_resid; 1104 ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; 1105 ueh.ueh_i_gen = ip->i_gen; 1106 local_aiov.iov_base = (caddr_t) &ueh; 1107 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1108 local_aio.uio_iov = &local_aiov; 1109 local_aio.uio_iovcnt = 1; 1110 local_aio.uio_rw = UIO_WRITE; 1111 local_aio.uio_segflg = UIO_SYSSPACE; 1112 local_aio.uio_td = td; 1113 local_aio.uio_offset = base_offset; 1114 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1115 1116 /* 1117 * Acquire locks. 1118 * 1119 * Don't need to get a lock on the backing file if the setattr is 1120 * being applied to the backing file, as the lock is already held. 1121 */ 1122 if (attribute->uele_backing_vnode != vp) 1123 vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY); 1124 1125 ioflag = IO_NODELOCKED; 1126 if (ufs_extattr_sync) 1127 ioflag |= IO_SYNC; 1128 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, 1129 ump->um_extattr.uepm_ucred); 1130 if (error) 1131 goto vopunlock_exit; 1132 1133 if (local_aio.uio_resid != 0) { 1134 error = ENXIO; 1135 goto vopunlock_exit; 1136 } 1137 1138 /* 1139 * Write out user data. 1140 */ 1141 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 1142 1143 ioflag = IO_NODELOCKED; 1144 if (ufs_extattr_sync) 1145 ioflag |= IO_SYNC; 1146 error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag, 1147 ump->um_extattr.uepm_ucred); 1148 1149 vopunlock_exit: 1150 uio->uio_offset = 0; 1151 1152 if (attribute->uele_backing_vnode != vp) 1153 VOP_UNLOCK(attribute->uele_backing_vnode, 0); 1154 1155 return (error); 1156 } 1157 1158 /* 1159 * Real work associated with removing an extended attribute from a vnode. 1160 * Assumes the attribute lock has already been grabbed. 1161 */ 1162 static int 1163 ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name, 1164 struct ucred *cred, struct thread *td) 1165 { 1166 struct ufs_extattr_list_entry *attribute; 1167 struct ufs_extattr_header ueh; 1168 struct iovec local_aiov; 1169 struct uio local_aio; 1170 struct mount *mp = vp->v_mount; 1171 struct ufsmount *ump = VFSTOUFS(mp); 1172 struct inode *ip = VTOI(vp); 1173 off_t base_offset; 1174 int error = 0, ioflag; 1175 1176 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1177 return (EROFS); 1178 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 1179 return (EOPNOTSUPP); 1180 if (!ufs_extattr_valid_attrname(attrnamespace, name)) 1181 return (EINVAL); 1182 1183 error = extattr_check_cred(vp, attrnamespace, cred, td, VWRITE); 1184 if (error) 1185 return (error); 1186 1187 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 1188 if (!attribute) 1189 return (ENOATTR); 1190 1191 /* 1192 * Find base offset of header in file based on file header size, and 1193 * data header size + maximum data size, indexed by inode number. 1194 */ 1195 base_offset = sizeof(struct ufs_extattr_fileheader) + 1196 ip->i_number * (sizeof(struct ufs_extattr_header) + 1197 attribute->uele_fileheader.uef_size); 1198 1199 /* 1200 * Check to see if currently defined. 1201 */ 1202 bzero(&ueh, sizeof(struct ufs_extattr_header)); 1203 1204 local_aiov.iov_base = (caddr_t) &ueh; 1205 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1206 local_aio.uio_iov = &local_aiov; 1207 local_aio.uio_iovcnt = 1; 1208 local_aio.uio_rw = UIO_READ; 1209 local_aio.uio_segflg = UIO_SYSSPACE; 1210 local_aio.uio_td = td; 1211 local_aio.uio_offset = base_offset; 1212 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1213 1214 /* 1215 * Don't need to get the lock on the backing vnode if the vnode we're 1216 * modifying is it, as we already hold the lock. 1217 */ 1218 if (attribute->uele_backing_vnode != vp) 1219 vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY); 1220 1221 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 1222 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 1223 if (error) 1224 goto vopunlock_exit; 1225 1226 /* Defined? */ 1227 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 1228 error = ENOATTR; 1229 goto vopunlock_exit; 1230 } 1231 1232 /* Valid for the current inode generation? */ 1233 if (ueh.ueh_i_gen != ip->i_gen) { 1234 /* 1235 * The inode itself has a different generation number than 1236 * the attribute data. For now, the best solution is to 1237 * coerce this to undefined, and let it get cleaned up by 1238 * the next write or extattrctl clean. 1239 */ 1240 printf("ufs_extattr_rm (%s): inode number inconsistency (%d, %jd)\n", 1241 mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen); 1242 error = ENOATTR; 1243 goto vopunlock_exit; 1244 } 1245 1246 /* Flag it as not in use. */ 1247 ueh.ueh_flags = 0; 1248 ueh.ueh_len = 0; 1249 1250 local_aiov.iov_base = (caddr_t) &ueh; 1251 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1252 local_aio.uio_iov = &local_aiov; 1253 local_aio.uio_iovcnt = 1; 1254 local_aio.uio_rw = UIO_WRITE; 1255 local_aio.uio_segflg = UIO_SYSSPACE; 1256 local_aio.uio_td = td; 1257 local_aio.uio_offset = base_offset; 1258 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1259 1260 ioflag = IO_NODELOCKED; 1261 if (ufs_extattr_sync) 1262 ioflag |= IO_SYNC; 1263 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, 1264 ump->um_extattr.uepm_ucred); 1265 if (error) 1266 goto vopunlock_exit; 1267 1268 if (local_aio.uio_resid != 0) 1269 error = ENXIO; 1270 1271 vopunlock_exit: 1272 VOP_UNLOCK(attribute->uele_backing_vnode, 0); 1273 1274 return (error); 1275 } 1276 1277 /* 1278 * Called by UFS when an inode is no longer active and should have its 1279 * attributes stripped. 1280 */ 1281 void 1282 ufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td) 1283 { 1284 struct ufs_extattr_list_entry *uele; 1285 struct mount *mp = vp->v_mount; 1286 struct ufsmount *ump = VFSTOUFS(mp); 1287 1288 /* 1289 * In that case, we cannot lock. We should not have any active vnodes 1290 * on the fs if this is not yet initialized but is going to be, so 1291 * this can go unlocked. 1292 */ 1293 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 1294 return; 1295 1296 ufs_extattr_uepm_lock(ump, td); 1297 1298 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 1299 ufs_extattr_uepm_unlock(ump, td); 1300 return; 1301 } 1302 1303 LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) 1304 ufs_extattr_rm(vp, uele->uele_attrnamespace, 1305 uele->uele_attrname, NULL, td); 1306 1307 ufs_extattr_uepm_unlock(ump, td); 1308 } 1309 1310 #endif /* !UFS_EXTATTR */ 1311