Explorar o código

Merge branch 'xfs-misc-fixes-for-4.4-2' into for-next

Dave Chinner %!s(int64=9) %!d(string=hai) anos
pai
achega
2da5c4b05a

+ 6 - 2
fs/xfs/libxfs/xfs_format.h

@@ -1495,9 +1495,13 @@ struct xfs_acl {
 						sizeof(struct xfs_acl_entry) \
 						sizeof(struct xfs_acl_entry) \
 		: 25)
 		: 25)
 
 
-#define XFS_ACL_MAX_SIZE(mp) \
+#define XFS_ACL_SIZE(cnt) \
 	(sizeof(struct xfs_acl) + \
 	(sizeof(struct xfs_acl) + \
-		sizeof(struct xfs_acl_entry) * XFS_ACL_MAX_ENTRIES((mp)))
+		sizeof(struct xfs_acl_entry) * cnt)
+
+#define XFS_ACL_MAX_SIZE(mp) \
+	XFS_ACL_SIZE(XFS_ACL_MAX_ENTRIES((mp)))
+
 
 
 /* On-disk XFS extended attribute names */
 /* On-disk XFS extended attribute names */
 #define SGI_ACL_FILE		"SGI_ACL_FILE"
 #define SGI_ACL_FILE		"SGI_ACL_FILE"

+ 8 - 5
fs/xfs/xfs_acl.c

@@ -37,16 +37,19 @@
 
 
 STATIC struct posix_acl *
 STATIC struct posix_acl *
 xfs_acl_from_disk(
 xfs_acl_from_disk(
-	struct xfs_acl	*aclp,
-	int		max_entries)
+	const struct xfs_acl	*aclp,
+	int			len,
+	int			max_entries)
 {
 {
 	struct posix_acl_entry *acl_e;
 	struct posix_acl_entry *acl_e;
 	struct posix_acl *acl;
 	struct posix_acl *acl;
-	struct xfs_acl_entry *ace;
+	const struct xfs_acl_entry *ace;
 	unsigned int count, i;
 	unsigned int count, i;
 
 
+	if (len < sizeof(*aclp))
+		return ERR_PTR(-EFSCORRUPTED);
 	count = be32_to_cpu(aclp->acl_cnt);
 	count = be32_to_cpu(aclp->acl_cnt);
-	if (count > max_entries)
+	if (count > max_entries || XFS_ACL_SIZE(count) != len)
 		return ERR_PTR(-EFSCORRUPTED);
 		return ERR_PTR(-EFSCORRUPTED);
 
 
 	acl = posix_acl_alloc(count, GFP_KERNEL);
 	acl = posix_acl_alloc(count, GFP_KERNEL);
@@ -163,7 +166,7 @@ xfs_get_acl(struct inode *inode, int type)
 		goto out;
 		goto out;
 	}
 	}
 
 
-	acl = xfs_acl_from_disk(xfs_acl, XFS_ACL_MAX_ENTRIES(ip->i_mount));
+	acl = xfs_acl_from_disk(xfs_acl, len, XFS_ACL_MAX_ENTRIES(ip->i_mount));
 	if (IS_ERR(acl))
 	if (IS_ERR(acl))
 		goto out;
 		goto out;
 
 

+ 3 - 0
fs/xfs/xfs_acl.h

@@ -36,4 +36,7 @@ static inline struct posix_acl *xfs_get_acl(struct inode *inode, int type)
 # define posix_acl_access_exists(inode)			0
 # define posix_acl_access_exists(inode)			0
 # define posix_acl_default_exists(inode)		0
 # define posix_acl_default_exists(inode)		0
 #endif /* CONFIG_XFS_POSIX_ACL */
 #endif /* CONFIG_XFS_POSIX_ACL */
+
+extern void xfs_forget_acl(struct inode *inode, const char *name, int xflags);
+
 #endif	/* __XFS_ACL_H__ */
 #endif	/* __XFS_ACL_H__ */

+ 16 - 5
fs/xfs/xfs_file.c

