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