1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * linux/fs/ext2/ioctl.c |
4 | * |
5 | * Copyright (C) 1993, 1994, 1995 |
6 | * Remy Card (card@masi.ibp.fr) |
7 | * Laboratoire MASI - Institut Blaise Pascal |
8 | * Universite Pierre et Marie Curie (Paris VI) |
9 | */ |
10 | |
11 | #include "ext2.h" |
12 | #include <linux/capability.h> |
13 | #include <linux/time.h> |
14 | #include <linux/sched.h> |
15 | #include <linux/compat.h> |
16 | #include <linux/mount.h> |
17 | #include <asm/current.h> |
18 | #include <linux/uaccess.h> |
19 | #include <linux/fileattr.h> |
20 | |
21 | int ext2_fileattr_get(struct dentry *dentry, struct fileattr *fa) |
22 | { |
23 | struct ext2_inode_info *ei = EXT2_I(inode: d_inode(dentry)); |
24 | |
25 | fileattr_fill_flags(fa, flags: ei->i_flags & EXT2_FL_USER_VISIBLE); |
26 | |
27 | return 0; |
28 | } |
29 | |
30 | int ext2_fileattr_set(struct mnt_idmap *idmap, |
31 | struct dentry *dentry, struct fileattr *fa) |
32 | { |
33 | struct inode *inode = d_inode(dentry); |
34 | struct ext2_inode_info *ei = EXT2_I(inode); |
35 | |
36 | if (fileattr_has_fsx(fa)) |
37 | return -EOPNOTSUPP; |
38 | |
39 | /* Is it quota file? Do not allow user to mess with it */ |
40 | if (IS_NOQUOTA(inode)) |
41 | return -EPERM; |
42 | |
43 | ei->i_flags = (ei->i_flags & ~EXT2_FL_USER_MODIFIABLE) | |
44 | (fa->flags & EXT2_FL_USER_MODIFIABLE); |
45 | |
46 | ext2_set_inode_flags(inode); |
47 | inode_set_ctime_current(inode); |
48 | mark_inode_dirty(inode); |
49 | |
50 | return 0; |
51 | } |
52 | |
53 | |
54 | long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
55 | { |
56 | struct inode *inode = file_inode(f: filp); |
57 | struct ext2_inode_info *ei = EXT2_I(inode); |
58 | unsigned short rsv_window_size; |
59 | int ret; |
60 | |
61 | ext2_debug ("cmd = %u, arg = %lu\n" , cmd, arg); |
62 | |
63 | switch (cmd) { |
64 | case EXT2_IOC_GETVERSION: |
65 | return put_user(inode->i_generation, (int __user *) arg); |
66 | case EXT2_IOC_SETVERSION: { |
67 | __u32 generation; |
68 | |
69 | if (!inode_owner_or_capable(idmap: &nop_mnt_idmap, inode)) |
70 | return -EPERM; |
71 | ret = mnt_want_write_file(file: filp); |
72 | if (ret) |
73 | return ret; |
74 | if (get_user(generation, (int __user *) arg)) { |
75 | ret = -EFAULT; |
76 | goto setversion_out; |
77 | } |
78 | |
79 | inode_lock(inode); |
80 | inode_set_ctime_current(inode); |
81 | inode->i_generation = generation; |
82 | inode_unlock(inode); |
83 | |
84 | mark_inode_dirty(inode); |
85 | setversion_out: |
86 | mnt_drop_write_file(file: filp); |
87 | return ret; |
88 | } |
89 | case EXT2_IOC_GETRSVSZ: |
90 | if (test_opt(inode->i_sb, RESERVATION) |
91 | && S_ISREG(inode->i_mode) |
92 | && ei->i_block_alloc_info) { |
93 | rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size; |
94 | return put_user(rsv_window_size, (int __user *)arg); |
95 | } |
96 | return -ENOTTY; |
97 | case EXT2_IOC_SETRSVSZ: { |
98 | |
99 | if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) |
100 | return -ENOTTY; |
101 | |
102 | if (!inode_owner_or_capable(idmap: &nop_mnt_idmap, inode)) |
103 | return -EACCES; |
104 | |
105 | if (get_user(rsv_window_size, (int __user *)arg)) |
106 | return -EFAULT; |
107 | |
108 | ret = mnt_want_write_file(file: filp); |
109 | if (ret) |
110 | return ret; |
111 | |
112 | if (rsv_window_size > EXT2_MAX_RESERVE_BLOCKS) |
113 | rsv_window_size = EXT2_MAX_RESERVE_BLOCKS; |
114 | |
115 | /* |
116 | * need to allocate reservation structure for this inode |
117 | * before set the window size |
118 | */ |
119 | /* |
120 | * XXX What lock should protect the rsv_goal_size? |
121 | * Accessed in ext2_get_block only. ext3 uses i_truncate. |
122 | */ |
123 | mutex_lock(&ei->truncate_mutex); |
124 | if (!ei->i_block_alloc_info) |
125 | ext2_init_block_alloc_info(inode); |
126 | |
127 | if (ei->i_block_alloc_info){ |
128 | struct ext2_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node; |
129 | rsv->rsv_goal_size = rsv_window_size; |
130 | } else { |
131 | ret = -ENOMEM; |
132 | } |
133 | |
134 | mutex_unlock(lock: &ei->truncate_mutex); |
135 | mnt_drop_write_file(file: filp); |
136 | return ret; |
137 | } |
138 | default: |
139 | return -ENOTTY; |
140 | } |
141 | } |
142 | |
143 | #ifdef CONFIG_COMPAT |
144 | long ext2_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
145 | { |
146 | /* These are just misnamed, they actually get/put from/to user an int */ |
147 | switch (cmd) { |
148 | case EXT2_IOC32_GETVERSION: |
149 | cmd = EXT2_IOC_GETVERSION; |
150 | break; |
151 | case EXT2_IOC32_SETVERSION: |
152 | cmd = EXT2_IOC_SETVERSION; |
153 | break; |
154 | default: |
155 | return -ENOIOCTLCMD; |
156 | } |
157 | return ext2_ioctl(filp: file, cmd, arg: (unsigned long) compat_ptr(uptr: arg)); |
158 | } |
159 | #endif |
160 | |