1/* $NetBSD: ffs_extattr.c,v 1.10 2022/11/28 04:52:04 chs Exp $ */ 2 3/*- 4 * SPDX-License-Identifier: (BSD-2-Clause-FreeBSD AND BSD-3-Clause) 5 * 6 * Copyright (c) 2002, 2003 Networks Associates Technology, Inc. 7 * All rights reserved. 8 * 9 * This software was developed for the FreeBSD Project by Marshall 10 * Kirk McKusick and Network Associates Laboratories, the Security 11 * Research Division of Network Associates, Inc. under DARPA/SPAWAR 12 * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS 13 * research program 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 1. Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer. 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in the 22 * documentation and/or other materials provided with the distribution. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * Copyright (c) 1982, 1986, 1989, 1993 37 * The Regents of the University of California. All rights reserved. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 3. Neither the name of the University nor the names of its contributors 48 * may be used to endorse or promote products derived from this software 49 * without specific prior written permission. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 * 63 * from: @(#)ufs_readwrite.c 8.11 (Berkeley) 5/8/95 64 * from: $FreeBSD: .../ufs/ufs_readwrite.c,v 1.96 2002/08/12 09:22:11 phk ... 65 * @(#)ffs_vnops.c 8.15 (Berkeley) 5/14/95 66 */ 67 68#include <sys/cdefs.h> 69__KERNEL_RCSID(0, "$NetBSD: ffs_extattr.c,v 1.10 2022/11/28 04:52:04 chs Exp $"); 70 71#if defined(_KERNEL_OPT) 72#include "opt_ffs.h" 73#include "opt_wapbl.h" 74#endif 75 76#include <sys/param.h> 77#include <sys/systm.h> 78#include <sys/resourcevar.h> 79#include <sys/kernel.h> 80#include <sys/file.h> 81#include <sys/stat.h> 82#include <sys/buf.h> 83#include <sys/event.h> 84#include <sys/extattr.h> 85#include <sys/kauth.h> 86#include <sys/proc.h> 87#include <sys/mount.h> 88#include <sys/vnode.h> 89#include <sys/malloc.h> 90#include <sys/pool.h> 91#include <sys/signalvar.h> 92#include <sys/kauth.h> 93#include <sys/wapbl.h> 94 95#include <miscfs/fifofs/fifo.h> 96#include <miscfs/genfs/genfs.h> 97#include <miscfs/specfs/specdev.h> 98 99#include <ufs/ufs/inode.h> 100#include <ufs/ufs/dir.h> 101#include <ufs/ufs/ufs_extern.h> 102#include <ufs/ufs/ufsmount.h> 103#include <ufs/ufs/ufs_wapbl.h> 104 105#include <ufs/ffs/fs.h> 106#include <ufs/ffs/ffs_extern.h> 107 108#define ALIGNED_TO(ptr, s) \ 109 (((uintptr_t)(ptr) & (_Alignof(s) - 1)) == 0) 110#define uoff_t uintmax_t 111#define ITOFS(ip) (ip)->i_fs 112#define i_din2 i_din.ffs2_din 113#define VI_LOCK(vp) mutex_enter((vp)->v_interlock) 114#define VI_UNLOCK(vp) mutex_exit((vp)->v_interlock) 115#define UFS_INODE_SET_FLAG(ip, f) ((ip)->i_flag |= (f)) 116#define ASSERT_VOP_ELOCKED(vp, m) KASSERT(VOP_ISLOCKED(vp)) 117#define I_IS_UFS2(ip) ((ip)->i_ump->um_fstype == UFS2) 118#define lblktosize(fs, o) ffs_lblktosize(fs, o) 119#define lblkno(fs, o) ffs_lblkno(fs, o) 120#define blkoff(fs, o) ffs_blkoff(fs, o) 121#define sblksize(fs, o, lbn) ffs_sblksize(fs, o, lbn) 122typedef daddr_t ufs_lbn_t; 123#define msleep(chan, mtx, pri, wmesg, timeo) \ 124 mtsleep((chan), (pri), (wmesg), (timeo), *(mtx)) 125#define vm_page_count_severe() 0 126#define buf_dirty_count_severe() 0 127#define BA_CLRBUF B_CLRBUF 128#define IO_ASYNC 0 129#define vfs_bio_brelse(bp, ioflag) brelse(bp, 0) 130#define vfs_bio_clrbuf(bp) clrbuf(bp) 131#define vfs_bio_set_flags(bp, ioflag) __nothing 132 133/* 134 * Extended attribute area reading. 135 */ 136static int 137ffs_extread(struct vnode *vp, struct uio *uio, int ioflag) 138{ 139 struct inode *ip; 140 struct ufs2_dinode *dp; 141 struct fs *fs; 142 struct buf *bp; 143 ufs_lbn_t lbn, nextlbn; 144 off_t bytesinfile; 145 long size, xfersize, blkoffset; 146 ssize_t orig_resid; 147 int error; 148 149 ip = VTOI(vp); 150 fs = ITOFS(ip); 151 dp = ip->i_din2; 152 153#ifdef INVARIANTS 154 if (uio->uio_rw != UIO_READ || ip->i_ump->um_fstype != UFS2) 155 panic("ffs_extread: mode"); 156 157#endif 158 orig_resid = uio->uio_resid; 159 KASSERT(orig_resid >= 0); 160 if (orig_resid == 0) 161 return (0); 162 KASSERT(uio->uio_offset >= 0); 163 164 for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) { 165 if ((bytesinfile = dp->di_extsize - uio->uio_offset) <= 0) 166 break; 167 lbn = lblkno(fs, uio->uio_offset); 168 nextlbn = lbn + 1; 169 170 /* 171 * size of buffer. The buffer representing the 172 * end of the file is rounded up to the size of 173 * the block type ( fragment or full block, 174 * depending ). 175 */ 176 size = sblksize(fs, dp->di_extsize, lbn); 177 blkoffset = blkoff(fs, uio->uio_offset); 178 179 /* 180 * The amount we want to transfer in this iteration is 181 * one FS block less the amount of the data before 182 * our startpoint (duh!) 183 */ 184 xfersize = fs->fs_bsize - blkoffset; 185 186 /* 187 * But if we actually want less than the block, 188 * or the file doesn't have a whole block more of data, 189 * then use the lesser number. 190 */ 191 if (uio->uio_resid < xfersize) 192 xfersize = uio->uio_resid; 193 if (bytesinfile < xfersize) 194 xfersize = bytesinfile; 195 196 if (lblktosize(fs, nextlbn) >= dp->di_extsize) { 197 /* 198 * Don't do readahead if this is the end of the info. 199 */ 200 error = bread(vp, -1 - lbn, size, 0, &bp); 201 } else { 202 /* 203 * If we have a second block, then 204 * fire off a request for a readahead 205 * as well as a read. Note that the 4th and 5th 206 * arguments point to arrays of the size specified in 207 * the 6th argument. 208 */ 209 u_int nextsize = sblksize(fs, dp->di_extsize, nextlbn); 210 211 nextlbn = -1 - nextlbn; 212 error = breadn(vp, -1 - lbn, 213 size, &nextlbn, &nextsize, 1, 0, &bp); 214 } 215 if (error) { 216 brelse(bp, 0); 217 bp = NULL; 218 break; 219 } 220 221 /* 222 * We should only get non-zero b_resid when an I/O error 223 * has occurred, which should cause us to break above. 224 * However, if the short read did not cause an error, 225 * then we want to ensure that we do not uiomove bad 226 * or uninitialized data. 227 */ 228 size -= bp->b_resid; 229 if (size < xfersize) { 230 if (size == 0) 231 break; 232 xfersize = size; 233 } 234 235 error = uiomove((char *)bp->b_data + blkoffset, 236 (int)xfersize, uio); 237 if (error) 238 break; 239 vfs_bio_brelse(bp, ioflag); 240 } 241 242 /* 243 * This can only happen in the case of an error 244 * because the loop above resets bp to NULL on each iteration 245 * and on normal completion has not set a new value into it. 246 * so it must have come from a 'break' statement 247 */ 248 if (bp != NULL) 249 vfs_bio_brelse(bp, ioflag); 250 return (error); 251} 252/* 253 * Extended attribute area writing. 254 */ 255static int 256ffs_extwrite(struct vnode *vp, struct uio *uio, int ioflag, kauth_cred_t ucred) 257{ 258 struct inode *ip; 259 struct ufs2_dinode *dp; 260 struct fs *fs; 261 struct buf *bp; 262 ufs_lbn_t lbn; 263 off_t osize; 264 ssize_t resid; 265 int blkoffset, error, flags, size, xfersize; 266 267 ip = VTOI(vp); 268 fs = ITOFS(ip); 269 dp = ip->i_din2; 270 271#ifdef INVARIANTS 272 if (uio->uio_rw != UIO_WRITE || ip->i_ump->um_fstype != UFS2) 273 panic("ffs_extwrite: mode"); 274#endif 275 276 if (ioflag & IO_APPEND) 277 uio->uio_offset = dp->di_extsize; 278 KASSERT(uio->uio_offset >= 0); 279 if ((uoff_t)uio->uio_offset + uio->uio_resid > 280 UFS_NXADDR * fs->fs_bsize) 281 return (EFBIG); 282 283 resid = uio->uio_resid; 284 osize = dp->di_extsize; 285 flags = IO_EXT; 286 if (ioflag & IO_SYNC) 287 flags |= IO_SYNC; 288 289 if ((error = UFS_WAPBL_BEGIN(vp->v_mount)) != 0) 290 return error; 291 292 for (error = 0; uio->uio_resid > 0;) { 293 lbn = lblkno(fs, uio->uio_offset); 294 blkoffset = blkoff(fs, uio->uio_offset); 295 xfersize = fs->fs_bsize - blkoffset; 296 if (uio->uio_resid < xfersize) 297 xfersize = uio->uio_resid; 298 299 /* 300 * We must perform a read-before-write if the transfer size 301 * does not cover the entire buffer. 302 */ 303 if (fs->fs_bsize > xfersize) 304 flags |= BA_CLRBUF; 305 else 306 flags &= ~BA_CLRBUF; 307 error = UFS_BALLOC(vp, uio->uio_offset, xfersize, 308 ucred, flags, &bp); 309 if (error != 0) 310 break; 311 /* 312 * If the buffer is not valid we have to clear out any 313 * garbage data from the pages instantiated for the buffer. 314 * If we do not, a failed uiomove() during a write can leave 315 * the prior contents of the pages exposed to a userland 316 * mmap(). XXX deal with uiomove() errors a better way. 317 */ 318 if ((bp->b_flags & BC_NOCACHE) && fs->fs_bsize <= xfersize) 319 vfs_bio_clrbuf(bp); 320 321 if (uio->uio_offset + xfersize > dp->di_extsize) 322 dp->di_extsize = uio->uio_offset + xfersize; 323 324 size = sblksize(fs, dp->di_extsize, lbn) - bp->b_resid; 325 if (size < xfersize) 326 xfersize = size; 327 328 error = 329 uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio); 330 331 vfs_bio_set_flags(bp, ioflag); 332 333 /* 334 * If IO_SYNC each buffer is written synchronously. Otherwise 335 * if we have a severe page deficiency write the buffer 336 * asynchronously. Otherwise try to cluster, and if that 337 * doesn't do it then either do an async write (if O_DIRECT), 338 * or a delayed write (if not). 339 */ 340 if (ioflag & IO_SYNC) { 341 (void)bwrite(bp); 342 } else if (vm_page_count_severe() || 343 buf_dirty_count_severe() || 344 xfersize + blkoffset == fs->fs_bsize || 345 (ioflag & (IO_ASYNC | IO_DIRECT))) 346 bawrite(bp); 347 else 348 bdwrite(bp); 349 if (error || xfersize == 0) 350 break; 351 UFS_INODE_SET_FLAG(ip, IN_CHANGE); 352 } 353 /* 354 * If we successfully wrote any data, and we are not the superuser 355 * we clear the setuid and setgid bits as a precaution against 356 * tampering. 357 */ 358 if ((ip->i_mode & (ISUID | ISGID)) && resid > uio->uio_resid && ucred) { 359 ip->i_mode &= ~(ISUID | ISGID); 360 dp->di_mode = ip->i_mode; 361 } 362 if (error) { 363 if (ioflag & IO_UNIT) { 364 (void)ffs_truncate(vp, osize, 365 IO_EXT | (ioflag&IO_SYNC), ucred); 366 uio->uio_offset -= resid - uio->uio_resid; 367 uio->uio_resid = resid; 368 } 369 } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) 370 error = ffs_update(vp, NULL, NULL, UPDATE_WAIT); 371 UFS_WAPBL_END(vp->v_mount); 372 return (error); 373} 374 375/* 376 * Vnode operating to retrieve a named extended attribute. 377 * 378 * Locate a particular EA (nspace:name) in the area (ptr:length), and return 379 * the length of the EA, and possibly the pointer to the entry and to the data. 380 */ 381static int 382ffs_findextattr(u_char *ptr, u_int length, int nspace, const char *name, 383 struct extattr **eapp, u_char **eac) 384{ 385 struct extattr *eap, *eaend; 386 size_t nlen; 387 388 nlen = strlen(name); 389 KASSERT(ALIGNED_TO(ptr, struct extattr)); 390 eap = (struct extattr *)ptr; 391 eaend = (struct extattr *)(ptr + length); 392 for (; eap < eaend; eap = EXTATTR_NEXT(eap)) { 393 /* make sure this entry is complete */ 394 if (EXTATTR_NEXT(eap) > eaend) 395 break; 396 if (eap->ea_namespace != nspace || eap->ea_namelength != nlen 397 || memcmp(eap->ea_name, name, nlen) != 0) 398 continue; 399 if (eapp != NULL) 400 *eapp = eap; 401 if (eac != NULL) 402 *eac = EXTATTR_CONTENT(eap); 403 return (EXTATTR_CONTENT_SIZE(eap)); 404 } 405 return (-1); 406} 407 408static int 409ffs_rdextattr(u_char **p, struct vnode *vp, int extra) 410{ 411 struct inode *ip; 412 struct ufs2_dinode *dp; 413 struct fs *fs; 414 struct uio luio; 415 struct iovec liovec; 416 u_int easize; 417 int error; 418 u_char *eae; 419 420 ip = VTOI(vp); 421 fs = ITOFS(ip); 422 dp = ip->i_din2; 423 easize = dp->di_extsize; 424 if ((uoff_t)easize + extra > UFS_NXADDR * fs->fs_bsize) 425 return (EFBIG); 426 427 eae = malloc(easize + extra, M_TEMP, M_WAITOK); 428 429 liovec.iov_base = eae; 430 liovec.iov_len = easize; 431 luio.uio_iov = &liovec; 432 luio.uio_iovcnt = 1; 433 luio.uio_offset = 0; 434 luio.uio_resid = easize; 435 luio.uio_vmspace = vmspace_kernel(); 436 luio.uio_rw = UIO_READ; 437 438 error = ffs_extread(vp, &luio, IO_EXT | IO_SYNC); 439 if (error) { 440 free(eae, M_TEMP); 441 return(error); 442 } 443 *p = eae; 444 return (0); 445} 446 447static void 448ffs_lock_ea(struct vnode *vp) 449{ 450 genfs_node_wrlock(vp); 451} 452 453static void 454ffs_unlock_ea(struct vnode *vp) 455{ 456 genfs_node_unlock(vp); 457} 458 459static int 460ffs_open_ea(struct vnode *vp, kauth_cred_t cred) 461{ 462 struct inode *ip; 463 struct ufs2_dinode *dp; 464 int error; 465 466 ip = VTOI(vp); 467 if ((ip->i_ump->um_flags & UFS_EA) == 0) { 468 return EOPNOTSUPP; 469 } 470 471 ffs_lock_ea(vp); 472 if (ip->i_ea_area != NULL) { 473 ip->i_ea_refs++; 474 ffs_unlock_ea(vp); 475 return (0); 476 } 477 dp = ip->i_din2; 478 error = ffs_rdextattr(&ip->i_ea_area, vp, 0); 479 if (error) { 480 ffs_unlock_ea(vp); 481 return (error); 482 } 483 ip->i_ea_len = dp->di_extsize; 484 ip->i_ea_error = 0; 485 ip->i_ea_refs++; 486 ffs_unlock_ea(vp); 487 return (0); 488} 489 490/* 491 * Vnode extattr transaction commit/abort 492 */ 493static int 494ffs_close_ea(struct vnode *vp, int commit, kauth_cred_t cred) 495{ 496 struct inode *ip; 497 struct uio luio; 498 struct iovec liovec; 499 int error; 500 struct ufs2_dinode *dp; 501 502 ip = VTOI(vp); 503 KASSERT((ip->i_ump->um_flags & UFS_EA) != 0); 504 505 if (commit) 506 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 507 else 508 KASSERT(VOP_ISLOCKED(vp)); 509 ffs_lock_ea(vp); 510 if (ip->i_ea_area == NULL) { 511 ffs_unlock_ea(vp); 512 return (EINVAL); 513 } 514 dp = ip->i_din2; 515 error = ip->i_ea_error; 516 if (commit && error == 0) { 517 ASSERT_VOP_ELOCKED(vp, "ffs_close_ea commit"); 518 if (cred == NOCRED) 519 cred = lwp0.l_cred; 520 liovec.iov_base = ip->i_ea_area; 521 liovec.iov_len = ip->i_ea_len; 522 luio.uio_iov = &liovec; 523 luio.uio_iovcnt = 1; 524 luio.uio_offset = 0; 525 luio.uio_resid = ip->i_ea_len; 526 luio.uio_vmspace = vmspace_kernel(); 527 luio.uio_rw = UIO_WRITE; 528 529 /* XXX: I'm not happy about truncating to zero size */ 530 if (ip->i_ea_len < dp->di_extsize) { 531 if ((error = UFS_WAPBL_BEGIN(vp->v_mount)) != 0) { 532 ffs_unlock_ea(vp); 533 return error; 534 } 535 error = ffs_truncate(vp, 0, IO_EXT, cred); 536 UFS_WAPBL_END(vp->v_mount); 537 } 538 error = ffs_extwrite(vp, &luio, IO_EXT | IO_SYNC, cred); 539 } 540 if (--ip->i_ea_refs == 0) { 541 free(ip->i_ea_area, M_TEMP); 542 ip->i_ea_area = NULL; 543 ip->i_ea_len = 0; 544 ip->i_ea_error = 0; 545 } 546 ffs_unlock_ea(vp); 547 return (error); 548} 549 550/* 551 * Vnode extattr strategy routine for fifos. 552 * 553 * We need to check for a read or write of the external attributes. 554 * Otherwise we just fall through and do the usual thing. 555 */ 556int 557ffsext_strategy(void *v) 558{ 559 struct vop_strategy_args /* { 560 struct vnodeop_desc *a_desc; 561 struct vnode *a_vp; 562 struct buf *a_bp; 563 } */ *ap = v; 564 struct vnode *vp; 565 daddr_t lbn; 566 567 vp = ap->a_vp; 568 lbn = ap->a_bp->b_lblkno; 569 if (I_IS_UFS2(VTOI(vp)) && lbn < 0 && lbn >= -UFS_NXADDR) 570 return ufs_strategy(ap); 571 if (vp->v_type == VFIFO) 572 return vn_fifo_bypass(ap); 573 panic("spec nodes went here"); 574} 575 576/* 577 * Vnode extattr transaction commit/abort 578 */ 579int 580ffs_openextattr(void *v) 581{ 582 struct vop_openextattr_args /* { 583 struct vnode *a_vp; 584 kauth_cred_t a_cred; 585 struct proc *a_p; 586 } */ *ap = v; 587 struct inode *ip = VTOI(ap->a_vp); 588 589 /* Not supported for UFS1 file systems. */ 590 if (ip->i_ump->um_fstype == UFS1) 591 return (EOPNOTSUPP); 592 593#ifdef __FreeBSD__ 594 if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) 595 return (EOPNOTSUPP); 596#endif 597 598 return (ffs_open_ea(ap->a_vp, ap->a_cred)); 599} 600 601/* 602 * Vnode extattr transaction commit/abort 603 */ 604int 605ffs_closeextattr(void *v) 606{ 607 struct vop_closeextattr_args /* { 608 struct vnode *a_vp; 609 int a_commit; 610 kauth_cred_t a_cred; 611 struct proc *a_p; 612 } */ *ap = v; 613 struct inode *ip = VTOI(ap->a_vp); 614 615 /* Not supported for UFS1 file systems. */ 616 if (ip->i_ump->um_fstype == UFS1) 617 return (EOPNOTSUPP); 618 619#ifdef __FreeBSD__ 620 if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) 621 return (EOPNOTSUPP); 622#endif 623 624 if (ap->a_commit && (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)) 625 return (EROFS); 626 627 return (ffs_close_ea(ap->a_vp, ap->a_commit, ap->a_cred)); 628} 629 630/* 631 * Vnode operation to retrieve a named extended attribute. 632 */ 633int 634ffs_getextattr(void *v) 635{ 636 struct vop_getextattr_args /* { 637 struct vnode *a_vp; 638 int a_attrnamespace; 639 const char *a_name; 640 struct uio *a_uio; 641 size_t *a_size; 642 kauth_cred_t a_cred; 643 struct proc *a_p; 644 } */ *ap = v; 645 struct vnode *vp = ap->a_vp; 646 struct inode *ip = VTOI(vp); 647 648 KASSERT(VOP_ISLOCKED(vp)); 649 650 if (ip->i_ump->um_fstype == UFS1) { 651#ifdef UFS_EXTATTR 652 return ufs_getextattr(ap); 653#else 654 return EOPNOTSUPP; 655#endif 656 } 657 658 u_char *eae, *p; 659 unsigned easize; 660 int error, ealen; 661 662#ifdef __FreeBSD__ 663 if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) 664 return (EOPNOTSUPP); 665#endif 666 667 error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, 668 ap->a_cred, VREAD); 669 if (error) 670 return (error); 671 672 error = ffs_open_ea(ap->a_vp, ap->a_cred); 673 if (error) 674 return (error); 675 676 eae = ip->i_ea_area; 677 easize = ip->i_ea_len; 678 679 ealen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name, 680 NULL, &p); 681 if (ealen >= 0) { 682 error = 0; 683 if (ap->a_size != NULL) 684 *ap->a_size = ealen; 685 else if (ap->a_uio != NULL) 686 error = uiomove(p, ealen, ap->a_uio); 687 } else 688 error = ENOATTR; 689 690 ffs_close_ea(ap->a_vp, 0, ap->a_cred); 691 return (error); 692} 693 694/* 695 * Vnode operation to set a named attribute. 696 */ 697int 698ffs_setextattr(void *v) 699{ 700 struct vop_setextattr_args /* { 701 struct vnode *a_vp; 702 int a_attrnamespace; 703 const char *a_name; 704 struct uio *a_uio; 705 kauth_cred_t a_cred; 706 struct proc *a_p; 707 } */ *ap = v; 708 struct vnode *vp = ap->a_vp; 709 struct inode *ip = VTOI(vp); 710 struct fs *fs = ip->i_fs; 711 712 KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE); 713 if (ip->i_ump->um_fstype == UFS1) { 714#ifdef UFS_EXTATTR 715 return ufs_setextattr(ap); 716#else 717 return EOPNOTSUPP; 718#endif 719 } 720 721 struct extattr *eap; 722 uint32_t ealength, ul; 723 ssize_t ealen; 724 int olen, eapad1, eapad2, error, i, easize; 725 u_char *eae; 726 void *tmp; 727 728 if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) 729 return (EOPNOTSUPP); 730 731 if (strlen(ap->a_name) == 0) 732 return (EINVAL); 733 734 /* XXX Now unsupported API to delete EAs using NULL uio. */ 735 if (ap->a_uio == NULL) 736 return (EOPNOTSUPP); 737 738 if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) 739 return (EROFS); 740 741 ealen = ap->a_uio->uio_resid; 742 if (ealen < 0 || ealen > lblktosize(fs, UFS_NXADDR)) 743 return (EINVAL); 744 745 error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, 746 ap->a_cred, VWRITE); 747 if (error) { 748 749 /* 750 * ffs_lock_ea is not needed there, because the vnode 751 * must be exclusively locked. 752 */ 753 if (ip->i_ea_area != NULL && ip->i_ea_error == 0) 754 ip->i_ea_error = error; 755 return (error); 756 } 757 758 error = ffs_open_ea(ap->a_vp, ap->a_cred); 759 if (error) 760 return (error); 761 762 ealength = sizeof(uint32_t) + 3 + strlen(ap->a_name); 763 eapad1 = roundup2(ealength, 8) - ealength; 764 eapad2 = roundup2(ealen, 8) - ealen; 765 ealength += eapad1 + ealen + eapad2; 766 767 /* 768 * CEM: rewrites of the same size or smaller could be done in-place 769 * instead. (We don't acquire any fine-grained locks in here either, 770 * so we could also do bigger writes in-place.) 771 */ 772 eae = malloc(ip->i_ea_len + ealength, M_TEMP, M_WAITOK); 773 bcopy(ip->i_ea_area, eae, ip->i_ea_len); 774 easize = ip->i_ea_len; 775 776 olen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name, 777 &eap, NULL); 778 if (olen == -1) { 779 /* new, append at end */ 780 KASSERT(ALIGNED_TO(eae + easize, struct extattr)); 781 eap = (struct extattr *)(eae + easize); 782 easize += ealength; 783 } else { 784 ul = eap->ea_length; 785 i = (u_char *)EXTATTR_NEXT(eap) - eae; 786 if (ul != ealength) { 787 bcopy(EXTATTR_NEXT(eap), (u_char *)eap + ealength, 788 easize - i); 789 easize += (ealength - ul); 790 } 791 } 792 if (easize > lblktosize(fs, UFS_NXADDR)) { 793 free(eae, M_TEMP); 794 ffs_close_ea(ap->a_vp, 0, ap->a_cred); 795 if (ip->i_ea_area != NULL && ip->i_ea_error == 0) 796 ip->i_ea_error = ENOSPC; 797 return (ENOSPC); 798 } 799 eap->ea_length = ealength; 800 eap->ea_namespace = ap->a_attrnamespace; 801 eap->ea_contentpadlen = eapad2; 802 eap->ea_namelength = strlen(ap->a_name); 803 memcpy(eap->ea_name, ap->a_name, strlen(ap->a_name)); 804 bzero(&eap->ea_name[strlen(ap->a_name)], eapad1); 805 error = uiomove(EXTATTR_CONTENT(eap), ealen, ap->a_uio); 806 if (error) { 807 free(eae, M_TEMP); 808 ffs_close_ea(ap->a_vp, 0, ap->a_cred); 809 if (ip->i_ea_area != NULL && ip->i_ea_error == 0) 810 ip->i_ea_error = error; 811 return (error); 812 } 813 bzero((u_char *)EXTATTR_CONTENT(eap) + ealen, eapad2); 814 815 tmp = ip->i_ea_area; 816 ip->i_ea_area = eae; 817 ip->i_ea_len = easize; 818 free(tmp, M_TEMP); 819 error = ffs_close_ea(ap->a_vp, 1, ap->a_cred); 820 return (error); 821} 822 823/* 824 * Vnode operation to retrieve extended attributes on a vnode. 825 */ 826int 827ffs_listextattr(void *v) 828{ 829 struct vop_listextattr_args /* { 830 struct vnode *a_vp; 831 int a_attrnamespace; 832 struct uio *a_uio; 833 size_t *a_size; 834 kauth_cred_t a_cred; 835 struct proc *a_p; 836 } */ *ap = v; 837 struct inode *ip = VTOI(ap->a_vp); 838 839 if (ip->i_ump->um_fstype == UFS1) { 840#ifdef UFS_EXTATTR 841 return ufs_listextattr(ap); 842#else 843 return EOPNOTSUPP; 844#endif 845 } 846 847 struct extattr *eap, *eaend; 848 int error, ealen; 849 850 if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) 851 return (EOPNOTSUPP); 852 853 error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, 854 ap->a_cred, VREAD); 855 if (error) 856 return (error); 857 858 error = ffs_open_ea(ap->a_vp, ap->a_cred); 859 if (error) 860 return (error); 861 862 error = 0; 863 if (ap->a_size != NULL) 864 *ap->a_size = 0; 865 866 KASSERT(ALIGNED_TO(ip->i_ea_area, struct extattr)); 867 eap = (struct extattr *)ip->i_ea_area; 868 eaend = (struct extattr *)(ip->i_ea_area + ip->i_ea_len); 869 for (; error == 0 && eap < eaend; eap = EXTATTR_NEXT(eap)) { 870 /* make sure this entry is complete */ 871 if (EXTATTR_NEXT(eap) > eaend) 872 break; 873 if (eap->ea_namespace != ap->a_attrnamespace) 874 continue; 875 876 ealen = eap->ea_namelength; 877 if (ap->a_size != NULL) 878 *ap->a_size += ealen + 1; 879 else if (ap->a_uio != NULL) 880 error = uiomove(&eap->ea_namelength, ealen + 1, 881 ap->a_uio); 882 } 883 884 ffs_close_ea(ap->a_vp, 0, ap->a_cred); 885 return (error); 886} 887 888/* 889 * Vnode operation to remove a named attribute. 890 */ 891int 892ffs_deleteextattr(void *v) 893{ 894 struct vop_deleteextattr_args /* { 895 struct vnode *a_vp; 896 int a_attrnamespace; 897 kauth_cred_t a_cred; 898 struct proc *a_p; 899 } */ *ap = v; 900 struct vnode *vp = ap->a_vp; 901 struct inode *ip = VTOI(vp); 902 903 if (ip->i_ump->um_fstype == UFS1) { 904#ifdef UFS_EXTATTR 905 return ufs_deleteextattr(ap); 906#else 907 return EOPNOTSUPP; 908#endif 909 } 910 911 struct extattr *eap; 912 uint32_t ul; 913 int olen, error, i, easize; 914 u_char *eae; 915 void *tmp; 916 917#ifdef __FreeBSD__ 918 if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK) 919 return (EOPNOTSUPP); 920#endif 921 922 if (strlen(ap->a_name) == 0) 923 return (EINVAL); 924 925 if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) 926 return (EROFS); 927 928 error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, 929 ap->a_cred, VWRITE); 930 if (error) { 931 /* 932 * ffs_lock_ea is not needed there, because the vnode 933 * must be exclusively locked. 934 */ 935 if (ip->i_ea_area != NULL && ip->i_ea_error == 0) 936 ip->i_ea_error = error; 937 return (error); 938 } 939 940 error = ffs_open_ea(ap->a_vp, ap->a_cred); 941 if (error) 942 return (error); 943 944 /* CEM: delete could be done in-place instead */ 945 eae = malloc(ip->i_ea_len, M_TEMP, M_WAITOK); 946 bcopy(ip->i_ea_area, eae, ip->i_ea_len); 947 easize = ip->i_ea_len; 948 949 olen = ffs_findextattr(eae, easize, ap->a_attrnamespace, ap->a_name, 950 &eap, NULL); 951 if (olen == -1) { 952 /* delete but nonexistent */ 953 free(eae, M_TEMP); 954 ffs_close_ea(ap->a_vp, 0, ap->a_cred); 955 return (ENOATTR); 956 } 957 ul = eap->ea_length; 958 i = (u_char *)EXTATTR_NEXT(eap) - eae; 959 bcopy(EXTATTR_NEXT(eap), eap, easize - i); 960 easize -= ul; 961 962 tmp = ip->i_ea_area; 963 ip->i_ea_area = eae; 964 ip->i_ea_len = easize; 965 free(tmp, M_TEMP); 966 error = ffs_close_ea(ap->a_vp, 1, ap->a_cred); 967 return error; 968} 969