hc
2024-05-10 23fa18eaa71266feff7ba8d83022d9e1cc83c65a
kernel/fs/adfs/dir.c
....@@ -1,208 +1,411 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /*
23 * linux/fs/adfs/dir.c
34 *
45 * Copyright (C) 1999-2000 Russell King
56 *
6
- * This program is free software; you can redistribute it and/or modify
7
- * it under the terms of the GNU General Public License version 2 as
8
- * published by the Free Software Foundation.
9
- *
107 * Common directory handling for ADFS
118 */
9
+#include <linux/slab.h>
1210 #include "adfs.h"
1311
1412 /*
1513 * For future. This should probably be per-directory.
1614 */
17
-static DEFINE_RWLOCK(adfs_dir_lock);
15
+static DECLARE_RWSEM(adfs_dir_rwsem);
1816
19
-static int
20
-adfs_readdir(struct file *file, struct dir_context *ctx)
17
+int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
18
+ size_t len)
19
+{
20
+ struct super_block *sb = dir->sb;
21
+ unsigned int index, remain;
22
+
23
+ index = offset >> sb->s_blocksize_bits;
24
+ offset &= sb->s_blocksize - 1;
25
+ remain = sb->s_blocksize - offset;
26
+ if (index + (remain < len) >= dir->nr_buffers)
27
+ return -EINVAL;
28
+
29
+ if (remain < len) {
30
+ memcpy(dst, dir->bhs[index]->b_data + offset, remain);
31
+ dst += remain;
32
+ len -= remain;
33
+ index += 1;
34
+ offset = 0;
35
+ }
36
+
37
+ memcpy(dst, dir->bhs[index]->b_data + offset, len);
38
+
39
+ return 0;
40
+}
41
+
42
+int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
43
+ size_t len)
44
+{
45
+ struct super_block *sb = dir->sb;
46
+ unsigned int index, remain;
47
+
48
+ index = offset >> sb->s_blocksize_bits;
49
+ offset &= sb->s_blocksize - 1;
50
+ remain = sb->s_blocksize - offset;
51
+ if (index + (remain < len) >= dir->nr_buffers)
52
+ return -EINVAL;
53
+
54
+ if (remain < len) {
55
+ memcpy(dir->bhs[index]->b_data + offset, src, remain);
56
+ src += remain;
57
+ len -= remain;
58
+ index += 1;
59
+ offset = 0;
60
+ }
61
+
62
+ memcpy(dir->bhs[index]->b_data + offset, src, len);
63
+
64
+ return 0;
65
+}
66
+
67
+static void __adfs_dir_cleanup(struct adfs_dir *dir)
68
+{
69
+ dir->nr_buffers = 0;
70
+
71
+ if (dir->bhs != dir->bh)
72
+ kfree(dir->bhs);
73
+ dir->bhs = NULL;
74
+ dir->sb = NULL;
75
+}
76
+
77
+void adfs_dir_relse(struct adfs_dir *dir)
78
+{
79
+ unsigned int i;
80
+
81
+ for (i = 0; i < dir->nr_buffers; i++)
82
+ brelse(dir->bhs[i]);
83
+
84
+ __adfs_dir_cleanup(dir);
85
+}
86
+
87
+static void adfs_dir_forget(struct adfs_dir *dir)
88
+{
89
+ unsigned int i;
90
+
91
+ for (i = 0; i < dir->nr_buffers; i++)
92
+ bforget(dir->bhs[i]);
93
+
94
+ __adfs_dir_cleanup(dir);
95
+}
96
+
97
+int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
98
+ unsigned int size, struct adfs_dir *dir)
99
+{
100
+ struct buffer_head **bhs;
101
+ unsigned int i, num;
102
+ int block;
103
+
104
+ num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits;
105
+ if (num > ARRAY_SIZE(dir->bh)) {
106
+ /* We only allow one extension */
107
+ if (dir->bhs != dir->bh)
108
+ return -EINVAL;
109
+
110
+ bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL);
111
+ if (!bhs)
112
+ return -ENOMEM;
113
+
114
+ if (dir->nr_buffers)
115
+ memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs));
116
+
117
+ dir->bhs = bhs;
118
+ }
119
+
120
+ for (i = dir->nr_buffers; i < num; i++) {
121
+ block = __adfs_block_map(sb, indaddr, i);
122
+ if (!block) {
123
+ adfs_error(sb, "dir %06x has a hole at offset %u",
124
+ indaddr, i);
125
+ goto error;
126
+ }
127
+
128
+ dir->bhs[i] = sb_bread(sb, block);
129
+ if (!dir->bhs[i]) {
130
+ adfs_error(sb,
131
+ "dir %06x failed read at offset %u, mapped block 0x%08x",
132
+ indaddr, i, block);
133
+ goto error;
134
+ }
135
+
136
+ dir->nr_buffers++;
137
+ }
138
+ return 0;
139
+
140
+error:
141
+ adfs_dir_relse(dir);
142
+
143
+ return -EIO;
144
+}
145
+
146
+static int adfs_dir_read(struct super_block *sb, u32 indaddr,
147
+ unsigned int size, struct adfs_dir *dir)
148
+{
149
+ dir->sb = sb;
150
+ dir->bhs = dir->bh;
151
+ dir->nr_buffers = 0;
152
+
153
+ return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
154
+}
155
+
156
+static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
157
+ struct adfs_dir *dir)
158
+{
159
+ int ret;
160
+
161
+ ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);
162
+ if (ret)
163
+ return ret;
164
+
165
+ if (ADFS_I(inode)->parent_id != dir->parent_id) {
166
+ adfs_error(sb,
167
+ "parent directory id changed under me! (%06x but got %06x)\n",
168
+ ADFS_I(inode)->parent_id, dir->parent_id);
169
+ adfs_dir_relse(dir);
170
+ ret = -EIO;
171
+ }
172
+
173
+ return ret;
174
+}
175
+
176
+static void adfs_dir_mark_dirty(struct adfs_dir *dir)
177
+{
178
+ unsigned int i;
179
+
180
+ /* Mark the buffers dirty */
181
+ for (i = 0; i < dir->nr_buffers; i++)
182
+ mark_buffer_dirty(dir->bhs[i]);
183
+}
184
+
185
+static int adfs_dir_sync(struct adfs_dir *dir)
186
+{
187
+ int err = 0;
188
+ int i;
189
+
190
+ for (i = dir->nr_buffers - 1; i >= 0; i--) {
191
+ struct buffer_head *bh = dir->bhs[i];
192
+ sync_dirty_buffer(bh);
193
+ if (buffer_req(bh) && !buffer_uptodate(bh))
194
+ err = -EIO;
195
+ }
196
+
197
+ return err;
198
+}
199
+
200
+void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
201
+{
202
+ unsigned int dots, i;
203
+
204
+ /*
205
+ * RISC OS allows the use of '/' in directory entry names, so we need
206
+ * to fix these up. '/' is typically used for FAT compatibility to
207
+ * represent '.', so do the same conversion here. In any case, '.'
208
+ * will never be in a RISC OS name since it is used as the pathname
209
+ * separator. Handle the case where we may generate a '.' or '..'
210
+ * name, replacing the first character with '^' (the RISC OS "parent
211
+ * directory" character.)
212
+ */
213
+ for (i = dots = 0; i < obj->name_len; i++)
214
+ if (obj->name[i] == '/') {
215
+ obj->name[i] = '.';
216
+ dots++;
217
+ }
218
+
219
+ if (obj->name_len <= 2 && dots == obj->name_len)
220
+ obj->name[0] = '^';
221
+
222
+ /*
223
+ * If the object is a file, and the user requested the ,xyz hex
224
+ * filetype suffix to the name, check the filetype and append.
225
+ */
226
+ if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {
227
+ u16 filetype = adfs_filetype(obj->loadaddr);
228
+
229
+ if (filetype != ADFS_FILETYPE_NONE) {
230
+ obj->name[obj->name_len++] = ',';
231
+ obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
232
+ obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
233
+ obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
234
+ }
235
+ }
236
+}
237
+
238
+static int adfs_iterate(struct file *file, struct dir_context *ctx)
21239 {
22240 struct inode *inode = file_inode(file);
23241 struct super_block *sb = inode->i_sb;
24242 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
25
- struct object_info obj;
26243 struct adfs_dir dir;
27
- int ret = 0;
244
+ int ret;
28245
29
- if (ctx->pos >> 32)
30
- return 0;
31
-
32
- ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
246
+ down_read(&adfs_dir_rwsem);
247
+ ret = adfs_dir_read_inode(sb, inode, &dir);
33248 if (ret)
34
- return ret;
249
+ goto unlock;
35250
36251 if (ctx->pos == 0) {
37252 if (!dir_emit_dot(file, ctx))
38
- goto free_out;
253
+ goto unlock_relse;
39254 ctx->pos = 1;
40255 }
41256 if (ctx->pos == 1) {
42257 if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
43
- goto free_out;
258
+ goto unlock_relse;
44259 ctx->pos = 2;
45260 }
46261
47
- read_lock(&adfs_dir_lock);
262
+ ret = ops->iterate(&dir, ctx);
48263
49
- ret = ops->setpos(&dir, ctx->pos - 2);
50
- if (ret)
51
- goto unlock_out;
52
- while (ops->getnext(&dir, &obj) == 0) {
53
- if (!dir_emit(ctx, obj.name, obj.name_len,
54
- obj.file_id, DT_UNKNOWN))
55
- break;
56
- ctx->pos++;
57
- }
264
+unlock_relse:
265
+ up_read(&adfs_dir_rwsem);
266
+ adfs_dir_relse(&dir);
267
+ return ret;
58268
59
-unlock_out:
60
- read_unlock(&adfs_dir_lock);
61
-
62
-free_out:
63
- ops->free(&dir);
269
+unlock:
270
+ up_read(&adfs_dir_rwsem);
64271 return ret;
65272 }
66273
67274 int
68275 adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
69276 {
70
- int ret = -EINVAL;
71
-#ifdef CONFIG_ADFS_FS_RW
72
- const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
73
- struct adfs_dir dir;
74
-
75
- printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
76
- obj->file_id, obj->parent_id);
77
-
78
- if (!ops->update) {
79
- ret = -EINVAL;
80
- goto out;
81
- }
82
-
83
- ret = ops->read(sb, obj->parent_id, 0, &dir);
84
- if (ret)
85
- goto out;
86
-
87
- write_lock(&adfs_dir_lock);
88
- ret = ops->update(&dir, obj);
89
- write_unlock(&adfs_dir_lock);
90
-
91
- if (wait) {
92
- int err = ops->sync(&dir);
93
- if (!ret)
94
- ret = err;
95
- }
96
-
97
- ops->free(&dir);
98
-out:
99
-#endif
100
- return ret;
101
-}
102
-
103
-static int
104
-adfs_match(const struct qstr *name, struct object_info *obj)
105
-{
106
- int i;
107
-
108
- if (name->len != obj->name_len)
109
- return 0;
110
-
111
- for (i = 0; i < name->len; i++) {
112
- char c1, c2;
113
-
114
- c1 = name->name[i];
115
- c2 = obj->name[i];
116
-
117
- if (c1 >= 'A' && c1 <= 'Z')
118
- c1 += 'a' - 'A';
119
- if (c2 >= 'A' && c2 <= 'Z')
120
- c2 += 'a' - 'A';
121
-
122
- if (c1 != c2)
123
- return 0;
124
- }
125
- return 1;
126
-}
127
-
128
-static int
129
-adfs_dir_lookup_byname(struct inode *inode, const struct qstr *name, struct object_info *obj)
130
-{
131
- struct super_block *sb = inode->i_sb;
132277 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
133278 struct adfs_dir dir;
134279 int ret;
135280
136
- ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
281
+ if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
282
+ return -EINVAL;
283
+
284
+ if (!ops->update)
285
+ return -EINVAL;
286
+
287
+ down_write(&adfs_dir_rwsem);
288
+ ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
137289 if (ret)
138
- goto out;
290
+ goto unlock;
139291
140
- if (ADFS_I(inode)->parent_id != dir.parent_id) {
141
- adfs_error(sb, "parent directory changed under me! (%lx but got %x)\n",
142
- ADFS_I(inode)->parent_id, dir.parent_id);
143
- ret = -EIO;
144
- goto free_out;
145
- }
292
+ ret = ops->update(&dir, obj);
293
+ if (ret)
294
+ goto forget;
146295
147
- obj->parent_id = inode->i_ino;
296
+ ret = ops->commit(&dir);
297
+ if (ret)
298
+ goto forget;
299
+ up_write(&adfs_dir_rwsem);
148300
149
- read_lock(&adfs_dir_lock);
301
+ adfs_dir_mark_dirty(&dir);
302
+
303
+ if (wait)
304
+ ret = adfs_dir_sync(&dir);
305
+
306
+ adfs_dir_relse(&dir);
307
+ return ret;
308
+
309
+ /*
310
+ * If the updated failed because the entry wasn't found, we can
311
+ * just release the buffers. If it was any other error, forget
312
+ * the dirtied buffers so they aren't written back to the media.
313
+ */
314
+forget:
315
+ if (ret == -ENOENT)
316
+ adfs_dir_relse(&dir);
317
+ else
318
+ adfs_dir_forget(&dir);
319
+unlock:
320
+ up_write(&adfs_dir_rwsem);
321
+
322
+ return ret;
323
+}
324
+
325
+static unsigned char adfs_tolower(unsigned char c)
326
+{
327
+ if (c >= 'A' && c <= 'Z')
328
+ c += 'a' - 'A';
329
+ return c;
330
+}
331
+
332
+static int __adfs_compare(const unsigned char *qstr, u32 qlen,
333
+ const char *str, u32 len)
334
+{
335
+ u32 i;
336
+
337
+ if (qlen != len)
338
+ return 1;
339
+
340
+ for (i = 0; i < qlen; i++)
341
+ if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
342
+ return 1;
343
+
344
+ return 0;
345
+}
346
+
347
+static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
348
+ struct object_info *obj)
349
+{
350
+ struct super_block *sb = inode->i_sb;
351
+ const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
352
+ const unsigned char *name;
353
+ struct adfs_dir dir;
354
+ u32 name_len;
355
+ int ret;
356
+
357
+ down_read(&adfs_dir_rwsem);
358
+ ret = adfs_dir_read_inode(sb, inode, &dir);
359
+ if (ret)
360
+ goto unlock;
150361
151362 ret = ops->setpos(&dir, 0);
152363 if (ret)
153
- goto unlock_out;
364
+ goto unlock_relse;
154365
155366 ret = -ENOENT;
367
+ name = qstr->name;
368
+ name_len = qstr->len;
156369 while (ops->getnext(&dir, obj) == 0) {
157
- if (adfs_match(name, obj)) {
370
+ if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
158371 ret = 0;
159372 break;
160373 }
161374 }
375
+ obj->parent_id = ADFS_I(inode)->indaddr;
162376
163
-unlock_out:
164
- read_unlock(&adfs_dir_lock);
377
+unlock_relse:
378
+ up_read(&adfs_dir_rwsem);
379
+ adfs_dir_relse(&dir);
380
+ return ret;
165381
166
-free_out:
167
- ops->free(&dir);
168
-out:
382
+unlock:
383
+ up_read(&adfs_dir_rwsem);
169384 return ret;
170385 }
171386
172387 const struct file_operations adfs_dir_operations = {
173388 .read = generic_read_dir,
174389 .llseek = generic_file_llseek,
175
- .iterate = adfs_readdir,
390
+ .iterate_shared = adfs_iterate,
176391 .fsync = generic_file_fsync,
177392 };
178393
179394 static int
180395 adfs_hash(const struct dentry *parent, struct qstr *qstr)
181396 {
182
- const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;
183397 const unsigned char *name;
184398 unsigned long hash;
185
- int i;
399
+ u32 len;
186400
187
- if (qstr->len < name_len)
188
- return 0;
401
+ if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
402
+ return -ENAMETOOLONG;
189403
190
- /*
191
- * Truncate the name in place, avoids
192
- * having to define a compare function.
193
- */
194
- qstr->len = i = name_len;
404
+ len = qstr->len;
195405 name = qstr->name;
196406 hash = init_name_hash(parent);
197
- while (i--) {
198
- char c;
199
-
200
- c = *name++;
201
- if (c >= 'A' && c <= 'Z')
202
- c += 'a' - 'A';
203
-
204
- hash = partial_name_hash(c, hash);
205
- }
407
+ while (len--)
408
+ hash = partial_name_hash(adfs_tolower(*name++), hash);
206409 qstr->hash = end_name_hash(hash);
207410
208411 return 0;
....@@ -212,30 +415,10 @@
212415 * Compare two names, taking note of the name length
213416 * requirements of the underlying filesystem.
214417 */
215
-static int
216
-adfs_compare(const struct dentry *dentry,
217
- unsigned int len, const char *str, const struct qstr *name)
418
+static int adfs_compare(const struct dentry *dentry, unsigned int len,
419
+ const char *str, const struct qstr *qstr)
218420 {
219
- int i;
220
-
221
- if (len != name->len)
222
- return 1;
223
-
224
- for (i = 0; i < name->len; i++) {
225
- char a, b;
226
-
227
- a = str[i];
228
- b = name->name[i];
229
-
230
- if (a >= 'A' && a <= 'Z')
231
- a += 'a' - 'A';
232
- if (b >= 'A' && b <= 'Z')
233
- b += 'a' - 'A';
234
-
235
- if (a != b)
236
- return 1;
237
- }
238
- return 0;
421
+ return __adfs_compare(qstr->name, qstr->len, str, len);
239422 }
240423
241424 const struct dentry_operations adfs_dentry_operations = {