1 // SPDX-License-Identifier: GPL-2.0-only
5 * Copyright (C) 1999-2000 Russell King
7 * Common directory handling for ADFS
9 #include <linux/slab.h>
13 * For future. This should probably be per-directory.
15 static DECLARE_RWSEM(adfs_dir_rwsem
);
17 int adfs_dir_copyfrom(void *dst
, struct adfs_dir
*dir
, unsigned int offset
,
20 struct super_block
*sb
= dir
->sb
;
21 unsigned int index
, remain
;
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
)
30 memcpy(dst
, dir
->bhs
[index
]->b_data
+ offset
, remain
);
37 memcpy(dst
, dir
->bhs
[index
]->b_data
+ offset
, len
);
42 int adfs_dir_copyto(struct adfs_dir
*dir
, unsigned int offset
, const void *src
,
45 struct super_block
*sb
= dir
->sb
;
46 unsigned int index
, remain
;
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
)
55 memcpy(dir
->bhs
[index
]->b_data
+ offset
, src
, remain
);
62 memcpy(dir
->bhs
[index
]->b_data
+ offset
, src
, len
);
67 static void __adfs_dir_cleanup(struct adfs_dir
*dir
)
71 if (dir
->bhs
!= dir
->bh
)
77 void adfs_dir_relse(struct adfs_dir
*dir
)
81 for (i
= 0; i
< dir
->nr_buffers
; i
++)
84 __adfs_dir_cleanup(dir
);
87 static void adfs_dir_forget(struct adfs_dir
*dir
)
91 for (i
= 0; i
< dir
->nr_buffers
; i
++)
94 __adfs_dir_cleanup(dir
);
97 int adfs_dir_read_buffers(struct super_block
*sb
, u32 indaddr
,
98 unsigned int size
, struct adfs_dir
*dir
)
100 struct buffer_head
**bhs
;
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
)
110 bhs
= kcalloc(num
, sizeof(*bhs
), GFP_KERNEL
);
115 memcpy(bhs
, dir
->bhs
, dir
->nr_buffers
* sizeof(*bhs
));
120 for (i
= dir
->nr_buffers
; i
< num
; i
++) {
121 block
= __adfs_block_map(sb
, indaddr
, i
);
123 adfs_error(sb
, "dir %06x has a hole at offset %u",
128 dir
->bhs
[i
] = sb_bread(sb
, block
);
131 "dir %06x failed read at offset %u, mapped block 0x%08x",
146 static int adfs_dir_read(struct super_block
*sb
, u32 indaddr
,
147 unsigned int size
, struct adfs_dir
*dir
)
153 return ADFS_SB(sb
)->s_dir
->read(sb
, indaddr
, size
, dir
);
156 static int adfs_dir_read_inode(struct super_block
*sb
, struct inode
*inode
,
157 struct adfs_dir
*dir
)
161 ret
= adfs_dir_read(sb
, ADFS_I(inode
)->indaddr
, inode
->i_size
, dir
);
165 if (ADFS_I(inode
)->parent_id
!= dir
->parent_id
) {
167 "parent directory id changed under me! (%06x but got %06x)\n",
168 ADFS_I(inode
)->parent_id
, dir
->parent_id
);
176 static void adfs_dir_mark_dirty(struct adfs_dir
*dir
)
180 /* Mark the buffers dirty */
181 for (i
= 0; i
< dir
->nr_buffers
; i
++)
182 mark_buffer_dirty(dir
->bhs
[i
]);
185 static int adfs_dir_sync(struct adfs_dir
*dir
)
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
))
200 void adfs_object_fixup(struct adfs_dir
*dir
, struct object_info
*obj
)
202 unsigned int dots
, i
;
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.)
213 for (i
= dots
= 0; i
< obj
->name_len
; i
++)
214 if (obj
->name
[i
] == '/') {
219 if (obj
->name_len
<= 2 && dots
== obj
->name_len
)
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.
226 if (!(obj
->attr
& ADFS_NDA_DIRECTORY
) && ADFS_SB(dir
->sb
)->s_ftsuffix
) {
227 u16 filetype
= adfs_filetype(obj
->loadaddr
);
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);
238 static int adfs_iterate(struct file
*file
, struct dir_context
*ctx
)
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
;
246 down_read(&adfs_dir_rwsem
);
247 ret
= adfs_dir_read_inode(sb
, inode
, &dir
);
252 if (!dir_emit_dot(file
, ctx
))
257 if (!dir_emit(ctx
, "..", 2, dir
.parent_id
, DT_DIR
))
262 ret
= ops
->iterate(&dir
, ctx
);
265 up_read(&adfs_dir_rwsem
);
266 adfs_dir_relse(&dir
);
270 up_read(&adfs_dir_rwsem
);
275 adfs_dir_update(struct super_block
*sb
, struct object_info
*obj
, int wait
)
277 const struct adfs_dir_ops
*ops
= ADFS_SB(sb
)->s_dir
;
281 if (!IS_ENABLED(CONFIG_ADFS_FS_RW
))
287 down_write(&adfs_dir_rwsem
);
288 ret
= adfs_dir_read(sb
, obj
->parent_id
, 0, &dir
);
292 ret
= ops
->update(&dir
, obj
);
296 ret
= ops
->commit(&dir
);
299 up_write(&adfs_dir_rwsem
);
301 adfs_dir_mark_dirty(&dir
);
304 ret
= adfs_dir_sync(&dir
);
306 adfs_dir_relse(&dir
);
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.
316 adfs_dir_relse(&dir
);
318 adfs_dir_forget(&dir
);
320 up_write(&adfs_dir_rwsem
);
325 static unsigned char adfs_tolower(unsigned char c
)
327 if (c
>= 'A' && c
<= 'Z')
332 static int __adfs_compare(const unsigned char *qstr
, u32 qlen
,
333 const char *str
, u32 len
)
340 for (i
= 0; i
< qlen
; i
++)
341 if (adfs_tolower(qstr
[i
]) != adfs_tolower(str
[i
]))
347 static int adfs_dir_lookup_byname(struct inode
*inode
, const struct qstr
*qstr
,
348 struct object_info
*obj
)
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
;
357 down_read(&adfs_dir_rwsem
);
358 ret
= adfs_dir_read_inode(sb
, inode
, &dir
);
362 ret
= ops
->setpos(&dir
, 0);
368 name_len
= qstr
->len
;
369 while (ops
->getnext(&dir
, obj
) == 0) {
370 if (!__adfs_compare(name
, name_len
, obj
->name
, obj
->name_len
)) {
375 obj
->parent_id
= ADFS_I(inode
)->indaddr
;
378 up_read(&adfs_dir_rwsem
);
379 adfs_dir_relse(&dir
);
383 up_read(&adfs_dir_rwsem
);
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
,
395 adfs_hash(const struct dentry
*parent
, struct qstr
*qstr
)
397 const unsigned char *name
;
401 if (qstr
->len
> ADFS_SB(parent
->d_sb
)->s_namelen
)
402 return -ENAMETOOLONG
;
406 hash
= init_name_hash(parent
);
408 hash
= partial_name_hash(adfs_tolower(*name
++), hash
);
409 qstr
->hash
= end_name_hash(hash
);
415 * Compare two names, taking note of the name length
416 * requirements of the underlying filesystem.
418 static int adfs_compare(const struct dentry
*dentry
, unsigned int len
,
419 const char *str
, const struct qstr
*qstr
)
421 return __adfs_compare(qstr
->name
, qstr
->len
, str
, len
);
424 const struct dentry_operations adfs_dentry_operations
= {
426 .d_compare
= adfs_compare
,
429 static struct dentry
*
430 adfs_lookup(struct inode
*dir
, struct dentry
*dentry
, unsigned int flags
)
432 struct inode
*inode
= NULL
;
433 struct object_info obj
;
436 error
= adfs_dir_lookup_byname(dir
, &dentry
->d_name
, &obj
);
439 * This only returns NULL if get_empty_inode
442 inode
= adfs_iget(dir
->i_sb
, &obj
);
444 inode
= ERR_PTR(-EACCES
);
445 } else if (error
!= -ENOENT
) {
446 inode
= ERR_PTR(error
);
448 return d_splice_alias(inode
, dentry
);
452 * directories can handle most operations...
454 const struct inode_operations adfs_dir_inode_operations
= {
455 .lookup
= adfs_lookup
,
456 .setattr
= adfs_notify_change
,