aboutsummaryrefslogtreecommitdiff
path: root/sys/ufs
diff options
context:
space:
mode:
Diffstat (limited to 'sys/ufs')
-rw-r--r--sys/ufs/ffs/ffs_alloc.c14
-rw-r--r--sys/ufs/ffs/ffs_vfsops.c29
-rw-r--r--sys/ufs/ufs/extattr.h2
-rw-r--r--sys/ufs/ufs/ufs_acl.c288
-rw-r--r--sys/ufs/ufs/ufs_extattr.c7
-rw-r--r--sys/ufs/ufs/ufs_extern.h1
-rw-r--r--sys/ufs/ufs/ufs_vfsops.c8
7 files changed, 194 insertions, 155 deletions
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c
index 4748f682101c..b4f001e6c677 100644
--- a/sys/ufs/ffs/ffs_alloc.c
+++ b/sys/ufs/ffs/ffs_alloc.c
@@ -326,10 +326,9 @@ retry:
ip->i_flag |= IN_CHANGE | IN_UPDATE;
allocbuf(bp, nsize);
bp->b_flags |= B_DONE;
- if ((bp->b_flags & (B_MALLOC | B_VMIO)) != B_VMIO)
- bzero((char *)bp->b_data + osize, nsize - osize);
- else
- vfs_bio_clrbuf(bp);
+ bzero(bp->b_data + osize, nsize - osize);
+ if ((bp->b_flags & (B_MALLOC | B_VMIO)) == B_VMIO)
+ vfs_bio_set_valid(bp, osize, nsize - osize);
*bpp = bp;
return (0);
}
@@ -404,10 +403,9 @@ retry:
ip->i_flag |= IN_CHANGE | IN_UPDATE;
allocbuf(bp, nsize);
bp->b_flags |= B_DONE;
- if ((bp->b_flags & (B_MALLOC | B_VMIO)) != B_VMIO)
- bzero((char *)bp->b_data + osize, nsize - osize);
- else
- vfs_bio_clrbuf(bp);
+ bzero(bp->b_data + osize, nsize - osize);
+ if ((bp->b_flags & (B_MALLOC | B_VMIO)) == B_VMIO)
+ vfs_bio_set_valid(bp, osize, nsize - osize);
*bpp = bp;
return (0);
}
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
index 94752c7bc832..c5c13289ba75 100644
--- a/sys/ufs/ffs/ffs_vfsops.c
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -132,9 +132,10 @@ static const char *ffs_opts[] = { "acls", "async", "noatime", "noclusterr",
"union", NULL };
static int
-ffs_mount(struct mount *mp, struct thread *td)
+ffs_mount(struct mount *mp)
{
struct vnode *devvp;
+ struct thread *td;
struct ufsmount *ump = 0;
struct fs *fs;
int error, flags;
@@ -143,6 +144,7 @@ ffs_mount(struct mount *mp, struct thread *td)
struct nameidata ndp;
char *fspec;
+ td = curthread;
if (vfs_filteropt(mp->mnt_optnew, ffs_opts))
return (EINVAL);
if (uma_inode == NULL) {
@@ -213,7 +215,7 @@ ffs_mount(struct mount *mp, struct thread *td)
* ignore the suspension to
* synchronize on-disk state.
*/
- curthread->td_pflags |= TDP_IGNSUSP;
+ td->td_pflags |= TDP_IGNSUSP;
break;
}
MNT_IUNLOCK(mp);
@@ -431,7 +433,7 @@ ffs_mount(struct mount *mp, struct thread *td)
*/
static int
-ffs_cmount(struct mntarg *ma, void *data, int flags, struct thread *td)
+ffs_cmount(struct mntarg *ma, void *data, int flags)
{
struct ufs_args args;
int error;
@@ -1025,11 +1027,11 @@ ffs_oldfscompat_write(fs, ump)
* unmount system call
*/
static int
-ffs_unmount(mp, mntflags, td)
+ffs_unmount(mp, mntflags)
struct mount *mp;
int mntflags;
- struct thread *td;
{
+ struct thread *td;
struct ufsmount *ump = VFSTOUFS(mp);
struct fs *fs;
int error, flags, susp;
@@ -1038,6 +1040,7 @@ ffs_unmount(mp, mntflags, td)
#endif
flags = 0;
+ td = curthread;
fs = ump->um_fs;
if (mntflags & MNT_FORCE) {
flags |= FORCECLOSE;
@@ -1069,7 +1072,7 @@ ffs_unmount(mp, mntflags, td)
MNTK_SUSPEND2);
wakeup(&mp->mnt_flag);
MNT_IUNLOCK(mp);
- curthread->td_pflags |= TDP_IGNSUSP;
+ td->td_pflags |= TDP_IGNSUSP;
break;
}
MNT_IUNLOCK(mp);
@@ -1199,10 +1202,9 @@ ffs_flushfiles(mp, flags, td)
* Get filesystem statistics.
*/
static int
-ffs_statfs(mp, sbp, td)
+ffs_statfs(mp, sbp)
struct mount *mp;
struct statfs *sbp;
- struct thread *td;
{
struct ufsmount *ump;
struct fs *fs;
@@ -1235,12 +1237,12 @@ ffs_statfs(mp, sbp, td)
* Note: we are always called with the filesystem marked `MPBUSY'.
*/
static int
-ffs_sync(mp, waitfor, td)
+ffs_sync(mp, waitfor)
struct mount *mp;
int waitfor;
- struct thread *td;
{
struct vnode *mvp, *vp, *devvp;
+ struct thread *td;
struct inode *ip;
struct ufsmount *ump = VFSTOUFS(mp);
struct fs *fs;
@@ -1253,6 +1255,7 @@ ffs_sync(mp, waitfor, td)
int softdep_accdeps;
struct bufobj *bo;
+ td = curthread;
fs = ump->um_fs;
if (fs->fs_fmod != 0 && fs->fs_ronly != 0) { /* XXX */
printf("fs = %s\n", fs->fs_fsmnt);
@@ -1698,15 +1701,15 @@ ffs_sbupdate(mp, waitfor, suspended)
static int
ffs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
- int attrnamespace, const char *attrname, struct thread *td)
+ int attrnamespace, const char *attrname)
{
#ifdef UFS_EXTATTR
return (ufs_extattrctl(mp, cmd, filename_vp, attrnamespace,
- attrname, td));
+ attrname));
#else
return (vfs_stdextattrctl(mp, cmd, filename_vp, attrnamespace,
- attrname, td));
+ attrname));
#endif
}
diff --git a/sys/ufs/ufs/extattr.h b/sys/ufs/ufs/extattr.h
index 5d26bf6cd552..3716ec27309a 100644
--- a/sys/ufs/ufs/extattr.h
+++ b/sys/ufs/ufs/extattr.h
@@ -143,7 +143,7 @@ int ufs_extattr_start(struct mount *mp, struct thread *td);
int ufs_extattr_autostart(struct mount *mp, struct thread *td);
int ufs_extattr_stop(struct mount *mp, struct thread *td);
int ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename,
- int attrnamespace, const char *attrname, struct thread *td);
+ int attrnamespace, const char *attrname);
int ufs_getextattr(struct vop_getextattr_args *ap);
int ufs_deleteextattr(struct vop_deleteextattr_args *ap);
int ufs_setextattr(struct vop_setextattr_args *ap);
diff --git a/sys/ufs/ufs/ufs_acl.c b/sys/ufs/ufs/ufs_acl.c
index 89488c7995fb..c04a5d2ffaac 100644
--- a/sys/ufs/ufs/ufs_acl.c
+++ b/sys/ufs/ufs/ufs_acl.c
@@ -141,24 +141,68 @@ ufs_sync_inode_from_acl(struct acl *acl, struct inode *ip)
}
/*
+ * Read POSIX.1e ACL from an EA. Return error if its not found
+ * or if any other error has occured.
+ */
+static int
+ufs_get_oldacl(acl_type_t type, struct oldacl *old, struct vnode *vp,
+ struct thread *td)
+{
+ int error, len;
+ struct inode *ip = VTOI(vp);
+
+ len = sizeof(*old);
+
+ switch (type) {
+ case ACL_TYPE_ACCESS:
+ error = vn_extattr_get(vp, IO_NODELOCKED,
+ POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE,
+ POSIX1E_ACL_ACCESS_EXTATTR_NAME, &len, (char *) old,
+ td);
+ break;
+ case ACL_TYPE_DEFAULT:
+ if (vp->v_type != VDIR)
+ return (EINVAL);
+ error = vn_extattr_get(vp, IO_NODELOCKED,
+ POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE,
+ POSIX1E_ACL_DEFAULT_EXTATTR_NAME, &len, (char *) old,
+ td);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (error != 0)
+ return (error);
+
+ if (len != sizeof(*old)) {
+ /*
+ * A short (or long) read, meaning that for some reason
+ * the ACL is corrupted. Return EPERM since the object
+ * DAC protections are unsafe.
+ */
+ printf("ufs_get_oldacl(): Loaded invalid ACL "
+ "(len = %d), inumber %d on %s\n", len,
+ ip->i_number, ip->i_fs->fs_fsmnt);
+ return (EPERM);
+ }
+
+ return (0);
+}
+
+/*
* Retrieve the ACL on a file.
*
* As part of the ACL is stored in the inode, and the rest in an EA,
* assemble both into a final ACL product. Right now this is not done
* very efficiently.
*/
-int
-ufs_getacl(ap)
- struct vop_getacl_args /* {
- struct vnode *vp;
- struct acl_type_t type;
- struct acl *aclp;
- struct ucred *cred;
- struct thread *td;
- } */ *ap;
+static int
+ufs_getacl_posix1e(struct vop_getacl_args *ap)
{
struct inode *ip = VTOI(ap->a_vp);
- int error, len;
+ int error;
+ struct oldacl *old;
/*
* XXX: If ufs_getacl() should work on file systems not supporting
@@ -167,121 +211,83 @@ ufs_getacl(ap)
if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
return (EOPNOTSUPP);
+ old = malloc(sizeof(*old), M_ACL, M_WAITOK | M_ZERO);
+
/*
- * Attempt to retrieve the ACL based on the ACL type.
+ * Attempt to retrieve the ACL from the extended attributes.
*/
- bzero(ap->a_aclp, sizeof(*ap->a_aclp));
- len = sizeof(*ap->a_aclp);
- switch(ap->a_type) {
- case ACL_TYPE_ACCESS:
- /*
- * ACL_TYPE_ACCESS ACLs may or may not be stored in the
- * EA, as they are in fact a combination of the inode
- * ownership/permissions and the EA contents. If the
- * EA is present, merge the two in a temporary ACL
- * storage, otherwise just return the inode contents.
- */
- error = vn_extattr_get(ap->a_vp, IO_NODELOCKED,
- POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE,
- POSIX1E_ACL_ACCESS_EXTATTR_NAME, &len, (char *) ap->a_aclp,
- ap->a_td);
- switch (error) {
- /* XXX: If ufs_getacl() should work on filesystems without
- * the EA configured, add case EOPNOTSUPP here. */
- case ENOATTR:
+ error = ufs_get_oldacl(ap->a_type, old, ap->a_vp, ap->a_td);
+ switch (error) {
+ /*
+ * XXX: If ufs_getacl() should work on filesystems
+ * without the EA configured, add case EOPNOTSUPP here.
+ */
+ case ENOATTR:
+ switch (ap->a_type) {
+ case ACL_TYPE_ACCESS:
/*
* Legitimately no ACL set on object, purely
* emulate it through the inode. These fields will
* be updated when the ACL is synchronized with
* the inode later.
*/
- ap->a_aclp->acl_cnt = 3;
- ap->a_aclp->acl_entry[0].ae_tag = ACL_USER_OBJ;
- ap->a_aclp->acl_entry[0].ae_id = ACL_UNDEFINED_ID;
- ap->a_aclp->acl_entry[0].ae_perm = ACL_PERM_NONE;
- ap->a_aclp->acl_entry[1].ae_tag = ACL_GROUP_OBJ;
- ap->a_aclp->acl_entry[1].ae_id = ACL_UNDEFINED_ID;
- ap->a_aclp->acl_entry[1].ae_perm = ACL_PERM_NONE;
- ap->a_aclp->acl_entry[2].ae_tag = ACL_OTHER;
- ap->a_aclp->acl_entry[2].ae_id = ACL_UNDEFINED_ID;
- ap->a_aclp->acl_entry[2].ae_perm = ACL_PERM_NONE;
- ufs_sync_acl_from_inode(ip, ap->a_aclp);
- error = 0;
- break;
-
- case 0:
- if (len != sizeof(*ap->a_aclp)) {
- /*
- * A short (or long) read, meaning that for
- * some reason the ACL is corrupted. Return
- * EPERM since the object DAC protections
- * are unsafe.
- */
- printf("ufs_getacl(): Loaded invalid ACL ("
- "%d bytes), inumber %d on %s\n", len,
- ip->i_number, ip->i_fs->fs_fsmnt);
- return (EPERM);
- }
- ufs_sync_acl_from_inode(ip, ap->a_aclp);
- break;
-
- default:
+ old->acl_cnt = 3;
+ old->acl_entry[0].ae_tag = ACL_USER_OBJ;
+ old->acl_entry[0].ae_id = ACL_UNDEFINED_ID;
+ old->acl_entry[0].ae_perm = ACL_PERM_NONE;
+ old->acl_entry[1].ae_tag = ACL_GROUP_OBJ;
+ old->acl_entry[1].ae_id = ACL_UNDEFINED_ID;
+ old->acl_entry[1].ae_perm = ACL_PERM_NONE;
+ old->acl_entry[2].ae_tag = ACL_OTHER;
+ old->acl_entry[2].ae_id = ACL_UNDEFINED_ID;
+ old->acl_entry[2].ae_perm = ACL_PERM_NONE;
break;
- }
- break;
- case ACL_TYPE_DEFAULT:
- if (ap->a_vp->v_type != VDIR) {
- error = EINVAL;
+ case ACL_TYPE_DEFAULT:
+ /*
+ * Unlike ACL_TYPE_ACCESS, there is no relationship
+ * between the inode contents and the ACL, and it is
+ * therefore possible for the request for the ACL
+ * to fail since the ACL is undefined. In this
+ * situation, return success and an empty ACL,
+ * as required by POSIX.1e.
+ */
+ old->acl_cnt = 0;
break;
}
- error = vn_extattr_get(ap->a_vp, IO_NODELOCKED,
- POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE,
- POSIX1E_ACL_DEFAULT_EXTATTR_NAME, &len,
- (char *) ap->a_aclp, ap->a_td);
- /*
- * Unlike ACL_TYPE_ACCESS, there is no relationship between
- * the inode contents and the ACL, and it is therefore
- * possible for the request for the ACL to fail since the
- * ACL is undefined. In this situation, return success
- * and an empty ACL, as required by POSIX.1e.
- */
- switch (error) {
- /* XXX: If ufs_getacl() should work on filesystems without
- * the EA configured, add case EOPNOTSUPP here. */
- case ENOATTR:
- bzero(ap->a_aclp, sizeof(*ap->a_aclp));
- ap->a_aclp->acl_cnt = 0;
- error = 0;
- break;
- case 0:
- if (len != sizeof(*ap->a_aclp)) {
- /*
- * A short (or long) read, meaning that for
- * some reason the ACL is corrupted. Return
- * EPERM since the object default DAC
- * protections are unsafe.
- */
- printf("ufs_getacl(): Loaded invalid ACL ("
- "%d bytes), inumber %d on %s\n", len,
- ip->i_number, ip->i_fs->fs_fsmnt);
- return (EPERM);
- }
- break;
+ error = 0;
- default:
+ /* FALLTHROUGH */
+ case 0:
+ error = acl_copy_oldacl_into_acl(old, ap->a_aclp);
+ if (error != 0)
break;
- }
- break;
+ if (ap->a_type == ACL_TYPE_ACCESS)
+ ufs_sync_acl_from_inode(ip, ap->a_aclp);
default:
- error = EINVAL;
+ break;
}
+ free(old, M_ACL);
return (error);
}
+int
+ufs_getacl(ap)
+ struct vop_getacl_args /* {
+ struct vnode *vp;
+ acl_type_t type;
+ struct acl *aclp;
+ struct ucred *cred;
+ struct thread *td;
+ } */ *ap;
+{
+
+ return (ufs_getacl_posix1e(ap));
+}
+
/*
* Set the ACL on a file.
*
@@ -291,18 +297,12 @@ ufs_getacl(ap)
* a fair number of different access checks may be required to go ahead
* with the operation at all.
*/
-int
-ufs_setacl(ap)
- struct vop_setacl_args /* {
- struct vnode *vp;
- acl_type_t type;
- struct acl *aclp;
- struct ucred *cred;
- struct proc *p;
- } */ *ap;
+static int
+ufs_setacl_posix1e(struct vop_setacl_args *ap)
{
struct inode *ip = VTOI(ap->a_vp);
int error;
+ struct oldacl *old;
if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
return (EOPNOTSUPP);
@@ -349,10 +349,15 @@ ufs_setacl(ap)
switch(ap->a_type) {
case ACL_TYPE_ACCESS:
- error = vn_extattr_set(ap->a_vp, IO_NODELOCKED,
- POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE,
- POSIX1E_ACL_ACCESS_EXTATTR_NAME, sizeof(*ap->a_aclp),
- (char *) ap->a_aclp, ap->a_td);
+ old = malloc(sizeof(*old), M_ACL, M_WAITOK | M_ZERO);
+ error = acl_copy_acl_into_oldacl(ap->a_aclp, old);
+ if (error == 0) {
+ error = vn_extattr_set(ap->a_vp, IO_NODELOCKED,
+ POSIX1E_ACL_ACCESS_EXTATTR_NAMESPACE,
+ POSIX1E_ACL_ACCESS_EXTATTR_NAME, sizeof(*old),
+ (char *) old, ap->a_td);
+ }
+ free(old, M_ACL);
break;
case ACL_TYPE_DEFAULT:
@@ -372,11 +377,17 @@ ufs_setacl(ap)
*/
if (error == ENOATTR)
error = 0;
- } else
- error = vn_extattr_set(ap->a_vp, IO_NODELOCKED,
- POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE,
- POSIX1E_ACL_DEFAULT_EXTATTR_NAME,
- sizeof(*ap->a_aclp), (char *) ap->a_aclp, ap->a_td);
+ } else {
+ old = malloc(sizeof(*old), M_ACL, M_WAITOK | M_ZERO);
+ error = acl_copy_acl_into_oldacl(ap->a_aclp, old);
+ if (error == 0) {
+ error = vn_extattr_set(ap->a_vp, IO_NODELOCKED,
+ POSIX1E_ACL_DEFAULT_EXTATTR_NAMESPACE,
+ POSIX1E_ACL_DEFAULT_EXTATTR_NAME,
+ sizeof(*old), (char *) old, ap->a_td);
+ }
+ free(old, M_ACL);
+ }
break;
default:
@@ -404,12 +415,9 @@ ufs_setacl(ap)
return (0);
}
-/*
- * Check the validity of an ACL for a file.
- */
int
-ufs_aclcheck(ap)
- struct vop_aclcheck_args /* {
+ufs_setacl(ap)
+ struct vop_setacl_args /* {
struct vnode *vp;
acl_type_t type;
struct acl *aclp;
@@ -418,6 +426,13 @@ ufs_aclcheck(ap)
} */ *ap;
{
+ return (ufs_setacl_posix1e(ap));
+}
+
+static int
+ufs_aclcheck_posix1e(struct vop_aclcheck_args *ap)
+{
+
if ((ap->a_vp->v_mount->mnt_flag & MNT_ACLS) == 0)
return (EOPNOTSUPP);
@@ -438,7 +453,28 @@ ufs_aclcheck(ap)
default:
return (EINVAL);
}
+
+ if (ap->a_aclp->acl_cnt > OLDACL_MAX_ENTRIES)
+ return (EINVAL);
+
return (acl_posix1e_check(ap->a_aclp));
}
+/*
+ * Check the validity of an ACL for a file.
+ */
+int
+ufs_aclcheck(ap)
+ struct vop_aclcheck_args /* {
+ struct vnode *vp;
+ acl_type_t type;
+ struct acl *aclp;
+ struct ucred *cred;
+ struct thread *td;
+ } */ *ap;
+{
+
+ return (ufs_aclcheck_posix1e(ap));
+}
+
#endif /* !UFS_ACL */
diff --git a/sys/ufs/ufs/ufs_extattr.c b/sys/ufs/ufs/ufs_extattr.c
index d034bee1f500..032d9cc34211 100644
--- a/sys/ufs/ufs/ufs_extattr.c
+++ b/sys/ufs/ufs/ufs_extattr.c
@@ -93,8 +93,10 @@ static int ufs_extattr_set(struct vnode *vp, int attrnamespace,
struct thread *td);
static int ufs_extattr_rm(struct vnode *vp, int attrnamespace,
const char *name, struct ucred *cred, struct thread *td);
+#ifdef UFS_EXTATTR_AUTOSTART
static int ufs_extattr_autostart_locked(struct mount *mp,
struct thread *td);
+#endif
static int ufs_extattr_start_locked(struct ufsmount *ump,
struct thread *td);
@@ -478,7 +480,7 @@ ufs_extattr_autostart_locked(struct mount *mp, struct thread *td)
* Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root?
* If so, automatically start EA's.
*/
- error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp, td);
+ error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp);
if (error) {
printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n",
error);
@@ -714,9 +716,10 @@ ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
*/
int
ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
- int attrnamespace, const char *attrname, struct thread *td)
+ int attrnamespace, const char *attrname)
{
struct ufsmount *ump = VFSTOUFS(mp);
+ struct thread *td = curthread;
int error;
/*
diff --git a/sys/ufs/ufs/ufs_extern.h b/sys/ufs/ufs/ufs_extern.h
index 2cb98016edab..b2e4a9757305 100644
--- a/sys/ufs/ufs/ufs_extern.h
+++ b/sys/ufs/ufs/ufs_extern.h
@@ -38,7 +38,6 @@ struct direct;
struct indir;
struct inode;
struct mount;
-struct netcred;
struct thread;
struct sockaddr;
struct ucred;
diff --git a/sys/ufs/ufs/ufs_vfsops.c b/sys/ufs/ufs/ufs_vfsops.c
index ac1fbcf71070..f6b6b1e2ef8c 100644
--- a/sys/ufs/ufs/ufs_vfsops.c
+++ b/sys/ufs/ufs/ufs_vfsops.c
@@ -66,11 +66,10 @@ MALLOC_DEFINE(M_UFSMNT, "ufs_mount", "UFS mount structure");
* Return the root of a filesystem.
*/
int
-ufs_root(mp, flags, vpp, td)
+ufs_root(mp, flags, vpp)
struct mount *mp;
int flags;
struct vnode **vpp;
- struct thread *td;
{
struct vnode *nvp;
int error;
@@ -86,18 +85,19 @@ ufs_root(mp, flags, vpp, td)
* Do operations associated with quotas
*/
int
-ufs_quotactl(mp, cmds, id, arg, td)
+ufs_quotactl(mp, cmds, id, arg)
struct mount *mp;
int cmds;
uid_t id;
void *arg;
- struct thread *td;
{
#ifndef QUOTA
return (EOPNOTSUPP);
#else
+ struct thread *td;
int cmd, type, error;
+ td = curthread;
cmd = cmds >> SUBCMDSHIFT;
type = cmds & SUBCMDMASK;
if (id == -1) {