@@ -242,19 +242,30 @@ xfs_file_fsync(
 	}
 	}
 
 
 	/*
 	/*
-	 * All metadata updates are logged, which means that we just have
-	 * to flush the log up to the latest LSN that touched the inode.
+	 * All metadata updates are logged, which means that we just have to
+	 * flush the log up to the latest LSN that touched the inode. If we have
+	 * concurrent fsync/fdatasync() calls, we need them to all block on the
+	 * log force before we clear the ili_fsync_fields field. This ensures
+	 * that we don't get a racing sync operation that does not wait for the
+	 * metadata to hit the journal before returning. If we race with
+	 * clearing the ili_fsync_fields, then all that will happen is the log
+	 * force will do nothing as the lsn will already be on disk. We can't
+	 * race with setting ili_fsync_fields because that is done under
+	 * XFS_ILOCK_EXCL, and that can't happen because we hold the lock shared
+	 * until after the ili_fsync_fields is cleared.
 	 */
 	 */
 	xfs_ilock(ip, XFS_ILOCK_SHARED);
 	xfs_ilock(ip, XFS_ILOCK_SHARED);
 	if (xfs_ipincount(ip)) {
 	if (xfs_ipincount(ip)) {
 		if (!datasync ||
 		if (!datasync ||
-		    (ip->i_itemp->ili_fields & ~XFS_ILOG_TIMESTAMP))
+		    (ip->i_itemp->ili_fsync_fields & ~XFS_ILOG_TIMESTAMP))
 			lsn = ip->i_itemp->ili_last_lsn;
 			lsn = ip->i_itemp->ili_last_lsn;
 	}
 	}
-	xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
 
-	if (lsn)
+	if (lsn) {
 		error = _xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, &log_flushed);
 		error = _xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, &log_flushed);
+		ip->i_itemp->ili_fsync_fields = 0;
+	}
+	xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
 
 	/*
 	/*
 	 * If we only have a single device, and the log force about was
 	 * If we only have a single device, and the log force about was

+ 2 - 0
fs/xfs/xfs_inode.c

@@ -2365,6 +2365,7 @@ retry:
 
 
 			iip->ili_last_fields = iip->ili_fields;
 			iip->ili_last_fields = iip->ili_fields;
 			iip->ili_fields = 0;
 			iip->ili_fields = 0;
+			iip->ili_fsync_fields = 0;
 			iip->ili_logged = 1;
 			iip->ili_logged = 1;
 			xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn,
 			xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn,
 						&iip->ili_item.li_lsn);
 						&iip->ili_item.li_lsn);
@@ -3560,6 +3561,7 @@ xfs_iflush_int(
 	 */
 	 */
 	iip->ili_last_fields = iip->ili_fields;
 	iip->ili_last_fields = iip->ili_fields;
 	iip->ili_fields = 0;
 	iip->ili_fields = 0;
+	iip->ili_fsync_fields = 0;
 	iip->ili_logged = 1;
 	iip->ili_logged = 1;
 
 
 	xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn,
 	xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn,

+ 1 - 0
fs/xfs/xfs_inode_item.c

@@ -719,6 +719,7 @@ xfs_iflush_abort(
 		 * attempted.
 		 * attempted.
 		 */
 		 */
 		iip->ili_fields = 0;
 		iip->ili_fields = 0;
+		iip->ili_fsync_fields = 0;
 	}
 	}
 	/*
 	/*
 	 * Release the inode's flush lock since we're done with it.
 	 * Release the inode's flush lock since we're done with it.

+ 1 - 0
fs/xfs/xfs_inode_item.h

@@ -34,6 +34,7 @@ typedef struct xfs_inode_log_item {
 	unsigned short		ili_logged;	   /* flushed logged data */
 	unsigned short		ili_logged;	   /* flushed logged data */
 	unsigned int		ili_last_fields;   /* fields when flushed */
 	unsigned int		ili_last_fields;   /* fields when flushed */
 	unsigned int		ili_fields;	   /* fields to be logged */
 	unsigned int		ili_fields;	   /* fields to be logged */
+	unsigned int		ili_fsync_fields;  /* logged since last fsync */
 } xfs_inode_log_item_t;
 } xfs_inode_log_item_t;
 
 
 static inline int xfs_inode_clean(xfs_inode_t *ip)
 static inline int xfs_inode_clean(xfs_inode_t *ip)

