fs/adfs: factor out object fixups
[linux-block.git] / fs / adfs / dir.c
CommitLineData
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 */
17static DEFINE_RWLOCK(adfs_dir_lock);
18
411c49bc
RK
19void 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 40static int
2638ffba 41adfs_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
80unlock_out:
81 read_unlock(&adfs_dir_lock);
82
83free_out:
84 ops->free(&dir);
1da177e4
LT
85 return ret;
86}
87
88int
ffdc9064 89adfs_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);
119out:
120#endif
121 return ret;
122}
123
525715d0
RK
124static 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
131static 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
146static 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
185unlock_out:
186 read_unlock(&adfs_dir_lock);
187
188free_out:
189 ops->free(&dir);
190out:
191 return ret;
192}
193
4b6f5d20 194const 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
201static int
da53be12 202adfs_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
230static 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 236const struct dentry_operations adfs_dentry_operations = {
1da177e4
LT
237 .d_hash = adfs_hash,
238 .d_compare = adfs_compare,
239};
240
241static struct dentry *
00cd8dd3 242adfs_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 266const struct inode_operations adfs_dir_inode_operations = {
1da177e4
LT
267 .lookup = adfs_lookup,
268 .setattr = adfs_notify_change,
269};