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
adfs_dir_copyfrom(void * dst,struct adfs_dir * dir,unsigned int offset,size_t len)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
adfs_dir_copyto(struct adfs_dir * dir,unsigned int offset,const void * src,size_t len)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
__adfs_dir_cleanup(struct adfs_dir * dir)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
adfs_dir_relse(struct adfs_dir * dir)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
adfs_dir_forget(struct adfs_dir * dir)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
adfs_dir_read_buffers(struct super_block * sb,u32 indaddr,unsigned int size,struct adfs_dir * dir)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 = kzalloc_objs(*bhs, num);
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
adfs_dir_read(struct super_block * sb,u32 indaddr,unsigned int size,struct adfs_dir * dir)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
adfs_dir_read_inode(struct super_block * sb,struct inode * inode,struct adfs_dir * dir)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
adfs_dir_mark_dirty(struct adfs_dir * dir)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
adfs_dir_sync(struct adfs_dir * dir)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
adfs_object_fixup(struct adfs_dir * dir,struct object_info * obj)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
adfs_iterate(struct file * file,struct dir_context * ctx)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
adfs_dir_update(struct super_block * sb,struct object_info * obj,int wait)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
adfs_tolower(unsigned char c)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
__adfs_compare(const unsigned char * qstr,u32 qlen,const char * str,u32 len)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
adfs_dir_lookup_byname(struct inode * inode,const struct qstr * qstr,struct object_info * obj)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
adfs_hash(const struct dentry * parent,struct qstr * qstr)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 */
adfs_compare(const struct dentry * dentry,unsigned int len,const char * str,const struct qstr * qstr)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 *
adfs_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)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