]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - fs/adfs/dir.c
treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 500
[mirror_ubuntu-jammy-kernel.git] / fs / adfs / dir.c
CommitLineData
d2912cb1 1// SPDX-License-Identifier: GPL-2.0-only
1da177e4
LT
2/*
3 * linux/fs/adfs/dir.c
4 *
5 * Copyright (C) 1999-2000 Russell King
6 *
1da177e4
LT
7 * Common directory handling for ADFS
8 */
1da177e4
LT
9#include "adfs.h"
10
11/*
12 * For future. This should probably be per-directory.
13 */
14static DEFINE_RWLOCK(adfs_dir_lock);
15
411c49bc
RK
16void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
17{
fc722a04 18 unsigned int dots, i;
adb514a4
RK
19
20 /*
21 * RISC OS allows the use of '/' in directory entry names, so we need
22 * to fix these up. '/' is typically used for FAT compatibility to
23 * represent '.', so do the same conversion here. In any case, '.'
24 * will never be in a RISC OS name since it is used as the pathname
fc722a04
RK
25 * separator. Handle the case where we may generate a '.' or '..'
26 * name, replacing the first character with '^' (the RISC OS "parent
27 * directory" character.)
adb514a4 28 */
fc722a04
RK
29 for (i = dots = 0; i < obj->name_len; i++)
30 if (obj->name[i] == '/') {
adb514a4 31 obj->name[i] = '.';
fc722a04
RK
32 dots++;
33 }
34
35 if (obj->name_len <= 2 && dots == obj->name_len)
36 obj->name[0] = '^';
adb514a4 37
411c49bc
RK
38 obj->filetype = -1;
39
40 /*
41 * object is a file and is filetyped and timestamped?
42 * RISC OS 12-bit filetype is stored in load_address[19:8]
43 */
44 if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) &&
45 (0xfff00000 == (0xfff00000 & obj->loadaddr))) {
46 obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8);
47
48 /* optionally append the ,xyz hex filetype suffix */
5f8de487
RK
49 if (ADFS_SB(dir->sb)->s_ftsuffix) {
50 __u16 filetype = obj->filetype;
51
52 obj->name[obj->name_len++] = ',';
53 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
54 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
55 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
56 }
411c49bc
RK
57 }
58}
59
1da177e4 60static int
2638ffba 61adfs_readdir(struct file *file, struct dir_context *ctx)
1da177e4 62{
2638ffba 63 struct inode *inode = file_inode(file);
1da177e4 64 struct super_block *sb = inode->i_sb;
0125f504 65 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
1da177e4
LT
66 struct object_info obj;
67 struct adfs_dir dir;
68 int ret = 0;
69
2638ffba
AV
70 if (ctx->pos >> 32)
71 return 0;
1da177e4
LT
72
73 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
74 if (ret)
2638ffba 75 return ret;
1da177e4 76
2638ffba
AV
77 if (ctx->pos == 0) {
78 if (!dir_emit_dot(file, ctx))
1da177e4 79 goto free_out;
2638ffba
AV
80 ctx->pos = 1;
81 }
82 if (ctx->pos == 1) {
83 if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
1da177e4 84 goto free_out;
2638ffba 85 ctx->pos = 2;
1da177e4
LT
86 }
87
88 read_lock(&adfs_dir_lock);
89
2638ffba 90 ret = ops->setpos(&dir, ctx->pos - 2);
1da177e4
LT
91 if (ret)
92 goto unlock_out;
93 while (ops->getnext(&dir, &obj) == 0) {
2638ffba
AV
94 if (!dir_emit(ctx, obj.name, obj.name_len,
95 obj.file_id, DT_UNKNOWN))
96 break;
97 ctx->pos++;
1da177e4
LT
98 }
99
100unlock_out:
101 read_unlock(&adfs_dir_lock);
102
103free_out:
104 ops->free(&dir);
1da177e4
LT
105 return ret;
106}
107
108int
ffdc9064 109adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
1da177e4
LT
110{
111 int ret = -EINVAL;
112#ifdef CONFIG_ADFS_FS_RW
0125f504 113 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
1da177e4
LT
114 struct adfs_dir dir;
115
116 printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
117 obj->file_id, obj->parent_id);
118
119 if (!ops->update) {
120 ret = -EINVAL;
121 goto out;
122 }
123
124 ret = ops->read(sb, obj->parent_id, 0, &dir);
125 if (ret)
126 goto out;
127
128 write_lock(&adfs_dir_lock);
129 ret = ops->update(&dir, obj);
130 write_unlock(&adfs_dir_lock);
131
ffdc9064
AV
132 if (wait) {
133 int err = ops->sync(&dir);
134 if (!ret)
135 ret = err;
136 }
137
1da177e4
LT
138 ops->free(&dir);
139out:
140#endif
141 return ret;
142}
143
525715d0
RK
144static unsigned char adfs_tolower(unsigned char c)
145{
146 if (c >= 'A' && c <= 'Z')
147 c += 'a' - 'A';
148 return c;
149}
150
1e504cf8
RK
151static int __adfs_compare(const unsigned char *qstr, u32 qlen,
152 const char *str, u32 len)
1da177e4 153{
1e504cf8 154 u32 i;
1da177e4 155
1e504cf8
RK
156 if (qlen != len)
157 return 1;
1da177e4 158
525715d0
RK
159 for (i = 0; i < qlen; i++)
160 if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
1e504cf8 161 return 1;
525715d0 162
1e504cf8 163 return 0;
1da177e4
LT
164}
165
1e504cf8
RK
166static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
167 struct object_info *obj)
1da177e4
LT
168{
169 struct super_block *sb = inode->i_sb;
0125f504 170 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
1e504cf8 171 const unsigned char *name;
1da177e4 172 struct adfs_dir dir;
1e504cf8 173 u32 name_len;
1da177e4
LT
174 int ret;
175
176 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
177 if (ret)
178 goto out;
179
180 if (ADFS_I(inode)->parent_id != dir.parent_id) {
19bdd41a 181 adfs_error(sb, "parent directory changed under me! (%lx but got %x)\n",
1da177e4
LT
182 ADFS_I(inode)->parent_id, dir.parent_id);
183 ret = -EIO;
184 goto free_out;
185 }
186
187 obj->parent_id = inode->i_ino;
188
1da177e4
LT
189 read_lock(&adfs_dir_lock);
190
191 ret = ops->setpos(&dir, 0);
192 if (ret)
193 goto unlock_out;
194
195 ret = -ENOENT;
1e504cf8
RK
196 name = qstr->name;
197 name_len = qstr->len;
1da177e4 198 while (ops->getnext(&dir, obj) == 0) {
1e504cf8 199 if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
1da177e4
LT
200 ret = 0;
201 break;
202 }
203 }
204
205unlock_out:
206 read_unlock(&adfs_dir_lock);
207
208free_out:
209 ops->free(&dir);
210out:
211 return ret;
212}
213
4b6f5d20 214const struct file_operations adfs_dir_operations = {
1da177e4 215 .read = generic_read_dir,
59af1584 216 .llseek = generic_file_llseek,
2638ffba 217 .iterate = adfs_readdir,
1b061d92 218 .fsync = generic_file_fsync,
1da177e4
LT
219};
220
221static int
da53be12 222adfs_hash(const struct dentry *parent, struct qstr *qstr)
1da177e4 223{
1da177e4
LT
224 const unsigned char *name;
225 unsigned long hash;
2eb0684f 226 u32 len;
1da177e4 227
2eb0684f
RK
228 if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
229 return -ENAMETOOLONG;
1da177e4 230
2eb0684f 231 len = qstr->len;
1da177e4 232 name = qstr->name;
8387ff25 233 hash = init_name_hash(parent);
2eb0684f 234 while (len--)
525715d0 235 hash = partial_name_hash(adfs_tolower(*name++), hash);
1da177e4
LT
236 qstr->hash = end_name_hash(hash);
237
238 return 0;
239}
240
241/*
242 * Compare two names, taking note of the name length
243 * requirements of the underlying filesystem.
244 */
1e504cf8
RK
245static int adfs_compare(const struct dentry *dentry, unsigned int len,
246 const char *str, const struct qstr *qstr)
1da177e4 247{
1e504cf8 248 return __adfs_compare(qstr->name, qstr->len, str, len);
1da177e4
LT
249}
250
e16404ed 251const struct dentry_operations adfs_dentry_operations = {
1da177e4
LT
252 .d_hash = adfs_hash,
253 .d_compare = adfs_compare,
254};
255
256static struct dentry *
00cd8dd3 257adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
1da177e4
LT
258{
259 struct inode *inode = NULL;
260 struct object_info obj;
261 int error;
262
1da177e4
LT
263 error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
264 if (error == 0) {
1da177e4
LT
265 /*
266 * This only returns NULL if get_empty_inode
267 * fails.
268 */
269 inode = adfs_iget(dir->i_sb, &obj);
9a7dddca
AV
270 if (!inode)
271 inode = ERR_PTR(-EACCES);
272 } else if (error != -ENOENT) {
273 inode = ERR_PTR(error);
1da177e4 274 }
9a7dddca 275 return d_splice_alias(inode, dentry);
1da177e4
LT
276}
277
278/*
279 * directories can handle most operations...
280 */
754661f1 281const struct inode_operations adfs_dir_inode_operations = {
1da177e4
LT
282 .lookup = adfs_lookup,
283 .setattr = adfs_notify_change,
284};