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