diff --git a/sys/ufs/ffs/ffs_vnops.c b/sys/ufs/ffs/ffs_vnops.c index 5054177c9eeb..9acc0bdceec3 100644 --- a/sys/ufs/ffs/ffs_vnops.c +++ b/sys/ufs/ffs/ffs_vnops.c @@ -1,4 +1,4 @@ -/* $NetBSD: ffs_vnops.c,v 1.119 2011/06/12 03:36:00 rmind Exp $ */ +/* $NetBSD: ffs_vnops.c,v 1.120 2011/06/27 16:34:47 manu Exp $ */ /*- * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. @@ -61,7 +61,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: ffs_vnops.c,v 1.119 2011/06/12 03:36:00 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ffs_vnops.c,v 1.120 2011/06/27 16:34:47 manu Exp $"); #if defined(_KERNEL_OPT) #include "opt_ffs.h" @@ -736,9 +736,19 @@ ffs_listextattr(void *v) struct inode *ip = VTOI(ap->a_vp); struct fs *fs = ip->i_fs; - /* Not supported for UFS1 file systems. */ - if (fs->fs_magic == FS_UFS1_MAGIC) + if (fs->fs_magic == FS_UFS1_MAGIC) { +#ifdef UFS_EXTATTR + struct vnode *vp = ap->a_vp; + int error; + + fstrans_start(vp->v_mount, FSTRANS_SHARED); + error = ufs_listextattr(ap); + fstrans_done(vp->v_mount); + return error; +#else return (EOPNOTSUPP); +#endif + } /* XXX Not implemented for UFS2 file systems. */ return (EOPNOTSUPP); diff --git a/sys/ufs/ufs/ufs_extattr.c b/sys/ufs/ufs/ufs_extattr.c index e9f7f6a52b49..144e32fd26a3 100644 --- a/sys/ufs/ufs/ufs_extattr.c +++ b/sys/ufs/ufs/ufs_extattr.c @@ -1,4 +1,4 @@ -/* $NetBSD: ufs_extattr.c,v 1.32 2011/06/17 14:23:52 manu Exp $ */ +/* $NetBSD: ufs_extattr.c,v 1.33 2011/06/27 16:34:47 manu Exp $ */ /*- * Copyright (c) 1999-2002 Robert N. M. Watson @@ -48,7 +48,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: ufs_extattr.c,v 1.32 2011/06/17 14:23:52 manu Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ufs_extattr.c,v 1.33 2011/06/27 16:34:47 manu Exp $"); #ifdef _KERNEL_OPT #include "opt_ffs.h" @@ -95,6 +95,9 @@ static int ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, static int ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name, struct uio *uio, size_t *size, kauth_cred_t cred, struct lwp *l); +static int ufs_extattr_list(struct vnode *vp, int attrnamespace, + struct uio *uio, size_t *size, + kauth_cred_t cred, struct lwp *l); static int ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name, struct uio *uio, kauth_cred_t cred, struct lwp *l); @@ -102,6 +105,9 @@ static int ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name, kauth_cred_t cred, struct lwp *l); static struct ufs_extattr_list_entry *ufs_extattr_find_attr(struct ufsmount *, int, const char *); +static int ufs_extattr_get_header(struct vnode *, + struct ufs_extattr_list_entry *, + struct ufs_extattr_header *, off_t *); /* * Per-FS attribute lock protecting attribute operations. @@ -924,6 +930,86 @@ ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, } } +/* + * Read extended attribute header for a given vnode and attribute. + * Backing vnode should be locked and unlocked by caller. + */ +static int +ufs_extattr_get_header(struct vnode *vp, struct ufs_extattr_list_entry *uele, + struct ufs_extattr_header *ueh, off_t *bap) +{ + struct mount *mp = vp->v_mount; + struct ufsmount *ump = VFSTOUFS(mp); + struct inode *ip = VTOI(vp); + off_t base_offset; + struct iovec aiov; + struct uio aio; + int error; + + /* + * 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) + + uele->uele_fileheader.uef_size); + + /* + * Read in the data header to see if the data is defined, and if so + * how much. + */ + memset(ueh, 0, sizeof(struct ufs_extattr_header)); + aiov.iov_base = ueh; + aiov.iov_len = sizeof(struct ufs_extattr_header); + aio.uio_iov = &aiov; + aio.uio_iovcnt = 1; + aio.uio_rw = UIO_READ; + aio.uio_offset = base_offset; + aio.uio_resid = sizeof(struct ufs_extattr_header); + UIO_SETUP_SYSSPACE(&aio); + + error = VOP_READ(uele->uele_backing_vnode, &aio, + IO_NODELOCKED, ump->um_extattr.uepm_ucred); + if (error) + return error; + + /* + * Attribute headers are kept in file system byte order. + * XXX What about the blob of data? + */ + ueh->ueh_flags = ufs_rw32(ueh->ueh_flags, UELE_NEEDSWAP(uele)); + ueh->ueh_len = ufs_rw32(ueh->ueh_len, UELE_NEEDSWAP(uele)); + ueh->ueh_i_gen = ufs_rw32(ueh->ueh_i_gen, UELE_NEEDSWAP(uele)); + + /* Defined? */ + if ((ueh->ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) + return ENOATTR; + + /* Valid for the current inode generation? */ + if (ueh->ueh_i_gen != ip->i_gen) { + /* + * The inode itself has a different generation number + * than the uele 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("%s (%s): inode gen inconsistency (%u, %jd)\n", + __func__, mp->mnt_stat.f_mntonname, ueh->ueh_i_gen, + (intmax_t)ip->i_gen); + return ENOATTR; + } + + /* Local size consistency check. */ + if (ueh->ueh_len > uele->uele_fileheader.uef_size) + return ENXIO; + + /* Return base offset */ + if (bap != NULL) + *bap = base_offset; + + return 0; +} + /* * Vnode operation to retrieve a named extended attribute. */ @@ -964,11 +1050,8 @@ ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name, { 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; @@ -995,76 +1078,17 @@ ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name, 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. - */ - memset(&ueh, 0, sizeof(struct ufs_extattr_header)); - local_aiov.iov_base = &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_offset = base_offset; - local_aio.uio_resid = sizeof(struct ufs_extattr_header); - UIO_SETUP_SYSSPACE(&local_aio); - /* * 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); + 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); + error = ufs_extattr_get_header(vp, attribute, &ueh, &base_offset); if (error) goto vopunlock_exit; - /* - * Attribute headers are kept in file system byte order. - * XXX What about the blob of data? - */ - ueh.ueh_flags = ufs_rw32(ueh.ueh_flags, UELE_NEEDSWAP(attribute)); - ueh.ueh_len = ufs_rw32(ueh.ueh_len, UELE_NEEDSWAP(attribute)); - ueh.ueh_i_gen = ufs_rw32(ueh.ueh_i_gen, UELE_NEEDSWAP(attribute)); - - /* 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 gen inconsistency (%u, %jd)\n", - mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, - (intmax_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; @@ -1102,6 +1126,109 @@ ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name, return (error); } +/* + * Vnode operation to list extended attribute for a vnode + */ +int +ufs_listextattr(struct vop_listextattr_args *ap) +/* +vop_listextattr { + IN struct vnode *a_vp; + IN int a_attrnamespace; + INOUT struct uio *a_uio; + OUT size_t *a_size; + IN kauth_cred_t a_cred; + struct proc *a_p; +}; +*/ +{ + struct mount *mp = ap->a_vp->v_mount; + struct ufsmount *ump = VFSTOUFS(mp); + int error; + + ufs_extattr_uepm_lock(ump); + + error = ufs_extattr_list(ap->a_vp, ap->a_attrnamespace, + ap->a_uio, ap->a_size, ap->a_cred, curlwp); + + ufs_extattr_uepm_unlock(ump); + + return (error); +} + +/* + * Real work associated with retrieving list of attributes--assumes that + * the attribute lock has already been grabbed. + */ +static int +ufs_extattr_list(struct vnode *vp, int attrnamespace, + struct uio *uio, size_t *size, kauth_cred_t cred, struct lwp *l) +{ + struct ufs_extattr_list_entry *uele; + struct ufs_extattr_header ueh; + struct mount *mp = vp->v_mount; + struct ufsmount *ump = VFSTOUFS(mp); + size_t listsize = 0; + int error = 0; + + if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) + return (EOPNOTSUPP); + + error = extattr_check_cred(vp, attrnamespace, cred, l, IREAD); + if (error) + return (error); + + LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) { + unsigned char attrnamelen; + + if (uele->uele_attrnamespace != attrnamespace) + continue; + + error = ufs_extattr_get_header(vp, uele, &ueh, NULL); + if (error == ENOATTR) + continue; + if (error != 0) + return error; + + /* + * Don't need to get a lock on the backing file if + * the listattr is being applied to the backing file, + * as the lock is already held. + */ + if (uele->uele_backing_vnode != vp) + vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY); + + /* + * +1 for trailing \0 + */ + attrnamelen = strlen(uele->uele_attrname) + 1; + listsize += attrnamelen; + + /* Return data if the caller requested it. */ + if (uio != NULL) { + error = uiomove(uele->uele_attrname, + (size_t)attrnamelen, uio); + if (error != 0) + break; + } + + if (uele->uele_backing_vnode != vp) + VOP_UNLOCK(uele->uele_backing_vnode); + + if (error != 0) + return error; + } + + if (uio != NULL) + uio->uio_offset = 0; + + /* Return full data size if caller requested it. */ + if (size != NULL) + *size = listsize; + + return 0; +} + /* * Vnode operation to remove a named attribute. */ @@ -1290,11 +1417,10 @@ ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name, { 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); + struct iovec local_aiov; + struct uio local_aio; off_t base_offset; int error = 0, ioflag; @@ -1314,67 +1440,16 @@ ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name, 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. - */ - memset(&ueh, 0, sizeof(struct ufs_extattr_header)); - - local_aiov.iov_base = &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_offset = base_offset; - local_aio.uio_resid = sizeof(struct ufs_extattr_header); - UIO_SETUP_SYSSPACE(&local_aio); - - /* - * 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. + * 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_EXCLUSIVE | LK_RETRY); + 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); + error = ufs_extattr_get_header(vp, attribute, &ueh, &base_offset); if (error) goto vopunlock_exit; - /* - * Attribute headers are kept in file system byte order. - */ - ueh.ueh_flags = ufs_rw32(ueh.ueh_flags, UELE_NEEDSWAP(attribute)); - ueh.ueh_len = ufs_rw32(ueh.ueh_len, UELE_NEEDSWAP(attribute)); - ueh.ueh_i_gen = ufs_rw32(ueh.ueh_i_gen, UELE_NEEDSWAP(attribute)); - - /* 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 (%u, %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; /* No need to byte swap 0 */ ueh.ueh_len = 0; /* ...ditto... */ diff --git a/usr.bin/extattr/getextattr.c b/usr.bin/extattr/getextattr.c index f185fd2c0031..083ec712442c 100644 --- a/usr.bin/extattr/getextattr.c +++ b/usr.bin/extattr/getextattr.c @@ -1,4 +1,4 @@ -/* $NetBSD: getextattr.c,v 1.4 2011/05/24 12:25:05 joerg Exp $ */ +/* $NetBSD: getextattr.c,v 1.5 2011/06/27 16:34:48 manu Exp $ */ /*- * Copyright (c) 2002, 2003 Networks Associates Technology, Inc. @@ -238,8 +238,7 @@ main(int argc, char *argv[]) if (!flag_quiet) printf("%s\t", argv[arg_counter]); for (i = 0; i < error; i += buf[i] + 1) - printf("%s%*.*s", i ? "\t" : "", - buf[i], buf[i], buf + i + 1); + printf("%s%s", i ? "\t" : "", buf + i + 1); printf("\n"); continue; case EAGET: