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