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