Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/fs/adfs/dir.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* linux/fs/adfs/dir.c
4
*
5
* Copyright (C) 1999-2000 Russell King
6
*
7
* Common directory handling for ADFS
8
*/
9
#include <linux/slab.h>
10
#include "adfs.h"
11
12
/*
13
* For future. This should probably be per-directory.
14
*/
15
static DECLARE_RWSEM(adfs_dir_rwsem);
16
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)
239
{
240
struct inode *inode = file_inode(file);
241
struct super_block *sb = inode->i_sb;
242
const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
243
struct adfs_dir dir;
244
int ret;
245
246
down_read(&adfs_dir_rwsem);
247
ret = adfs_dir_read_inode(sb, inode, &dir);
248
if (ret)
249
goto unlock;
250
251
if (ctx->pos == 0) {
252
if (!dir_emit_dot(file, ctx))
253
goto unlock_relse;
254
ctx->pos = 1;
255
}
256
if (ctx->pos == 1) {
257
if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
258
goto unlock_relse;
259
ctx->pos = 2;
260
}
261
262
ret = ops->iterate(&dir, ctx);
263
264
unlock_relse:
265
up_read(&adfs_dir_rwsem);
266
adfs_dir_relse(&dir);
267
return ret;
268
269
unlock:
270
up_read(&adfs_dir_rwsem);
271
return ret;
272
}
273
274
int
275
adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
276
{
277
const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
278
struct adfs_dir dir;
279
int ret;
280
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);
289
if (ret)
290
goto unlock;
291
292
ret = ops->update(&dir, obj);
293
if (ret)
294
goto forget;
295
296
ret = ops->commit(&dir);
297
if (ret)
298
goto forget;
299
up_write(&adfs_dir_rwsem);
300
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;
361
362
ret = ops->setpos(&dir, 0);
363
if (ret)
364
goto unlock_relse;
365
366
ret = -ENOENT;
367
name = qstr->name;
368
name_len = qstr->len;
369
while (ops->getnext(&dir, obj) == 0) {
370
if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
371
ret = 0;
372
break;
373
}
374
}
375
obj->parent_id = ADFS_I(inode)->indaddr;
376
377
unlock_relse:
378
up_read(&adfs_dir_rwsem);
379
adfs_dir_relse(&dir);
380
return ret;
381
382
unlock:
383
up_read(&adfs_dir_rwsem);
384
return ret;
385
}
386
387
const struct file_operations adfs_dir_operations = {
388
.read = generic_read_dir,
389
.llseek = generic_file_llseek,
390
.iterate_shared = adfs_iterate,
391
.fsync = generic_file_fsync,
392
};
393
394
static int
395
adfs_hash(const struct dentry *parent, struct qstr *qstr)
396
{
397
const unsigned char *name;
398
unsigned long hash;
399
u32 len;
400
401
if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
402
return -ENAMETOOLONG;
403
404
len = qstr->len;
405
name = qstr->name;
406
hash = init_name_hash(parent);
407
while (len--)
408
hash = partial_name_hash(adfs_tolower(*name++), hash);
409
qstr->hash = end_name_hash(hash);
410
411
return 0;
412
}
413
414
/*
415
* Compare two names, taking note of the name length
416
* requirements of the underlying filesystem.
417
*/
418
static int adfs_compare(const struct dentry *dentry, unsigned int len,
419
const char *str, const struct qstr *qstr)
420
{
421
return __adfs_compare(qstr->name, qstr->len, str, len);
422
}
423
424
const struct dentry_operations adfs_dentry_operations = {
425
.d_hash = adfs_hash,
426
.d_compare = adfs_compare,
427
};
428
429
static struct dentry *
430
adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
431
{
432
struct inode *inode = NULL;
433
struct object_info obj;
434
int error;
435
436
error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
437
if (error == 0) {
438
/*
439
* This only returns NULL if get_empty_inode
440
* fails.
441
*/
442
inode = adfs_iget(dir->i_sb, &obj);
443
if (!inode)
444
inode = ERR_PTR(-EACCES);
445
} else if (error != -ENOENT) {
446
inode = ERR_PTR(error);
447
}
448
return d_splice_alias(inode, dentry);
449
}
450
451
/*
452
* directories can handle most operations...
453
*/
454
const struct inode_operations adfs_dir_inode_operations = {
455
.lookup = adfs_lookup,
456
.setattr = adfs_notify_change,
457
};
458
459