Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/fs/adfs/dir.c
54333 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/hex.h>
10
#include <linux/slab.h>
11
#include "adfs.h"
12
13
/*
14
* For future. This should probably be per-directory.
15
*/
16
static DECLARE_RWSEM(adfs_dir_rwsem);
17
18
int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
19
size_t len)
20
{
21
struct super_block *sb = dir->sb;
22
unsigned int index, remain;
23
24
index = offset >> sb->s_blocksize_bits;
25
offset &= sb->s_blocksize - 1;
26
remain = sb->s_blocksize - offset;
27
if (index + (remain < len) >= dir->nr_buffers)
28
return -EINVAL;
29
30
if (remain < len) {
31
memcpy(dst, dir->bhs[index]->b_data + offset, remain);
32
dst += remain;
33
len -= remain;
34
index += 1;
35
offset = 0;
36
}
37
38
memcpy(dst, dir->bhs[index]->b_data + offset, len);
39
40
return 0;
41
}
42
43
int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
44
size_t len)
45
{
46
struct super_block *sb = dir->sb;
47
unsigned int index, remain;
48
49
index = offset >> sb->s_blocksize_bits;
50
offset &= sb->s_blocksize - 1;
51
remain = sb->s_blocksize - offset;
52
if (index + (remain < len) >= dir->nr_buffers)
53
return -EINVAL;
54
55
if (remain < len) {
56
memcpy(dir->bhs[index]->b_data + offset, src, remain);
57
src += remain;
58
len -= remain;
59
index += 1;
60
offset = 0;
61
}
62
63
memcpy(dir->bhs[index]->b_data + offset, src, len);
64
65
return 0;
66
}
67
68
static void __adfs_dir_cleanup(struct adfs_dir *dir)
69
{
70
dir->nr_buffers = 0;
71
72
if (dir->bhs != dir->bh)
73
kfree(dir->bhs);
74
dir->bhs = NULL;
75
dir->sb = NULL;
76
}
77
78
void adfs_dir_relse(struct adfs_dir *dir)
79
{
80
unsigned int i;
81
82
for (i = 0; i < dir->nr_buffers; i++)
83
brelse(dir->bhs[i]);
84
85
__adfs_dir_cleanup(dir);
86
}
87
88
static void adfs_dir_forget(struct adfs_dir *dir)
89
{
90
unsigned int i;
91
92
for (i = 0; i < dir->nr_buffers; i++)
93
bforget(dir->bhs[i]);
94
95
__adfs_dir_cleanup(dir);
96
}
97
98
int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
99
unsigned int size, struct adfs_dir *dir)
100
{
101
struct buffer_head **bhs;
102
unsigned int i, num;
103
int block;
104
105
num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits;
106
if (num > ARRAY_SIZE(dir->bh)) {
107
/* We only allow one extension */
108
if (dir->bhs != dir->bh)
109
return -EINVAL;
110
111
bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL);
112
if (!bhs)
113
return -ENOMEM;
114
115
if (dir->nr_buffers)
116
memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs));
117
118
dir->bhs = bhs;
119
}
120
121
for (i = dir->nr_buffers; i < num; i++) {
122
block = __adfs_block_map(sb, indaddr, i);
123
if (!block) {
124
adfs_error(sb, "dir %06x has a hole at offset %u",
125
indaddr, i);
126
goto error;
127
}
128
129
dir->bhs[i] = sb_bread(sb, block);
130
if (!dir->bhs[i]) {
131
adfs_error(sb,
132
"dir %06x failed read at offset %u, mapped block 0x%08x",
133
indaddr, i, block);
134
goto error;
135
}
136
137
dir->nr_buffers++;
138
}
139
return 0;
140
141
error:
142
adfs_dir_relse(dir);
143
144
return -EIO;
145
}
146
147
static int adfs_dir_read(struct super_block *sb, u32 indaddr,
148
unsigned int size, struct adfs_dir *dir)
149
{
150
dir->sb = sb;
151
dir->bhs = dir->bh;
152
dir->nr_buffers = 0;
153
154
return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
155
}
156
157
static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
158
struct adfs_dir *dir)
159
{
160
int ret;
161
162
ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);
163
if (ret)
164
return ret;
165
166
if (ADFS_I(inode)->parent_id != dir->parent_id) {
167
adfs_error(sb,
168
"parent directory id changed under me! (%06x but got %06x)\n",
169
ADFS_I(inode)->parent_id, dir->parent_id);
170
adfs_dir_relse(dir);
171
ret = -EIO;
172
}
173
174
return ret;
175
}
176
177
static void adfs_dir_mark_dirty(struct adfs_dir *dir)
178
{
179
unsigned int i;
180
181
/* Mark the buffers dirty */
182
for (i = 0; i < dir->nr_buffers; i++)
183
mark_buffer_dirty(dir->bhs[i]);
184
}
185
186
static int adfs_dir_sync(struct adfs_dir *dir)
187
{
188
int err = 0;
189
int i;
190
191
for (i = dir->nr_buffers - 1; i >= 0; i--) {
192
struct buffer_head *bh = dir->bhs[i];
193
sync_dirty_buffer(bh);
194
if (buffer_req(bh) && !buffer_uptodate(bh))
195
err = -EIO;
196
}
197
198
return err;
199
}
200
201
void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
202
{
203
unsigned int dots, i;
204
205
/*
206
* RISC OS allows the use of '/' in directory entry names, so we need
207
* to fix these up. '/' is typically used for FAT compatibility to
208
* represent '.', so do the same conversion here. In any case, '.'
209
* will never be in a RISC OS name since it is used as the pathname
210
* separator. Handle the case where we may generate a '.' or '..'
211
* name, replacing the first character with '^' (the RISC OS "parent
212
* directory" character.)
213
*/
214
for (i = dots = 0; i < obj->name_len; i++)
215
if (obj->name[i] == '/') {
216
obj->name[i] = '.';
217
dots++;
218
}
219
220
if (obj->name_len <= 2 && dots == obj->name_len)
221
obj->name[0] = '^';
222
223
/*
224
* If the object is a file, and the user requested the ,xyz hex
225
* filetype suffix to the name, check the filetype and append.
226
*/
227
if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {
228
u16 filetype = adfs_filetype(obj->loadaddr);
229
230
if (filetype != ADFS_FILETYPE_NONE) {
231
obj->name[obj->name_len++] = ',';
232
obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
233
obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
234
obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
235
}
236
}
237
}
238
239
static int adfs_iterate(struct file *file, struct dir_context *ctx)
240
{
241
struct inode *inode = file_inode(file);
242
struct super_block *sb = inode->i_sb;
243
const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
244
struct adfs_dir dir;
245
int ret;
246
247
down_read(&adfs_dir_rwsem);
248
ret = adfs_dir_read_inode(sb, inode, &dir);
249
if (ret)
250
goto unlock;
251
252
if (ctx->pos == 0) {
253
if (!dir_emit_dot(file, ctx))
254
goto unlock_relse;
255
ctx->pos = 1;
256
}
257
if (ctx->pos == 1) {
258
if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
259
goto unlock_relse;
260
ctx->pos = 2;
261
}
262
263
ret = ops->iterate(&dir, ctx);
264
265
unlock_relse:
266
up_read(&adfs_dir_rwsem);
267
adfs_dir_relse(&dir);
268
return ret;
269
270
unlock:
271
up_read(&adfs_dir_rwsem);
272
return ret;
273
}
274
275
int
276
adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
277
{
278
const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
279
struct adfs_dir dir;
280
int ret;
281
282
if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
283
return -EINVAL;
284
285
if (!ops->update)
286
return -EINVAL;
287
288
down_write(&adfs_dir_rwsem);
289
ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
290
if (ret)
291
goto unlock;
292
293
ret = ops->update(&dir, obj);
294
if (ret)
295
goto forget;
296
297
ret = ops->commit(&dir);
298
if (ret)
299
goto forget;
300
up_write(&adfs_dir_rwsem);
301
302
adfs_dir_mark_dirty(&dir);
303
304
if (wait)
305
ret = adfs_dir_sync(&dir);
306
307
adfs_dir_relse(&dir);
308
return ret;
309
310
/*
311
* If the updated failed because the entry wasn't found, we can
312
* just release the buffers. If it was any other error, forget
313
* the dirtied buffers so they aren't written back to the media.
314
*/
315
forget:
316
if (ret == -ENOENT)
317
adfs_dir_relse(&dir);
318
else
319
adfs_dir_forget(&dir);
320
unlock:
321
up_write(&adfs_dir_rwsem);
322
323
return ret;
324
}
325
326
static unsigned char adfs_tolower(unsigned char c)
327
{
328
if (c >= 'A' && c <= 'Z')
329
c += 'a' - 'A';
330
return c;
331
}
332
333
static int __adfs_compare(const unsigned char *qstr, u32 qlen,
334
const char *str, u32 len)
335
{
336
u32 i;
337
338
if (qlen != len)
339
return 1;
340
341
for (i = 0; i < qlen; i++)
342
if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
343
return 1;
344
345
return 0;
346
}
347
348
static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
349
struct object_info *obj)
350
{
351
struct super_block *sb = inode->i_sb;
352
const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
353
const unsigned char *name;
354
struct adfs_dir dir;
355
u32 name_len;
356
int ret;
357
358
down_read(&adfs_dir_rwsem);
359
ret = adfs_dir_read_inode(sb, inode, &dir);
360
if (ret)
361
goto unlock;
362
363
ret = ops->setpos(&dir, 0);
364
if (ret)
365
goto unlock_relse;
366
367
ret = -ENOENT;
368
name = qstr->name;
369
name_len = qstr->len;
370
while (ops->getnext(&dir, obj) == 0) {
371
if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
372
ret = 0;
373
break;
374
}
375
}
376
obj->parent_id = ADFS_I(inode)->indaddr;
377
378
unlock_relse:
379
up_read(&adfs_dir_rwsem);
380
adfs_dir_relse(&dir);
381
return ret;
382
383
unlock:
384
up_read(&adfs_dir_rwsem);
385
return ret;
386
}
387
388
const struct file_operations adfs_dir_operations = {
389
.read = generic_read_dir,
390
.llseek = generic_file_llseek,
391
.iterate_shared = adfs_iterate,
392
.fsync = generic_file_fsync,
393
};
394
395
static int
396
adfs_hash(const struct dentry *parent, struct qstr *qstr)
397
{
398
const unsigned char *name;
399
unsigned long hash;
400
u32 len;
401
402
if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
403
return -ENAMETOOLONG;
404
405
len = qstr->len;
406
name = qstr->name;
407
hash = init_name_hash(parent);
408
while (len--)
409
hash = partial_name_hash(adfs_tolower(*name++), hash);
410
qstr->hash = end_name_hash(hash);
411
412
return 0;
413
}
414
415
/*
416
* Compare two names, taking note of the name length
417
* requirements of the underlying filesystem.
418
*/
419
static int adfs_compare(const struct dentry *dentry, unsigned int len,
420
const char *str, const struct qstr *qstr)
421
{
422
return __adfs_compare(qstr->name, qstr->len, str, len);
423
}
424
425
const struct dentry_operations adfs_dentry_operations = {
426
.d_hash = adfs_hash,
427
.d_compare = adfs_compare,
428
};
429
430
static struct dentry *
431
adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
432
{
433
struct inode *inode = NULL;
434
struct object_info obj;
435
int error;
436
437
error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
438
if (error == 0) {
439
/*
440
* This only returns NULL if get_empty_inode
441
* fails.
442
*/
443
inode = adfs_iget(dir->i_sb, &obj);
444
if (!inode)
445
inode = ERR_PTR(-EACCES);
446
} else if (error != -ENOENT) {
447
inode = ERR_PTR(error);
448
}
449
return d_splice_alias(inode, dentry);
450
}
451
452
/*
453
* directories can handle most operations...
454
*/
455
const struct inode_operations adfs_dir_inode_operations = {
456
.lookup = adfs_lookup,
457
.setattr = adfs_notify_change,
458
};
459
460