]>
Commit | Line | Data |
---|---|---|
0b07de85 AG |
1 | /* |
2 | * Copyright 2004 Peter M. Jones <[email protected]> | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public Licens | |
15 | * along with this program; if not, write to the Free Software | |
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111- | |
17 | * | |
18 | */ | |
19 | ||
20 | #include <linux/list.h> | |
21 | #include <linux/genhd.h> | |
22 | #include <linux/spinlock.h> | |
23 | #include <linux/parser.h> | |
24 | #include <linux/capability.h> | |
25 | #include <linux/bitops.h> | |
26 | ||
27 | #include <scsi/scsi.h> | |
28 | #include <linux/cdrom.h> | |
29 | ||
30 | int blk_cmd_filter_verify_command(struct blk_scsi_cmd_filter *filter, | |
31 | unsigned char *cmd, mode_t *f_mode) | |
32 | { | |
33 | /* root can do any command. */ | |
34 | if (capable(CAP_SYS_RAWIO)) | |
35 | return 0; | |
36 | ||
37 | /* if there's no filter set, assume we're filtering everything out */ | |
38 | if (!filter) | |
39 | return -EPERM; | |
40 | ||
41 | /* Anybody who can open the device can do a read-safe command */ | |
42 | if (test_bit(cmd[0], filter->read_ok)) | |
43 | return 0; | |
44 | ||
45 | /* Write-safe commands require a writable open */ | |
46 | if (test_bit(cmd[0], filter->write_ok) && (*f_mode & FMODE_WRITE)) | |
47 | return 0; | |
48 | ||
49 | return -EPERM; | |
50 | } | |
51 | EXPORT_SYMBOL(blk_cmd_filter_verify_command); | |
52 | ||
53 | int blk_verify_command(struct file *file, unsigned char *cmd) | |
54 | { | |
55 | struct gendisk *disk; | |
56 | struct inode *inode; | |
57 | ||
58 | if (!file) | |
59 | return -EINVAL; | |
60 | ||
61 | inode = file->f_dentry->d_inode; | |
62 | if (!inode) | |
63 | return -EINVAL; | |
64 | ||
65 | disk = inode->i_bdev->bd_disk; | |
66 | ||
67 | return blk_cmd_filter_verify_command(&disk->cmd_filter, | |
68 | cmd, &file->f_mode); | |
69 | } | |
70 | EXPORT_SYMBOL(blk_verify_command); | |
71 | ||
72 | /* and now, the sysfs stuff */ | |
73 | static ssize_t rcf_cmds_show(struct blk_scsi_cmd_filter *filter, char *page, | |
74 | int rw) | |
75 | { | |
76 | char *npage = page; | |
77 | unsigned long *okbits; | |
78 | int i; | |
79 | ||
80 | if (rw == READ) | |
81 | okbits = filter->read_ok; | |
82 | else | |
83 | okbits = filter->write_ok; | |
84 | ||
85 | for (i = 0; i < BLK_SCSI_MAX_CMDS; i++) { | |
86 | if (test_bit(i, okbits)) { | |
87 | sprintf(npage, "%02x", i); | |
88 | npage += 2; | |
89 | if (i < BLK_SCSI_MAX_CMDS - 1) | |
90 | sprintf(npage++, " "); | |
91 | } | |
92 | } | |
93 | ||
94 | if (npage != page) | |
95 | npage += sprintf(npage, "\n"); | |
96 | ||
97 | return npage - page; | |
98 | } | |
99 | ||
100 | static ssize_t rcf_readcmds_show(struct blk_scsi_cmd_filter *filter, char *page) | |
101 | { | |
102 | return rcf_cmds_show(filter, page, READ); | |
103 | } | |
104 | ||
105 | static ssize_t rcf_writecmds_show(struct blk_scsi_cmd_filter *filter, | |
106 | char *page) | |
107 | { | |
108 | return rcf_cmds_show(filter, page, WRITE); | |
109 | } | |
110 | ||
111 | static ssize_t rcf_cmds_store(struct blk_scsi_cmd_filter *filter, | |
112 | const char *page, size_t count, int rw) | |
113 | { | |
114 | ssize_t ret = 0; | |
115 | unsigned long okbits[BLK_SCSI_CMD_PER_LONG], *target_okbits; | |
116 | int cmd, status, len; | |
117 | substring_t ss; | |
118 | ||
119 | memset(&okbits, 0, sizeof(okbits)); | |
120 | ||
121 | for (len = strlen(page); len > 0; len -= 3) { | |
122 | if (len < 2) | |
123 | break; | |
124 | ss.from = (char *) page + ret; | |
125 | ss.to = (char *) page + ret + 2; | |
126 | ret += 3; | |
127 | status = match_hex(&ss, &cmd); | |
128 | /* either of these cases means invalid input, so do nothing. */ | |
129 | if (status || cmd >= BLK_SCSI_MAX_CMDS) | |
130 | return -EINVAL; | |
131 | ||
132 | __set_bit(cmd, okbits); | |
133 | } | |
134 | ||
135 | if (rw == READ) | |
136 | target_okbits = filter->read_ok; | |
137 | else | |
138 | target_okbits = filter->write_ok; | |
139 | ||
140 | memmove(target_okbits, okbits, sizeof(okbits)); | |
141 | return count; | |
142 | } | |
143 | ||
144 | static ssize_t rcf_readcmds_store(struct blk_scsi_cmd_filter *filter, | |
145 | const char *page, size_t count) | |
146 | { | |
147 | return rcf_cmds_store(filter, page, count, READ); | |
148 | } | |
149 | ||
150 | static ssize_t rcf_writecmds_store(struct blk_scsi_cmd_filter *filter, | |
151 | const char *page, size_t count) | |
152 | { | |
153 | return rcf_cmds_store(filter, page, count, WRITE); | |
154 | } | |
155 | ||
156 | struct rcf_sysfs_entry { | |
157 | struct attribute attr; | |
158 | ssize_t (*show)(struct blk_scsi_cmd_filter *, char *); | |
159 | ssize_t (*store)(struct blk_scsi_cmd_filter *, const char *, size_t); | |
160 | }; | |
161 | ||
162 | static struct rcf_sysfs_entry rcf_readcmds_entry = { | |
163 | .attr = { .name = "read_table", .mode = S_IRUGO | S_IWUSR }, | |
164 | .show = rcf_readcmds_show, | |
165 | .store = rcf_readcmds_store, | |
166 | }; | |
167 | ||
168 | static struct rcf_sysfs_entry rcf_writecmds_entry = { | |
169 | .attr = {.name = "write_table", .mode = S_IRUGO | S_IWUSR }, | |
170 | .show = rcf_writecmds_show, | |
171 | .store = rcf_writecmds_store, | |
172 | }; | |
173 | ||
174 | static struct attribute *default_attrs[] = { | |
175 | &rcf_readcmds_entry.attr, | |
176 | &rcf_writecmds_entry.attr, | |
177 | NULL, | |
178 | }; | |
179 | ||
180 | #define to_rcf(atr) container_of((atr), struct rcf_sysfs_entry, attr) | |
181 | ||
182 | static ssize_t | |
183 | rcf_attr_show(struct kobject *kobj, struct attribute *attr, char *page) | |
184 | { | |
185 | struct rcf_sysfs_entry *entry = to_rcf(attr); | |
186 | struct blk_scsi_cmd_filter *filter; | |
187 | ||
188 | filter = container_of(kobj, struct blk_scsi_cmd_filter, kobj); | |
189 | if (entry->show) | |
190 | return entry->show(filter, page); | |
191 | ||
192 | return 0; | |
193 | } | |
194 | ||
195 | static ssize_t | |
196 | rcf_attr_store(struct kobject *kobj, struct attribute *attr, | |
197 | const char *page, size_t length) | |
198 | { | |
199 | struct rcf_sysfs_entry *entry = to_rcf(attr); | |
200 | struct blk_scsi_cmd_filter *filter; | |
201 | ||
202 | if (!capable(CAP_SYS_RAWIO)) | |
203 | return -EPERM; | |
204 | ||
205 | if (!entry->store) | |
206 | return -EINVAL; | |
207 | ||
208 | filter = container_of(kobj, struct blk_scsi_cmd_filter, kobj); | |
209 | return entry->store(filter, page, length); | |
210 | } | |
211 | ||
212 | static struct sysfs_ops rcf_sysfs_ops = { | |
213 | .show = rcf_attr_show, | |
214 | .store = rcf_attr_store, | |
215 | }; | |
216 | ||
217 | static struct kobj_type rcf_ktype = { | |
218 | .sysfs_ops = &rcf_sysfs_ops, | |
219 | .default_attrs = default_attrs, | |
220 | }; | |
221 | ||
222 | static void rcf_set_defaults(struct blk_scsi_cmd_filter *filter) | |
223 | { | |
224 | /* Basic read-only commands */ | |
225 | __set_bit(TEST_UNIT_READY, filter->read_ok); | |
226 | __set_bit(REQUEST_SENSE, filter->read_ok); | |
227 | __set_bit(READ_6, filter->read_ok); | |
228 | __set_bit(READ_10, filter->read_ok); | |
229 | __set_bit(READ_12, filter->read_ok); | |
230 | __set_bit(READ_16, filter->read_ok); | |
231 | __set_bit(READ_BUFFER, filter->read_ok); | |
232 | __set_bit(READ_DEFECT_DATA, filter->read_ok); | |
233 | __set_bit(READ_LONG, filter->read_ok); | |
234 | __set_bit(INQUIRY, filter->read_ok); | |
235 | __set_bit(MODE_SENSE, filter->read_ok); | |
236 | __set_bit(MODE_SENSE_10, filter->read_ok); | |
237 | __set_bit(LOG_SENSE, filter->read_ok); | |
238 | __set_bit(START_STOP, filter->read_ok); | |
239 | __set_bit(GPCMD_VERIFY_10, filter->read_ok); | |
240 | __set_bit(VERIFY_16, filter->read_ok); | |
241 | __set_bit(GPCMD_READ_BUFFER_CAPACITY, filter->read_ok); | |
242 | ||
243 | /* Audio CD commands */ | |
244 | __set_bit(GPCMD_PLAY_CD, filter->read_ok); | |
245 | __set_bit(GPCMD_PLAY_AUDIO_10, filter->read_ok); | |
246 | __set_bit(GPCMD_PLAY_AUDIO_MSF, filter->read_ok); | |
247 | __set_bit(GPCMD_PLAY_AUDIO_TI, filter->read_ok); | |
248 | __set_bit(GPCMD_PAUSE_RESUME, filter->read_ok); | |
249 | ||
250 | /* CD/DVD data reading */ | |
251 | __set_bit(GPCMD_READ_CD, filter->read_ok); | |
252 | __set_bit(GPCMD_READ_CD_MSF, filter->read_ok); | |
253 | __set_bit(GPCMD_READ_DISC_INFO, filter->read_ok); | |
254 | __set_bit(GPCMD_READ_CDVD_CAPACITY, filter->read_ok); | |
255 | __set_bit(GPCMD_READ_DVD_STRUCTURE, filter->read_ok); | |
256 | __set_bit(GPCMD_READ_HEADER, filter->read_ok); | |
257 | __set_bit(GPCMD_READ_TRACK_RZONE_INFO, filter->read_ok); | |
258 | __set_bit(GPCMD_READ_SUBCHANNEL, filter->read_ok); | |
259 | __set_bit(GPCMD_READ_TOC_PMA_ATIP, filter->read_ok); | |
260 | __set_bit(GPCMD_REPORT_KEY, filter->read_ok); | |
261 | __set_bit(GPCMD_SCAN, filter->read_ok); | |
262 | __set_bit(GPCMD_GET_CONFIGURATION, filter->read_ok); | |
263 | __set_bit(GPCMD_READ_FORMAT_CAPACITIES, filter->read_ok); | |
264 | __set_bit(GPCMD_GET_EVENT_STATUS_NOTIFICATION, filter->read_ok); | |
265 | __set_bit(GPCMD_GET_PERFORMANCE, filter->read_ok); | |
266 | __set_bit(GPCMD_SEEK, filter->read_ok); | |
267 | __set_bit(GPCMD_STOP_PLAY_SCAN, filter->read_ok); | |
268 | ||
269 | /* Basic writing commands */ | |
270 | __set_bit(WRITE_6, filter->write_ok); | |
271 | __set_bit(WRITE_10, filter->write_ok); | |
272 | __set_bit(WRITE_VERIFY, filter->write_ok); | |
273 | __set_bit(WRITE_12, filter->write_ok); | |
274 | __set_bit(WRITE_VERIFY_12, filter->write_ok); | |
275 | __set_bit(WRITE_16, filter->write_ok); | |
276 | __set_bit(WRITE_LONG, filter->write_ok); | |
277 | __set_bit(WRITE_LONG_2, filter->write_ok); | |
278 | __set_bit(ERASE, filter->write_ok); | |
279 | __set_bit(GPCMD_MODE_SELECT_10, filter->write_ok); | |
280 | __set_bit(MODE_SELECT, filter->write_ok); | |
281 | __set_bit(LOG_SELECT, filter->write_ok); | |
282 | __set_bit(GPCMD_BLANK, filter->write_ok); | |
283 | __set_bit(GPCMD_CLOSE_TRACK, filter->write_ok); | |
284 | __set_bit(GPCMD_FLUSH_CACHE, filter->write_ok); | |
285 | __set_bit(GPCMD_FORMAT_UNIT, filter->write_ok); | |
286 | __set_bit(GPCMD_REPAIR_RZONE_TRACK, filter->write_ok); | |
287 | __set_bit(GPCMD_RESERVE_RZONE_TRACK, filter->write_ok); | |
288 | __set_bit(GPCMD_SEND_DVD_STRUCTURE, filter->write_ok); | |
289 | __set_bit(GPCMD_SEND_EVENT, filter->write_ok); | |
290 | __set_bit(GPCMD_SEND_KEY, filter->write_ok); | |
291 | __set_bit(GPCMD_SEND_OPC, filter->write_ok); | |
292 | __set_bit(GPCMD_SEND_CUE_SHEET, filter->write_ok); | |
293 | __set_bit(GPCMD_SET_SPEED, filter->write_ok); | |
294 | __set_bit(GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, filter->write_ok); | |
295 | __set_bit(GPCMD_LOAD_UNLOAD, filter->write_ok); | |
296 | __set_bit(GPCMD_SET_STREAMING, filter->write_ok); | |
297 | } | |
298 | ||
299 | int blk_register_filter(struct gendisk *disk) | |
300 | { | |
301 | int ret; | |
302 | struct blk_scsi_cmd_filter *filter = &disk->cmd_filter; | |
303 | struct kobject *parent = kobject_get(disk->holder_dir->parent); | |
304 | ||
305 | if (!parent) | |
306 | return -ENODEV; | |
307 | ||
308 | ret = kobject_init_and_add(&filter->kobj, &rcf_ktype, parent, | |
309 | "%s", "cmd_filter"); | |
310 | ||
311 | if (ret < 0) | |
312 | return ret; | |
313 | ||
314 | rcf_set_defaults(filter); | |
315 | return 0; | |
316 | } | |
317 | ||
318 | void blk_unregister_filter(struct gendisk *disk) | |
319 | { | |
320 | struct blk_scsi_cmd_filter *filter = &disk->cmd_filter; | |
321 | ||
322 | kobject_put(&filter->kobj); | |
323 | kobject_put(disk->holder_dir->parent); | |
324 | } | |
325 |