+ 13 - 2
fs/xfs/xfs_ioctl.c

@@ -40,6 +40,7 @@
 #include "xfs_symlink.h"
 #include "xfs_symlink.h"
 #include "xfs_trans.h"
 #include "xfs_trans.h"
 #include "xfs_pnfs.h"
 #include "xfs_pnfs.h"
+#include "xfs_acl.h"
 
 
 #include <linux/capability.h>
 #include <linux/capability.h>
 #include <linux/dcache.h>
 #include <linux/dcache.h>
@@ -482,6 +483,7 @@ xfs_attrmulti_attr_set(
 	__uint32_t		flags)
 	__uint32_t		flags)
 {
 {
 	unsigned char		*kbuf;
 	unsigned char		*kbuf;
+	int			error;
 
 
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		return -EPERM;
 		return -EPERM;
@@ -492,7 +494,11 @@ xfs_attrmulti_attr_set(
 	if (IS_ERR(kbuf))
 	if (IS_ERR(kbuf))
 		return PTR_ERR(kbuf);
 		return PTR_ERR(kbuf);
 
 
-	return xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
+	error = xfs_attr_set(XFS_I(inode), name, kbuf, len, flags);
+	if (!error)
+		xfs_forget_acl(inode, name, flags);
+	kfree(kbuf);
+	return error;
 }
 }
 
 
 int
 int
@@ -501,9 +507,14 @@ xfs_attrmulti_attr_remove(
 	unsigned char		*name,
 	unsigned char		*name,
 	__uint32_t		flags)
 	__uint32_t		flags)
 {
 {
+	int			error;
+
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 	if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 		return -EPERM;
 		return -EPERM;
-	return xfs_attr_remove(XFS_I(inode), name, flags);
+	error = xfs_attr_remove(XFS_I(inode), name, flags);
+	if (!error)
+		xfs_forget_acl(inode, name, flags);
+	return error;
 }
 }
 
 
 STATIC int
 STATIC int

+ 10 - 0
fs/xfs/xfs_mount.c

@@ -47,6 +47,16 @@ static DEFINE_MUTEX(xfs_uuid_table_mutex);
 static int xfs_uuid_table_size;
 static int xfs_uuid_table_size;
 static uuid_t *xfs_uuid_table;
 static uuid_t *xfs_uuid_table;
 
 
+void
+xfs_uuid_table_free(void)
+{
+	if (xfs_uuid_table_size == 0)
+		return;
+	kmem_free(xfs_uuid_table);
+	xfs_uuid_table = NULL;
+	xfs_uuid_table_size = 0;
+}
+
 /*
 /*
  * See if the UUID is unique among mounted XFS filesystems.
  * See if the UUID is unique among mounted XFS filesystems.
  * Mount fails if UUID is nil or a FS with the same UUID is already mounted.
  * Mount fails if UUID is nil or a FS with the same UUID is already mounted.

+ 1 - 0
fs/xfs/xfs_mount.h

@@ -313,6 +313,7 @@ typedef struct xfs_perag {
 	int		pagb_count;	/* pagb slots in use */
 	int		pagb_count;	/* pagb slots in use */
 } xfs_perag_t;
 } xfs_perag_t;
 
 
+extern void	xfs_uuid_table_free(void);
 extern int	xfs_log_sbcount(xfs_mount_t *);
 extern int	xfs_log_sbcount(xfs_mount_t *);
 extern __uint64_t xfs_default_resblks(xfs_mount_t *mp);
 extern __uint64_t xfs_default_resblks(xfs_mount_t *mp);
 extern int	xfs_mountfs(xfs_mount_t *mp);
 extern int	xfs_mountfs(xfs_mount_t *mp);

+ 1 - 0
fs/xfs/xfs_super.c

@@ -1925,6 +1925,7 @@ exit_xfs_fs(void)
 	xfs_mru_cache_uninit();
 	xfs_mru_cache_uninit();
 	xfs_destroy_workqueues();
 	xfs_destroy_workqueues();
 	xfs_destroy_zones();
 	xfs_destroy_zones();
