]> git.sesse.net Git - bcachefs-tools-debian/blob - libbcachefs/fs-ioctl.c
Update bcachefs sources to e99d29e402 bcachefs: zstd support, compression refactoring
[bcachefs-tools-debian] / libbcachefs / fs-ioctl.c
1 #ifndef NO_BCACHEFS_FS
2
3 #include "bcachefs.h"
4 #include "chardev.h"
5 #include "fs.h"
6 #include "fs-ioctl.h"
7 #include "quota.h"
8
9 #include <linux/compat.h>
10 #include <linux/mount.h>
11
12 #define FS_IOC_GOINGDOWN             _IOR('X', 125, __u32)
13
14 /* Inode flags: */
15
16 /* bcachefs inode flags -> vfs inode flags: */
17 static const unsigned bch_flags_to_vfs[] = {
18         [__BCH_INODE_SYNC]      = S_SYNC,
19         [__BCH_INODE_IMMUTABLE] = S_IMMUTABLE,
20         [__BCH_INODE_APPEND]    = S_APPEND,
21         [__BCH_INODE_NOATIME]   = S_NOATIME,
22 };
23
24 /* bcachefs inode flags -> FS_IOC_GETFLAGS: */
25 static const unsigned bch_flags_to_uflags[] = {
26         [__BCH_INODE_SYNC]      = FS_SYNC_FL,
27         [__BCH_INODE_IMMUTABLE] = FS_IMMUTABLE_FL,
28         [__BCH_INODE_APPEND]    = FS_APPEND_FL,
29         [__BCH_INODE_NODUMP]    = FS_NODUMP_FL,
30         [__BCH_INODE_NOATIME]   = FS_NOATIME_FL,
31 };
32
33 /* bcachefs inode flags -> FS_IOC_FSGETXATTR: */
34 static const unsigned bch_flags_to_xflags[] = {
35         [__BCH_INODE_SYNC]      = FS_XFLAG_SYNC,
36         [__BCH_INODE_IMMUTABLE] = FS_XFLAG_IMMUTABLE,
37         [__BCH_INODE_APPEND]    = FS_XFLAG_APPEND,
38         [__BCH_INODE_NODUMP]    = FS_XFLAG_NODUMP,
39         [__BCH_INODE_NOATIME]   = FS_XFLAG_NOATIME,
40         //[__BCH_INODE_PROJINHERIT] = FS_XFLAG_PROJINHERIT;
41 };
42
43 #define set_flags(_map, _in, _out)                                      \
44 do {                                                                    \
45         unsigned _i;                                                    \
46                                                                         \
47         for (_i = 0; _i < ARRAY_SIZE(_map); _i++)                       \
48                 if ((_in) & (1 << _i))                                  \
49                         (_out) |= _map[_i];                             \
50                 else                                                    \
51                         (_out) &= ~_map[_i];                            \
52 } while (0)
53
54 #define map_flags(_map, _in)                                            \
55 ({                                                                      \
56         unsigned _out = 0;                                              \
57                                                                         \
58         set_flags(_map, _in, _out);                                     \
59         _out;                                                           \
60 })
61
62 #define map_flags_rev(_map, _in)                                        \
63 ({                                                                      \
64         unsigned _i, _out = 0;                                          \
65                                                                         \
66         for (_i = 0; _i < ARRAY_SIZE(_map); _i++)                       \
67                 if ((_in) & _map[_i]) {                                 \
68                         (_out) |= 1 << _i;                              \
69                         (_in) &= ~_map[_i];                             \
70                 }                                                       \
71         (_out);                                                         \
72 })
73
74 #define map_defined(_map)                                               \
75 ({                                                                      \
76         unsigned _in = ~0;                                              \
77                                                                         \
78         map_flags_rev(_map, _in);                                       \
79 })
80
81 /* Set VFS inode flags from bcachefs inode: */
82 void bch2_inode_flags_to_vfs(struct bch_inode_info *inode)
83 {
84         set_flags(bch_flags_to_vfs, inode->ei_inode.bi_flags, inode->v.i_flags);
85 }
86
87 struct flags_set {
88         unsigned                mask;
89         unsigned                flags;
90 };
91
92 static int bch2_inode_flags_set(struct bch_inode_info *inode,
93                                 struct bch_inode_unpacked *bi,
94                                 void *p)
95 {
96         /*
97          * We're relying on btree locking here for exclusion with other ioctl
98          * calls - use the flags in the btree (@bi), not inode->i_flags:
99          */
100         struct flags_set *s = p;
101         unsigned newflags = s->flags;
102         unsigned oldflags = bi->bi_flags & s->mask;
103
104         if (((newflags ^ oldflags) & (BCH_INODE_APPEND|BCH_INODE_IMMUTABLE)) &&
105             !capable(CAP_LINUX_IMMUTABLE))
106                 return -EPERM;
107
108         if (!S_ISREG(inode->v.i_mode) &&
109             !S_ISDIR(inode->v.i_mode) &&
110             (newflags & (BCH_INODE_NODUMP|BCH_INODE_NOATIME)) != newflags)
111                 return -EINVAL;
112
113         bi->bi_flags &= ~s->mask;
114         bi->bi_flags |= newflags;
115         inode->v.i_ctime = current_time(&inode->v);
116         return 0;
117 }
118
119 static int bch2_ioc_getflags(struct bch_inode_info *inode, int __user *arg)
120 {
121         unsigned flags = map_flags(bch_flags_to_uflags, inode->ei_inode.bi_flags);
122
123         return put_user(flags, arg);
124 }
125
126 static int bch2_ioc_setflags(struct bch_fs *c,
127                              struct file *file,
128                              struct bch_inode_info *inode,
129                              void __user *arg)
130 {
131         struct flags_set s = { .mask = map_defined(bch_flags_to_uflags) };
132         unsigned uflags;
133         int ret;
134
135         if (get_user(uflags, (int __user *) arg))
136                 return -EFAULT;
137
138         s.flags = map_flags_rev(bch_flags_to_uflags, uflags);
139         if (uflags)
140                 return -EOPNOTSUPP;
141
142         ret = mnt_want_write_file(file);
143         if (ret)
144                 return ret;
145
146         inode_lock(&inode->v);
147         if (!inode_owner_or_capable(&inode->v)) {
148                 ret = -EACCES;
149                 goto setflags_out;
150         }
151
152         mutex_lock(&inode->ei_update_lock);
153         ret = __bch2_write_inode(c, inode, bch2_inode_flags_set, &s);
154
155         if (!ret)
156                 bch2_inode_flags_to_vfs(inode);
157         mutex_unlock(&inode->ei_update_lock);
158
159 setflags_out:
160         inode_unlock(&inode->v);
161         mnt_drop_write_file(file);
162         return ret;
163 }
164
165 static int bch2_ioc_fsgetxattr(struct bch_inode_info *inode,
166                                struct fsxattr __user *arg)
167 {
168         struct fsxattr fa = { 0 };
169
170         fa.fsx_xflags = map_flags(bch_flags_to_xflags, inode->ei_inode.bi_flags);
171         fa.fsx_projid = inode->ei_qid.q[QTYP_PRJ];
172
173         return copy_to_user(arg, &fa, sizeof(fa));
174 }
175
176 static int bch2_set_projid(struct bch_fs *c,
177                            struct bch_inode_info *inode,
178                            u32 projid)
179 {
180         struct bch_qid qid = inode->ei_qid;
181         int ret;
182
183         if (projid == inode->ei_qid.q[QTYP_PRJ])
184                 return 0;
185
186         qid.q[QTYP_PRJ] = projid;
187
188         ret = bch2_quota_transfer(c, 1 << QTYP_PRJ, qid, inode->ei_qid,
189                                   inode->v.i_blocks +
190                                   inode->ei_quota_reserved);
191         if (ret)
192                 return ret;
193
194         inode->ei_qid.q[QTYP_PRJ] = projid;
195         return 0;
196 }
197
198 static int bch2_ioc_fssetxattr(struct bch_fs *c,
199                                struct file *file,
200                                struct bch_inode_info *inode,
201                                struct fsxattr __user *arg)
202 {
203         struct flags_set s = { .mask = map_defined(bch_flags_to_xflags) };
204         struct fsxattr fa;
205         int ret;
206
207         if (copy_from_user(&fa, arg, sizeof(fa)))
208                 return -EFAULT;
209
210         s.flags = map_flags_rev(bch_flags_to_xflags, fa.fsx_xflags);
211         if (fa.fsx_xflags)
212                 return -EOPNOTSUPP;
213
214         ret = mnt_want_write_file(file);
215         if (ret)
216                 return ret;
217
218         inode_lock(&inode->v);
219         if (!inode_owner_or_capable(&inode->v)) {
220                 ret = -EACCES;
221                 goto err;
222         }
223
224         mutex_lock(&inode->ei_update_lock);
225         ret = bch2_set_projid(c, inode, fa.fsx_projid);
226         if (ret)
227                 goto err_unlock;
228
229         ret = __bch2_write_inode(c, inode, bch2_inode_flags_set, &s);
230         if (!ret)
231                 bch2_inode_flags_to_vfs(inode);
232 err_unlock:
233         mutex_unlock(&inode->ei_update_lock);
234 err:
235         inode_unlock(&inode->v);
236         mnt_drop_write_file(file);
237         return ret;
238 }
239
240 long bch2_fs_file_ioctl(struct file *file, unsigned cmd, unsigned long arg)
241 {
242         struct bch_inode_info *inode = file_bch_inode(file);
243         struct super_block *sb = inode->v.i_sb;
244         struct bch_fs *c = sb->s_fs_info;
245
246         switch (cmd) {
247         case FS_IOC_GETFLAGS:
248                 return bch2_ioc_getflags(inode, (int __user *) arg);
249
250         case FS_IOC_SETFLAGS:
251                 return bch2_ioc_setflags(c, file, inode, (int __user *) arg);
252
253         case FS_IOC_FSGETXATTR:
254                 return bch2_ioc_fsgetxattr(inode, (void __user *) arg);
255         case FS_IOC_FSSETXATTR:
256                 return bch2_ioc_fssetxattr(c, file, inode, (void __user *) arg);
257
258         case FS_IOC_GETVERSION:
259                 return -ENOTTY;
260         case FS_IOC_SETVERSION:
261                 return -ENOTTY;
262
263         case FS_IOC_GOINGDOWN:
264                 if (!capable(CAP_SYS_ADMIN))
265                         return -EPERM;
266
267                 down_write(&sb->s_umount);
268                 sb->s_flags |= MS_RDONLY;
269                 bch2_fs_emergency_read_only(c);
270                 up_write(&sb->s_umount);
271                 return 0;
272
273         default:
274                 return bch2_fs_ioctl(c, cmd, (void __user *) arg);
275         }
276 }
277
278 #ifdef CONFIG_COMPAT
279 long bch2_compat_fs_ioctl(struct file *file, unsigned cmd, unsigned long arg)
280 {
281         /* These are just misnamed, they actually get/put from/to user an int */
282         switch (cmd) {
283         case FS_IOC_GETFLAGS:
284                 cmd = FS_IOC_GETFLAGS;
285                 break;
286         case FS_IOC32_SETFLAGS:
287                 cmd = FS_IOC_SETFLAGS;
288                 break;
289         default:
290                 return -ENOIOCTLCMD;
291         }
292         return bch2_fs_file_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
293 }
294 #endif
295
296 #endif /* NO_BCACHEFS_FS */