-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy path0001-ubnt-Add-switchable-write-protection-functionality-f.patch
307 lines (287 loc) · 8.14 KB
/
0001-ubnt-Add-switchable-write-protection-functionality-f.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
From 94b868d30bf1daed9d2d88c34c88470fbb461bab Mon Sep 17 00:00:00 2001
From: Fabian Mastenbroek <mail.fabianm@gmail.com>
Date: Thu, 11 Feb 2021 11:28:51 +0000
Subject: [PATCH] ubnt: Add switchable write-protection functionality for disks
This commit adds certain hooks to the generic harddisk layer
of Linux to allow kernel modules to toggle write-protection
of attached disks.
Ubiquiti enables write-protection to prevent users from
modifying the boot and root disk, but disables it during
firmware upgrade.
---
block/genhd.c | 184 +++++++++++++++++++++++++++++++++++++++++-
include/linux/genhd.h | 18 +++++
2 files changed, 198 insertions(+), 4 deletions(-)
diff --git a/block/genhd.c b/block/genhd.c
index f5d12185d6..197921c2f2 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -43,6 +43,172 @@ static void disk_add_events(struct gendisk *disk);
static void disk_del_events(struct gendisk *disk);
static void disk_release_events(struct gendisk *disk);
+static LIST_HEAD(disk_hook_list);
+static DEFINE_MUTEX(disk_hook_lock);
+
+struct disk_event_hook {
+ gendisk_callback cb;
+ void *priv;
+};
+
+struct disk_hook_entry {
+ struct disk_event_hook hooks[DISK_EVENT_COUNT];
+ struct list_head list;
+};
+
+static void gendisk_event_hook_call(struct gendisk *disk, unsigned disk_event)
+{
+ struct disk_part_iter piter;
+ struct disk_hook_entry *entry;
+ struct hd_struct *part;
+
+ if (get_capacity(disk) == 0) {
+ return;
+ }
+
+ mutex_lock(&disk_hook_lock);
+ disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0);
+ while ((part = disk_part_iter_next(&piter))) {
+ list_for_each_entry (entry, &disk_hook_list, list) {
+ if (entry->hooks[disk_event].cb) {
+ entry->hooks[disk_event].cb(disk, part,
+ entry->hooks[disk_event].priv);
+ break;
+ }
+ }
+ }
+ disk_part_iter_exit(&piter);
+ mutex_unlock(&disk_hook_lock);
+}
+
+void gendisk_callback_for_each(gendisk_callback cb, void *priv)
+{
+ struct gendisk *disk = NULL;
+ struct class_dev_iter iter;
+ struct device *dev;
+ struct disk_part_iter piter;
+ struct hd_struct *part;
+
+ if(NULL == cb) {
+ WARN(1, "%s : cb can't be null\n", __func__);
+ return;
+ }
+
+ class_dev_iter_init(&iter, &block_class, NULL, &disk_type);
+ while ((dev = class_dev_iter_next(&iter))) {
+ disk = dev_to_disk(dev);
+
+ if (get_capacity(disk) == 0)
+ continue;
+
+ disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0);
+ while ((part = disk_part_iter_next(&piter))) {
+ cb(disk, part, priv);
+ }
+ disk_part_iter_exit(&piter);
+ }
+ class_dev_iter_exit(&iter);
+}
+EXPORT_SYMBOL(gendisk_callback_for_each);
+
+int gendisk_event_hook_reg(gendisk_callback cb, void *priv, unsigned disk_event)
+{
+ struct disk_hook_entry *entry;
+
+ if(NULL == cb) {
+ WARN(1, "%s : cb can't be null\n", __func__);
+ return -EINVAL;
+ }
+
+
+ if (disk_event >= DISK_EVENT_COUNT) {
+ WARN(1, "%s : unknowm disk event\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&disk_hook_lock);
+ list_for_each_entry (entry, &disk_hook_list, list) {
+ if (entry->hooks[disk_event].cb == cb) {
+ /* Update private */
+ entry->hooks[disk_event].priv = priv;
+ mutex_unlock(&disk_hook_lock);
+ return 0;
+ }
+ }
+
+ /* Add new hook */
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (NULL == entry) {
+ mutex_unlock(&disk_hook_lock);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&entry->list);
+ list_add_tail(&entry->list, &disk_hook_list);
+ entry->hooks[disk_event].cb = cb;
+ entry->hooks[disk_event].priv = priv;
+ mutex_unlock(&disk_hook_lock);
+ return 0;
+}
+EXPORT_SYMBOL(gendisk_event_hook_reg);
+
+void gendisk_event_hook_unreg(gendisk_callback cb, unsigned disk_event)
+{
+ struct disk_hook_entry *entry, *entry_temp;
+
+ if(NULL == cb) {
+ WARN(1, "%s : cb can't be null\n", __func__);
+ return;
+ }
+
+ if (disk_event >= DISK_EVENT_COUNT) {
+ WARN(1, "%s : unknowm disk event\n", __func__);
+ return;
+ }
+
+ mutex_lock(&disk_hook_lock);
+ list_for_each_entry_safe (entry, entry_temp, &disk_hook_list, list) {
+ if (entry->hooks[disk_event].cb == cb) {
+ list_del(&entry->list);
+ kfree(entry);
+ goto found;
+ }
+ }
+ printk("%s: event hook has not been found.\n", __func__);
+found:
+ mutex_unlock(&disk_hook_lock);
+}
+EXPORT_SYMBOL(gendisk_event_hook_unreg);
+
+void ubnt_set_disk_ro(struct gendisk *disk, int flag)
+{
+ if (NULL == disk) {
+ WARN(1, "%s : disk can't be null\n", __func__);
+ return;
+ }
+
+ disk->ubnt_readonly = flag;
+ set_disk_ro(disk, flag);
+}
+EXPORT_SYMBOL(ubnt_set_disk_ro);
+
+void ubnt_set_part_ro(struct gendisk *disk, int partno, int flag)
+{
+ struct hd_struct *part = disk_get_part(disk, partno);
+
+ if (NULL == part) {
+ WARN(1, "%s : part can't be null\n", __func__);
+ return;
+ }
+
+ part->ubnt_readonly = flag;
+ part->policy = flag;
+ disk_put_part(part);
+
+ return;
+}
+EXPORT_SYMBOL(ubnt_set_part_ro);
+
/**
* disk_get_part - get partition
* @disk: disk to look partition from
@@ -629,6 +795,8 @@ void add_disk(struct gendisk *disk)
WARN_ON(retval);
disk_add_events(disk);
+
+ gendisk_event_hook_call(disk, DISK_EVENT_ADD);
}
EXPORT_SYMBOL(add_disk);
@@ -907,7 +1075,7 @@ static int __init genhd_device_init(void)
return error;
bdev_map = kobj_map_init(base_probe, &block_class_lock);
blk_dev_init();
-
+ INIT_LIST_HEAD(&disk_hook_list);
register_blkdev(BLOCK_EXT_MAJOR, "blkext");
/* create top-level block dir */
@@ -1338,15 +1506,21 @@ static void set_disk_ro_uevent(struct gendisk *gd, int ro)
void set_device_ro(struct block_device *bdev, int flag)
{
- bdev->bd_part->policy = flag;
+ struct hd_struct *part = bdev->bd_part;
+
+ if(part->ubnt_readonly) {
+ flag = part->ubnt_readonly;
+ }
+ part->policy = flag;
}
EXPORT_SYMBOL(set_device_ro);
-void set_disk_ro(struct gendisk *disk, int flag)
+void set_disk_ro(struct gendisk *disk, int flag_in)
{
struct disk_part_iter piter;
struct hd_struct *part;
+ int flag = (disk->ubnt_readonly) ? disk->ubnt_readonly : flag_in;
if (disk->part0.policy != flag) {
set_disk_ro_uevent(disk, flag);
@@ -1355,7 +1529,7 @@ void set_disk_ro(struct gendisk *disk, int flag)
disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY);
while ((part = disk_part_iter_next(&piter)))
- part->policy = flag;
+ part->policy = (part->ubnt_readonly) ? part->ubnt_readonly : flag_in;
disk_part_iter_exit(&piter);
}
@@ -1475,6 +1649,7 @@ void disk_block_events(struct gendisk *disk)
mutex_unlock(&ev->block_mutex);
}
+EXPORT_SYMBOL(disk_block_events);
static void __disk_unblock_events(struct gendisk *disk, bool check_now)
{
@@ -1521,6 +1696,7 @@ void disk_unblock_events(struct gendisk *disk)
if (disk->ev)
__disk_unblock_events(disk, false);
}
+EXPORT_SYMBOL(disk_unblock_events);
/**
* disk_flush_events - schedule immediate event checking and flushing
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index ec274e0f4e..104beacc52 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -113,6 +113,7 @@ struct hd_struct {
struct device __dev;
struct kobject *holder_dir;
int policy, partno;
+ int ubnt_readonly;
struct partition_meta_info *info;
#ifdef CONFIG_FAIL_MAKE_REQUEST
int make_it_fail;
@@ -190,6 +191,7 @@ struct gendisk {
void *private_data;
int flags;
+ int ubnt_readonly;
struct device *driverfs_dev; // FIXME: remove
struct kobject *slave_dir;
@@ -416,6 +418,22 @@ static inline void free_part_info(struct hd_struct *part)
extern void part_round_stats(int cpu, struct hd_struct *part);
/* block/genhd.c */
+typedef void (*gendisk_callback)(struct gendisk *disk, struct hd_struct *part, void * priv);
+extern void gendisk_callback_for_each(gendisk_callback cb, void *priv);
+extern int gendisk_event_hook_reg(gendisk_callback cb, void *priv, unsigned disk_event);
+extern void gendisk_event_hook_unreg(gendisk_callback cb, unsigned disk_event);
+#define DISK_EVENTS(EVENT) \
+ EVENT(DISK_EVENT_ADD)
+
+enum {
+#define EVENT(_ename) _ename,
+ DISK_EVENTS(EVENT)
+#undef EVENT
+ DISK_EVENT_COUNT
+};
+extern void ubnt_set_part_ro(struct gendisk *disk, int partno, int flag);
+extern void ubnt_set_disk_ro(struct gendisk *disk, int flag);
+
extern void add_disk(struct gendisk *disk);
extern void del_gendisk(struct gendisk *gp);
extern struct gendisk *get_gendisk(dev_t dev, int *partno);
--
2.17.1