summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2018-01-21 16:20:53 -0500
committerKent Overstreet <kent.overstreet@gmail.com>2018-01-30 20:41:31 -0500
commit433e434abc508410c7444feaa8d4fdcf3ad1b47b (patch)
tree48dcdc8f2d4f5bf97418d5ec0eb22f5463937a57
parente3e4cb867b31d9c70c8f5855423f9a6be6679c23 (diff)
bcachefs: fix ioctls that set inode flags
inode flags that weren't being modified were being cleared
-rw-r--r--fs/bcachefs/fs-ioctl.c57
1 files changed, 35 insertions, 22 deletions
diff --git a/fs/bcachefs/fs-ioctl.c b/fs/bcachefs/fs-ioctl.c
index bfbe574dd697..2c1ecf7732cd 100644
--- a/fs/bcachefs/fs-ioctl.c
+++ b/fs/bcachefs/fs-ioctl.c
@@ -40,14 +40,23 @@ static const unsigned bch_flags_to_xflags[] = {
//[__BCH_INODE_PROJINHERIT] = FS_XFLAG_PROJINHERIT;
};
-#define map_flags(_map, _in) \
-({ \
- unsigned _i, _out = 0; \
+#define set_flags(_map, _in, _out) \
+do { \
+ unsigned _i; \
\
for (_i = 0; _i < ARRAY_SIZE(_map); _i++) \
if ((_in) & (1 << _i)) \
(_out) |= _map[_i]; \
- (_out); \
+ else \
+ (_out) &= ~_map[_i]; \
+} while (0)
+
+#define map_flags(_map, _in) \
+({ \
+ unsigned _out = 0; \
+ \
+ set_flags(_map, _in, _out); \
+ _out; \
})
#define map_flags_rev(_map, _in) \
@@ -62,16 +71,12 @@ static const unsigned bch_flags_to_xflags[] = {
(_out); \
})
-#define set_flags(_map, _in, _out) \
-do { \
- unsigned _i; \
+#define map_defined(_map) \
+({ \
+ unsigned _in = ~0; \
\
- for (_i = 0; _i < ARRAY_SIZE(_map); _i++) \
- if ((_in) & (1 << _i)) \
- (_out) |= _map[_i]; \
- else \
- (_out) &= ~_map[_i]; \
-} while (0)
+ map_flags_rev(_map, _in); \
+})
/* Set VFS inode flags from bcachefs inode: */
void bch2_inode_flags_to_vfs(struct bch_inode_info *inode)
@@ -79,6 +84,11 @@ void bch2_inode_flags_to_vfs(struct bch_inode_info *inode)
set_flags(bch_flags_to_vfs, inode->ei_inode.bi_flags, inode->v.i_flags);
}
+struct flags_set {
+ unsigned mask;
+ unsigned flags;
+};
+
static int bch2_inode_flags_set(struct bch_inode_info *inode,
struct bch_inode_unpacked *bi,
void *p)
@@ -87,8 +97,9 @@ static int bch2_inode_flags_set(struct bch_inode_info *inode,
* We're relying on btree locking here for exclusion with other ioctl
* calls - use the flags in the btree (@bi), not inode->i_flags:
*/
- unsigned newflags = *((unsigned *) p);
- unsigned oldflags = bi->bi_flags;
+ struct flags_set *s = p;
+ unsigned newflags = s->flags;
+ unsigned oldflags = bi->bi_flags & s->mask;
if (((newflags ^ oldflags) & (BCH_INODE_APPEND|BCH_INODE_IMMUTABLE)) &&
!capable(CAP_LINUX_IMMUTABLE))
@@ -99,7 +110,8 @@ static int bch2_inode_flags_set(struct bch_inode_info *inode,
(newflags & (BCH_INODE_NODUMP|BCH_INODE_NOATIME)) != newflags)
return -EINVAL;
- bi->bi_flags = newflags;
+ bi->bi_flags &= ~s->mask;
+ bi->bi_flags |= newflags;
inode->v.i_ctime = current_time(&inode->v);
return 0;
}
@@ -116,13 +128,14 @@ static int bch2_ioc_setflags(struct bch_fs *c,
struct bch_inode_info *inode,
void __user *arg)
{
- unsigned flags, uflags;
+ struct flags_set s = { .mask = map_defined(bch_flags_to_uflags) };
+ unsigned uflags;
int ret;
if (get_user(uflags, (int __user *) arg))
return -EFAULT;
- flags = map_flags_rev(bch_flags_to_uflags, uflags);
+ s.flags = map_flags_rev(bch_flags_to_uflags, uflags);
if (uflags)
return -EOPNOTSUPP;
@@ -137,7 +150,7 @@ static int bch2_ioc_setflags(struct bch_fs *c,
}
mutex_lock(&inode->ei_update_lock);
- ret = __bch2_write_inode(c, inode, bch2_inode_flags_set, &flags);
+ ret = __bch2_write_inode(c, inode, bch2_inode_flags_set, &s);
if (!ret)
bch2_inode_flags_to_vfs(inode);
@@ -187,14 +200,14 @@ static int bch2_ioc_fssetxattr(struct bch_fs *c,
struct bch_inode_info *inode,
struct fsxattr __user *arg)
{
+ struct flags_set s = { .mask = map_defined(bch_flags_to_xflags) };
struct fsxattr fa;
- unsigned flags;
int ret;
if (copy_from_user(&fa, arg, sizeof(fa)))
return -EFAULT;
- flags = map_flags_rev(bch_flags_to_xflags, fa.fsx_xflags);
+ s.flags = map_flags_rev(bch_flags_to_xflags, fa.fsx_xflags);
if (fa.fsx_xflags)
return -EOPNOTSUPP;
@@ -213,7 +226,7 @@ static int bch2_ioc_fssetxattr(struct bch_fs *c,
if (ret)
goto err_unlock;
- ret = __bch2_write_inode(c, inode, bch2_inode_flags_set, &flags);
+ ret = __bch2_write_inode(c, inode, bch2_inode_flags_set, &s);
if (!ret)
bch2_inode_flags_to_vfs(inode);
err_unlock: