static int _xfs_deleteextattr(struct vop_deleteextattr_args *ap) /* vop_deleteextattr { IN struct vnode *a_vp; IN int a_attrnamespace; IN const char *a_name; IN struct ucred *a_cred; IN struct thread *a_td; }; */ { int error, xfs_flags; if (ap->a_vp->v_type == VCHR) return (EOPNOTSUPP); if (ap->a_name[0] == '\0') return (EINVAL); error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_td, VWRITE); if (error) return (error); xfs_flags = 0; if (ap->a_attrnamespace & EXTATTR_NAMESPACE_USER) xfs_flags |= ATTR_KERNORMALS; if (ap->a_attrnamespace & EXTATTR_NAMESPACE_SYSTEM) xfs_flags |= ATTR_KERNROOTLS; XVOP_ATTR_REMOVE(VPTOXFSVP(ap->a_vp), ap->a_name, xfs_flags, ap->a_cred, error); return (error); }
/* * Vnode operation to retrieve a named extended attribute. */ int ffs_ea_get(void *v) { struct vop_getextattr_args *ap = v; struct inode *ip; struct fs *fs; unsigned char *p; int error, ealen; ip = VTOI(ap->a_vp); fs = ip->i_fs; /* * Validate the vnode. It has to be a FFS2 file, directory, or symlink. */ if (ip->i_fs->fs_magic != FS_UFS2_MAGIC) return (EOPNOTSUPP); if (ap->a_vp->v_type != VREG && ap->a_vp->v_type != VDIR && ap->a_vp->v_type != VLNK) return (EOPNOTSUPP); /* * Validate credentials, read in the inode's extended attributes area, * and make sure there is no attribute with the same name. */ error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_p, IREAD); if (error) return (error); error = ffs_ea_iget(ap->a_vp, ap->a_cred, ap->a_p); if (error) return (error); /* Call ffs_ea_find() to do the real job. */ ealen = ffs_ea_find(ip, ap->a_attrnamespace, ap->a_name, NULL, &p); if (ealen >= 0) { error = 0; if (ap->a_size != NULL) *ap->a_size = ealen; else if (ap->a_uio != NULL) error = uiomove(p, ealen, ap->a_uio); } else error = ENOATTR; (void) ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); return (error); }
static int _xfs_setextattr(struct vop_setextattr_args *ap) /* vop_setextattr { IN struct vnode *a_vp; IN int a_attrnamespace; IN const char *a_name; INOUT struct uio *a_uio; IN struct ucred *a_cred; IN struct thread *a_td; }; */ { char *val; size_t vallen; int error, xfs_flags; if (ap->a_vp->v_type == VCHR) return (EOPNOTSUPP); if (ap->a_uio == NULL) return (EINVAL); vallen = ap->a_uio->uio_resid; if (vallen > ATTR_MAX_VALUELEN) return (EOVERFLOW); if (ap->a_name[0] == '\0') return (EINVAL); error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_td, VWRITE); if (error) return (error); xfs_flags = 0; if (ap->a_attrnamespace & EXTATTR_NAMESPACE_USER) xfs_flags |= ATTR_KERNORMALS; if (ap->a_attrnamespace & EXTATTR_NAMESPACE_SYSTEM) xfs_flags |= ATTR_KERNROOTLS; val = (char *)kmem_zalloc(vallen, KM_SLEEP); if (val == NULL) return (ENOMEM); error = uiomove(val, (int)vallen, ap->a_uio); if (error) goto err_out; XVOP_ATTR_SET(VPTOXFSVP(ap->a_vp), ap->a_name, val, vallen, xfs_flags, ap->a_cred, error); err_out: kmem_free(val, vallen); return(error); }
static int _xfs_getextattr( struct vop_getextattr_args /* { struct vnode *a_vp; int a_attrnamespace; const char *a_name; struct uio *a_uio; size_t *a_size; struct ucred *a_cred; struct thread *a_td; } */ *ap) { int error; char *value; int size; error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_td, VREAD); if (error) return (error); size = ATTR_MAX_VALUELEN; value = (char *)kmem_zalloc(size, KM_SLEEP); if (value == NULL) return (ENOMEM); XVOP_ATTR_GET(VPTOXFSVP(ap->a_vp), ap->a_name, value, &size, 1, ap->a_cred, error); if (ap->a_uio != NULL) { if (ap->a_uio->uio_iov->iov_len < size) error = ERANGE; else uiomove(value, size, ap->a_uio); } if (ap->a_size != NULL) *ap->a_size = size; kmem_free(value, ATTR_MAX_VALUELEN); return (error); }
/* * Real work associated with retrieving a named attribute--assumes that * the attribute lock has already been grabbed. */ static int ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name, struct uio *uio, size_t *size, struct ucred *cred, struct thread *td) { struct ufs_extattr_list_entry *attribute; struct ufs_extattr_header ueh; struct iovec local_aiov; struct uio local_aio; struct mount *mp = vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); struct inode *ip = VTOI(vp); off_t base_offset; size_t len, old_len; int error = 0; if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) return (EOPNOTSUPP); if (strlen(name) == 0) return (EINVAL); error = extattr_check_cred(vp, attrnamespace, cred, td, VREAD); if (error) return (error); attribute = ufs_extattr_find_attr(ump, attrnamespace, name); if (!attribute) return (ENOATTR); /* * Allow only offsets of zero to encourage the read/replace * extended attribute semantic. Otherwise we can't guarantee * atomicity, as we don't provide locks for extended attributes. */ if (uio != NULL && uio->uio_offset != 0) return (ENXIO); /* * Find base offset of header in file based on file header size, and * data header size + maximum data size, indexed by inode number. */ base_offset = sizeof(struct ufs_extattr_fileheader) + ip->i_number * (sizeof(struct ufs_extattr_header) + attribute->uele_fileheader.uef_size); /* * Read in the data header to see if the data is defined, and if so * how much. */ bzero(&ueh, sizeof(struct ufs_extattr_header)); local_aiov.iov_base = (caddr_t) &ueh; local_aiov.iov_len = sizeof(struct ufs_extattr_header); local_aio.uio_iov = &local_aiov; local_aio.uio_iovcnt = 1; local_aio.uio_rw = UIO_READ; local_aio.uio_segflg = UIO_SYSSPACE; local_aio.uio_td = td; local_aio.uio_offset = base_offset; local_aio.uio_resid = sizeof(struct ufs_extattr_header); /* * Acquire locks. * * Don't need to get a lock on the backing file if the getattr is * being applied to the backing file, as the lock is already held. */ if (attribute->uele_backing_vnode != vp) vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_RETRY); error = VOP_READ(attribute->uele_backing_vnode, &local_aio, IO_NODELOCKED, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; /* Defined? */ if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { error = ENOATTR; goto vopunlock_exit; } /* Valid for the current inode generation? */ if (ueh.ueh_i_gen != ip->i_gen) { /* * The inode itself has a different generation number * than the attribute data. For now, the best solution * is to coerce this to undefined, and let it get cleaned * up by the next write or extattrctl clean. */ printf("ufs_extattr_get (%s): inode number inconsistency (%d, %ju)\n", mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (uintmax_t)ip->i_gen); error = ENOATTR; goto vopunlock_exit; } /* Local size consistency check. */ if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { error = ENXIO; goto vopunlock_exit; } /* Return full data size if caller requested it. */ if (size != NULL) *size = ueh.ueh_len; /* Return data if the caller requested it. */ if (uio != NULL) { /* Allow for offset into the attribute data. */ uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); /* * Figure out maximum to transfer -- use buffer size and * local data limit. */ len = MIN(uio->uio_resid, ueh.ueh_len); old_len = uio->uio_resid; uio->uio_resid = len; error = VOP_READ(attribute->uele_backing_vnode, uio, IO_NODELOCKED, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; uio->uio_resid = old_len - (len - uio->uio_resid); } vopunlock_exit: if (uio != NULL) uio->uio_offset = 0; if (attribute->uele_backing_vnode != vp) VOP_UNLOCK(attribute->uele_backing_vnode, 0); return (error); }
/* * Real work associated with removing an extended attribute from a vnode. * Assumes the attribute lock has already been grabbed. */ static int ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name, struct ucred *cred, struct thread *td) { struct ufs_extattr_list_entry *attribute; struct ufs_extattr_header ueh; struct iovec local_aiov; struct uio local_aio; struct mount *mp = vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); struct inode *ip = VTOI(vp); off_t base_offset; int error = 0, ioflag; if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) return (EOPNOTSUPP); if (!ufs_extattr_valid_attrname(attrnamespace, name)) return (EINVAL); error = extattr_check_cred(vp, attrnamespace, cred, td, VWRITE); if (error) return (error); attribute = ufs_extattr_find_attr(ump, attrnamespace, name); if (!attribute) return (ENOATTR); /* * Find base offset of header in file based on file header size, and * data header size + maximum data size, indexed by inode number. */ base_offset = sizeof(struct ufs_extattr_fileheader) + ip->i_number * (sizeof(struct ufs_extattr_header) + attribute->uele_fileheader.uef_size); /* * Check to see if currently defined. */ bzero(&ueh, sizeof(struct ufs_extattr_header)); local_aiov.iov_base = (caddr_t) &ueh; local_aiov.iov_len = sizeof(struct ufs_extattr_header); local_aio.uio_iov = &local_aiov; local_aio.uio_iovcnt = 1; local_aio.uio_rw = UIO_READ; local_aio.uio_segflg = UIO_SYSSPACE; local_aio.uio_td = td; local_aio.uio_offset = base_offset; local_aio.uio_resid = sizeof(struct ufs_extattr_header); /* * Don't need to get the lock on the backing vnode if the vnode we're * modifying is it, as we already hold the lock. */ if (attribute->uele_backing_vnode != vp) vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY); error = VOP_READ(attribute->uele_backing_vnode, &local_aio, IO_NODELOCKED, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; /* Defined? */ if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { error = ENOATTR; goto vopunlock_exit; } /* Valid for the current inode generation? */ if (ueh.ueh_i_gen != ip->i_gen) { /* * The inode itself has a different generation number than * the attribute data. For now, the best solution is to * coerce this to undefined, and let it get cleaned up by * the next write or extattrctl clean. */ printf("ufs_extattr_rm (%s): inode number inconsistency (%d, %jd)\n", mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen); error = ENOATTR; goto vopunlock_exit; } /* Flag it as not in use. */ ueh.ueh_flags = 0; ueh.ueh_len = 0; local_aiov.iov_base = (caddr_t) &ueh; local_aiov.iov_len = sizeof(struct ufs_extattr_header); local_aio.uio_iov = &local_aiov; local_aio.uio_iovcnt = 1; local_aio.uio_rw = UIO_WRITE; local_aio.uio_segflg = UIO_SYSSPACE; local_aio.uio_td = td; local_aio.uio_offset = base_offset; local_aio.uio_resid = sizeof(struct ufs_extattr_header); ioflag = IO_NODELOCKED; if (ufs_extattr_sync) ioflag |= IO_SYNC; error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; if (local_aio.uio_resid != 0) error = ENXIO; vopunlock_exit: VOP_UNLOCK(attribute->uele_backing_vnode, 0); return (error); }
/* * Real work associated with setting a vnode's extended attributes; * assumes that the attribute lock has already been grabbed. */ static int ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name, struct uio *uio, struct ucred *cred, struct thread *td) { struct ufs_extattr_list_entry *attribute; struct ufs_extattr_header ueh; struct iovec local_aiov; struct uio local_aio; struct mount *mp = vp->v_mount; struct ufsmount *ump = VFSTOUFS(mp); struct inode *ip = VTOI(vp); off_t base_offset; int error = 0, ioflag; if (vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) return (EOPNOTSUPP); if (!ufs_extattr_valid_attrname(attrnamespace, name)) return (EINVAL); error = extattr_check_cred(vp, attrnamespace, cred, td, VWRITE); if (error) return (error); attribute = ufs_extattr_find_attr(ump, attrnamespace, name); if (!attribute) return (ENOATTR); /* * Early rejection of invalid offsets/length. * Reject: any offset but 0 (replace) * Any size greater than attribute size limit */ if (uio->uio_offset != 0 || uio->uio_resid > attribute->uele_fileheader.uef_size) return (ENXIO); /* * Find base offset of header in file based on file header size, and * data header size + maximum data size, indexed by inode number. */ base_offset = sizeof(struct ufs_extattr_fileheader) + ip->i_number * (sizeof(struct ufs_extattr_header) + attribute->uele_fileheader.uef_size); /* * Write out a data header for the data. */ ueh.ueh_len = uio->uio_resid; ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; ueh.ueh_i_gen = ip->i_gen; local_aiov.iov_base = (caddr_t) &ueh; local_aiov.iov_len = sizeof(struct ufs_extattr_header); local_aio.uio_iov = &local_aiov; local_aio.uio_iovcnt = 1; local_aio.uio_rw = UIO_WRITE; local_aio.uio_segflg = UIO_SYSSPACE; local_aio.uio_td = td; local_aio.uio_offset = base_offset; local_aio.uio_resid = sizeof(struct ufs_extattr_header); /* * Acquire locks. * * Don't need to get a lock on the backing file if the setattr is * being applied to the backing file, as the lock is already held. */ if (attribute->uele_backing_vnode != vp) vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY); ioflag = IO_NODELOCKED; if (ufs_extattr_sync) ioflag |= IO_SYNC; error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, ump->um_extattr.uepm_ucred); if (error) goto vopunlock_exit; if (local_aio.uio_resid != 0) { error = ENXIO; goto vopunlock_exit; } /* * Write out user data. */ uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); ioflag = IO_NODELOCKED; if (ufs_extattr_sync) ioflag |= IO_SYNC; error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag, ump->um_extattr.uepm_ucred); vopunlock_exit: uio->uio_offset = 0; if (attribute->uele_backing_vnode != vp) VOP_UNLOCK(attribute->uele_backing_vnode, 0); return (error); }
static int _xfs_listextattr( struct vop_listextattr_args /* { struct vnode *a_vp; int a_attrnamespace; struct uio *a_uio; size_t *a_size; struct ucred *a_cred; struct thread *a_td; } */ *ap) { int error; char *buf = NULL; int buf_len = 0; attrlist_cursor_kern_t cursor = { 0 }; int i; char name_len; int attrnames_len = 0; int xfs_flags = ATTR_KERNAMELS; error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_td, VREAD); if (error) return (error); if (ap->a_attrnamespace & EXTATTR_NAMESPACE_USER) xfs_flags |= ATTR_KERNORMALS; if (ap->a_attrnamespace & EXTATTR_NAMESPACE_SYSTEM) xfs_flags |= ATTR_KERNROOTLS; if (ap->a_uio == NULL || ap->a_uio->uio_iov[0].iov_base == NULL) { xfs_flags |= ATTR_KERNOVAL; buf_len = 0; } else { buf = ap->a_uio->uio_iov[0].iov_base; buf_len = ap->a_uio->uio_iov[0].iov_len; } XVOP_ATTR_LIST(VPTOXFSVP(ap->a_vp), buf, buf_len, xfs_flags, &cursor, ap->a_cred, error); if (error < 0) { attrnames_len = -error; error = 0; } if (buf == NULL) goto done; /* * extattr_list expects a list of names. Each list * entry consists of one byte for the name length, followed * by the name (not null terminated) */ name_len=0; for(i=attrnames_len-1; i > 0 ; --i) { buf[i] = buf[i-1]; if (buf[i]) ++name_len; else { buf[i] = name_len; name_len = 0; } } buf[0] = name_len; if (ap->a_uio != NULL) ap->a_uio->uio_resid -= attrnames_len; done: if (ap->a_size != NULL) *ap->a_size = attrnames_len; return (error); }
/* * Vnode operation to set a named attribute. */ int ffs_ea_set(void *v) { struct vop_setextattr_args *ap = v; struct inode *ip; struct fs *fs; u_int32_t prefixlen, bodylen, entrysize, pad1, pad2, pesize; int error; unsigned int peoffset; unsigned char *eae, *p, *peaddr; ip = VTOI(ap->a_vp); fs = ip->i_fs; /* * Validate the vnode. It has to be a FFS2 file, directory, or symlink, * and must reside in a writable file system. */ if (ip->i_fs->fs_magic != FS_UFS2_MAGIC) return (EOPNOTSUPP); if (ap->a_vp->v_type != VREG && ap->a_vp->v_type != VDIR && ap->a_vp->v_type != VLNK) return (EOPNOTSUPP); if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); /* The attribute name must be at least one byte long. */ if (strlen(ap->a_name) == 0) return (EINVAL); /* * Validate credentials, read in the inode's extended attributes area, * and make sure there is no attribute with the same name. */ error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_p, IWRITE); if (error) return (error); error = ffs_ea_iget(ap->a_vp, ap->a_cred, ap->a_p); if (error) return (error); /* * Check whether an entry for the given attribute already exists, and * get its parameters. */ peaddr = NULL; pesize = peoffset = 0; (void) ffs_ea_find(ip, ap->a_attrnamespace, ap->a_name, &peaddr, NULL); if (peaddr != NULL) { /* * Get absolute offset of entry. Make sure it is within the * extended attribute area. */ peoffset = peaddr - ip->i_ea_area; if (peoffset >= ip->i_ea_len) { (void) ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); return (EINVAL); } /* * Get size of entry. Make sure it is a sane value. */ bcopy(peaddr, &pesize, sizeof(pesize)); if (peoffset + pesize > ip->i_ea_len) { (void) ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); return (EINVAL); } } /* * Start constructing the extended attribute entry in memory. An entry * is made up of the following fields, laid out in top/bottom order: * * +-----------------------------+---------+ * | | 1. Record size | 4 bytes | * | | 2. Attribute name space | 1 byte | * | | 3. Length of second padding | 1 byte | * | | 4. Length of attribute name | 1 byte | * | +-----------------------------+---------+ * | | 5. Attribute name | * | | 6. First padding | * | | 7. Attribute content | * V | 8. Second padding | * +-----------------------------+ * * The first 5 fields have their length computed in 'prefixlen'. Based * on this value, the first padding is calculated. Likewise, the 7th * field (the attribute content) has its length computed in 'bodylen', * based on which the second padding is calculated. The complete entry * length is computed in 'entrysize'. */ prefixlen = sizeof(u_int32_t) + 3 + strlen(ap->a_name); pad1 = 8 - (prefixlen % 8); if (pad1 == 8) pad1 = 0; bodylen = ap->a_uio->uio_resid; pad2 = 8 - (bodylen % 8); if (pad2 == 8) pad2 = 0; entrysize = prefixlen + pad1 + bodylen + pad2; /* * Make sure we're not crossing the limit for extended attributes. * Account for the released space of previous entries (which will be * removed in favour of the new one). */ if (ip->i_ea_len + entrysize - pesize > NXADDR * fs->fs_bsize) { ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); return (ENOSPC); } eae = malloc(ip->i_ea_len + entrysize - pesize, M_TEMP, M_WAITOK | M_CANFAIL); if (eae == NULL) { ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); return (ENOMEM); } /* Find out where to store the entry in the extended attribute area. */ if (peaddr != NULL) { if (pesize == entrysize) { /* Previous entry of same size. Just overwrite it. */ bcopy(ip->i_ea_area, eae, ip->i_ea_len); p = eae + peoffset; } else { /* * Previous entry of different size. Skip it when * copying contents, and insert new entry at the end. */ bcopy(ip->i_ea_area, eae, peoffset); bcopy(peaddr + pesize, eae + peoffset, ip->i_ea_len - peoffset - pesize); p = eae + ip->i_ea_len - pesize; } } else { /* New entry. Insert at the end. */ bcopy(ip->i_ea_area, eae, ip->i_ea_len); p = eae + ip->i_ea_len; } /* Prefix (1st-5th fields). */ bcopy(&entrysize, p, sizeof(entrysize)); p += sizeof(entrysize); *p++ = ap->a_attrnamespace; *p++ = pad2; *p++ = strlen(ap->a_name); bcopy(ap->a_name, p, strlen(ap->a_name)); p += strlen(ap->a_name); /* Paddings and content (6th-8th fields). */ bzero(p, pad1); p += pad1; error = uiomove(p, bodylen, ap->a_uio); if (error) { free(eae, M_TEMP); ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); return (error); } p += bodylen; bzero(p, pad2); /* * Swap old extended attributes area with new one, and write it out. */ free(ip->i_ea_area, M_TEMP); ip->i_ea_area = eae; ip->i_ea_len += entrysize - pesize; return (ffs_ea_iput(ap->a_vp, 1, ap->a_cred, ap->a_p)); }
/* * Vnode operation to retrieve extended attributes on a vnode. */ int ffs_ea_list(void *v) { struct vop_listextattr_args *ap = v; struct inode *ip; struct fs *fs; unsigned char *p, *pend, *pnext; u_int32_t rz; int error, attrnamelen; ip = VTOI(ap->a_vp); fs = ip->i_fs; /* * Validate the vnode. It has to be a FFS2 file, directory, or symlink. */ if (ip->i_fs->fs_magic != FS_UFS2_MAGIC) return (EOPNOTSUPP); if (ap->a_vp->v_type != VREG && ap->a_vp->v_type != VDIR && ap->a_vp->v_type != VLNK) return (EOPNOTSUPP); /* * Validate credentials and read in the inode's extended attributes * area. */ error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_p, IREAD); if (error) return (error); error = ffs_ea_iget(ap->a_vp, ap->a_cred, ap->a_p); if (error) return (error); error = 0; if (ap->a_size != NULL) *ap->a_size = 0; pend = ip->i_ea_area + ip->i_ea_len; /* * See comment in ffs_ea_set() for a detailed description of how * extended attribute entries are laid out. */ for(p = (unsigned char *)ip->i_ea_area; p < pend; p = pnext) { /* * Read in the record size, set pointer to next entry. Make * sure this is an entry belonging to the attribute space we * are interesting in, and copy the attribute name over. */ bcopy(p, &rz, sizeof(rz)); pnext = p + rz; if (pnext > pend) break; /* Stepping out of extended attribute area. */ p += sizeof(rz); if (*p++ != ap->a_attrnamespace) continue; p++; /* Skip length of second padding. */ attrnamelen = (int)*p; if (ap->a_size != NULL) *ap->a_size += attrnamelen + 1; else if (ap->a_uio != NULL) { error = uiomove(p, attrnamelen + 1, ap->a_uio); if (error) { ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); return (error); } } } return (ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p)); }
/* * Vnode operation to remove a named attribute. */ int ffs_ea_del(void *v) { struct vop_deleteextattr_args *ap = v; struct inode *ip; int error, olen; u_int32_t esize; unsigned int eoffset; unsigned char *eae, *eaddr; ip = VTOI(ap->a_vp); /* * Validate the vnode. It has to be a FFS2 file, directory, or symlink, * and must reside in a writable file system. */ if (ip->i_fs->fs_magic != FS_UFS2_MAGIC) return (EOPNOTSUPP); if (ap->a_vp->v_type != VREG && ap->a_vp->v_type != VDIR && ap->a_vp->v_type != VLNK) return (EOPNOTSUPP); if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) return (EROFS); /* The attribute name must be at least one byte long. */ if (strlen(ap->a_name) == 0) return (EINVAL); /* * Validate credentials, read in the inode's extended attributes area, * and make sure there is an attribute with the given name. */ error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_p, IWRITE); if (error) return (error); error = ffs_ea_iget(ap->a_vp, ap->a_cred, ap->a_p); if (error) return (error); olen = ffs_ea_find(ip, ap->a_attrnamespace, ap->a_name, &eaddr, NULL); if (olen == -1) { /* No such attribute. */ (void) ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); return (ENOATTR); } /* * Get absolute offset of entry. Make sure it is within the extended * attribute area. */ eoffset = eaddr - ip->i_ea_area; if (eoffset >= ip->i_ea_len) { (void) ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); return (EINVAL); } /* * Get size of entry. Make sure it is a sane value. */ bcopy(eaddr, &esize, sizeof(esize)); if (eoffset + esize > ip->i_ea_len) { (void) ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); return (EINVAL); } /* * Allocate a new extended attribute area and copy the contents over, * skipping the entry found. */ eae = malloc(ip->i_ea_len - esize, M_TEMP, M_WAITOK | M_CANFAIL); if (eae == NULL) { (void) ffs_ea_iput(ap->a_vp, 0, ap->a_cred, ap->a_p); return (ENOMEM); } bcopy(ip->i_ea_area, eae, eoffset); bcopy(eaddr + esize, eae + eoffset, ip->i_ea_len - eoffset - esize); free(ip->i_ea_area, M_TEMP); ip->i_ea_area = eae; ip->i_ea_len -= esize; return (ffs_ea_iput(ap->a_vp, 1, ap->a_cred, ap->a_p)); }