+	xfs_uuid_table_free();
 }
 }
 
 
 module_init(init_xfs_fs);
 module_init(init_xfs_fs);

+ 1 - 0
fs/xfs/xfs_trans_ail.c

@@ -497,6 +497,7 @@ xfsaild(
 	long		tout = 0;	/* milliseconds */
 	long		tout = 0;	/* milliseconds */
 
 
 	current->flags |= PF_MEMALLOC;
 	current->flags |= PF_MEMALLOC;
+	set_freezable();
 
 
 	while (!kthread_should_stop()) {
 	while (!kthread_should_stop()) {
 		if (tout && tout <= 20)
 		if (tout && tout <= 20)

+ 9 - 0
fs/xfs/xfs_trans_inode.c

@@ -107,6 +107,15 @@ xfs_trans_log_inode(
 	ASSERT(ip->i_itemp != NULL);
 	ASSERT(ip->i_itemp != NULL);
 	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
 	ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
 
 
+	/*
+	 * Record the specific change for fdatasync optimisation. This
+	 * allows fdatasync to skip log forces for inodes that are only
+	 * timestamp dirty. We do this before the change count so that
+	 * the core being logged in this case does not impact on fdatasync
+	 * behaviour.
+	 */
+	ip->i_itemp->ili_fsync_fields |= flags;
+
 	/*
 	/*
 	 * First time we log the inode in a transaction, bump the inode change
 	 * First time we log the inode in a transaction, bump the inode change
 	 * counter if it is configured for this to occur. We don't use
 	 * counter if it is configured for this to occur. We don't use

+ 29 - 2
fs/xfs/xfs_xattr.c

@@ -53,11 +53,34 @@ xfs_xattr_get(struct dentry *dentry, const char *name,
 	return asize;
 	return asize;
 }
 }
 
 
+void
+xfs_forget_acl(
+	struct inode		*inode,
+	const char		*name,
+	int			xflags)
+{
+	/*
+	 * Invalidate any cached ACLs if the user has bypassed the ACL
+	 * interface. We don't validate the content whatsoever so it is caller
+	 * responsibility to provide data in valid format and ensure i_mode is
+	 * consistent.
+	 */
+	if (xflags & ATTR_ROOT) {
+#ifdef CONFIG_XFS_POSIX_ACL
+		if (!strcmp(name, SGI_ACL_FILE))
+			forget_cached_acl(inode, ACL_TYPE_ACCESS);
+		else if (!strcmp(name, SGI_ACL_DEFAULT))
+			forget_cached_acl(inode, ACL_TYPE_DEFAULT);
+#endif
+	}
+}
+
 static int
 static int
 xfs_xattr_set(struct dentry *dentry, const char *name, const void *value,
 xfs_xattr_set(struct dentry *dentry, const char *name, const void *value,
 		size_t size, int flags, int xflags)
 		size_t size, int flags, int xflags)
 {
 {
-	struct xfs_inode *ip = XFS_I(d_inode(dentry));
+	struct xfs_inode	*ip = XFS_I(d_inode(dentry));
+	int			error;
 
 
 	if (strcmp(name, "") == 0)
 	if (strcmp(name, "") == 0)
 		return -EINVAL;
 		return -EINVAL;
@@ -70,8 +93,12 @@ xfs_xattr_set(struct dentry *dentry, const char *name, const void *value,
 
 
 	if (!value)
 	if (!value)
 		return xfs_attr_remove(ip, (unsigned char *)name, xflags);
 		return xfs_attr_remove(ip, (unsigned char *)name, xflags);
-	return xfs_attr_set(ip, (unsigned char *)name,
+	error = xfs_attr_set(ip, (unsigned char *)name,
 				(void *)value, size, xflags);
 				(void *)value, size, xflags);
+	if (!error)
+		xfs_forget_acl(d_inode(dentry), name, xflags);
+
+	return error;
 }
 }
 
 
 static const struct xattr_handler xfs_xattr_user_handler = {
 static const struct xattr_handler xfs_xattr_user_handler = {