GNU Linux-libre 5.19-rc6-gnu
[releases.git] / fs / jfs / ioctl.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * linux/fs/jfs/ioctl.c
4  *
5  * Copyright (C) 2006 Herbert Poetzl
6  * adapted from Remy Card's ext2/ioctl.c
7  */
8
9 #include <linux/fs.h>
10 #include <linux/ctype.h>
11 #include <linux/capability.h>
12 #include <linux/mount.h>
13 #include <linux/time.h>
14 #include <linux/sched.h>
15 #include <linux/blkdev.h>
16 #include <asm/current.h>
17 #include <linux/uaccess.h>
18 #include <linux/fileattr.h>
19
20 #include "jfs_filsys.h"
21 #include "jfs_debug.h"
22 #include "jfs_incore.h"
23 #include "jfs_dinode.h"
24 #include "jfs_inode.h"
25 #include "jfs_dmap.h"
26 #include "jfs_discard.h"
27
28 static struct {
29         long jfs_flag;
30         long ext2_flag;
31 } jfs_map[] = {
32         {JFS_NOATIME_FL,        FS_NOATIME_FL},
33         {JFS_DIRSYNC_FL,        FS_DIRSYNC_FL},
34         {JFS_SYNC_FL,           FS_SYNC_FL},
35         {JFS_SECRM_FL,          FS_SECRM_FL},
36         {JFS_UNRM_FL,           FS_UNRM_FL},
37         {JFS_APPEND_FL,         FS_APPEND_FL},
38         {JFS_IMMUTABLE_FL,      FS_IMMUTABLE_FL},
39         {0, 0},
40 };
41
42 static long jfs_map_ext2(unsigned long flags, int from)
43 {
44         int index=0;
45         long mapped=0;
46
47         while (jfs_map[index].jfs_flag) {
48                 if (from) {
49                         if (jfs_map[index].ext2_flag & flags)
50                                 mapped |= jfs_map[index].jfs_flag;
51                 } else {
52                         if (jfs_map[index].jfs_flag & flags)
53                                 mapped |= jfs_map[index].ext2_flag;
54                 }
55                 index++;
56         }
57         return mapped;
58 }
59
60 int jfs_fileattr_get(struct dentry *dentry, struct fileattr *fa)
61 {
62         struct jfs_inode_info *jfs_inode = JFS_IP(d_inode(dentry));
63         unsigned int flags = jfs_inode->mode2 & JFS_FL_USER_VISIBLE;
64
65         if (d_is_special(dentry))
66                 return -ENOTTY;
67
68         fileattr_fill_flags(fa, jfs_map_ext2(flags, 0));
69
70         return 0;
71 }
72
73 int jfs_fileattr_set(struct user_namespace *mnt_userns,
74                      struct dentry *dentry, struct fileattr *fa)
75 {
76         struct inode *inode = d_inode(dentry);
77         struct jfs_inode_info *jfs_inode = JFS_IP(inode);
78         unsigned int flags;
79
80         if (d_is_special(dentry))
81                 return -ENOTTY;
82
83         if (fileattr_has_fsx(fa))
84                 return -EOPNOTSUPP;
85
86         flags = jfs_map_ext2(fa->flags, 1);
87         if (!S_ISDIR(inode->i_mode))
88                 flags &= ~JFS_DIRSYNC_FL;
89
90         /* Is it quota file? Do not allow user to mess with it */
91         if (IS_NOQUOTA(inode))
92                 return -EPERM;
93
94         flags = flags & JFS_FL_USER_MODIFIABLE;
95         flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
96         jfs_inode->mode2 = flags;
97
98         jfs_set_inode_flags(inode);
99         inode->i_ctime = current_time(inode);
100         mark_inode_dirty(inode);
101
102         return 0;
103 }
104
105 long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
106 {
107         struct inode *inode = file_inode(filp);
108
109         switch (cmd) {
110         case FITRIM:
111         {
112                 struct super_block *sb = inode->i_sb;
113                 struct fstrim_range range;
114                 s64 ret = 0;
115
116                 if (!capable(CAP_SYS_ADMIN))
117                         return -EPERM;
118
119                 if (!bdev_max_discard_sectors(sb->s_bdev)) {
120                         jfs_warn("FITRIM not supported on device");
121                         return -EOPNOTSUPP;
122                 }
123
124                 if (copy_from_user(&range, (struct fstrim_range __user *)arg,
125                     sizeof(range)))
126                         return -EFAULT;
127
128                 range.minlen = max_t(unsigned int, range.minlen,
129                                      bdev_discard_granularity(sb->s_bdev));
130
131                 ret = jfs_ioc_trim(inode, &range);
132                 if (ret < 0)
133                         return ret;
134
135                 if (copy_to_user((struct fstrim_range __user *)arg, &range,
136                     sizeof(range)))
137                         return -EFAULT;
138
139                 return 0;
140         }
141
142         default:
143                 return -ENOTTY;
144         }
145 }