Import 2.4.0-test2pre7
[davej-history.git] / fs / adfs / dir.c
blob21a27734426b5aee79a22b8a04929031a147a2ce
1 /*
2 * linux/fs/adfs/dir.c
4 * Copyright (C) 1999-2000 Russell King
6 * Common directory handling for ADFS
7 */
8 #include <linux/config.h>
9 #include <linux/version.h>
10 #include <linux/errno.h>
11 #include <linux/fs.h>
12 #include <linux/adfs_fs.h>
13 #include <linux/sched.h>
14 #include <linux/stat.h>
15 #include <linux/spinlock.h>
17 #include "adfs.h"
20 * For future. This should probably be per-directory.
22 static rwlock_t adfs_dir_lock;
24 static int
25 adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
27 struct inode *inode = filp->f_dentry->d_inode;
28 struct super_block *sb = inode->i_sb;
29 struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
30 struct object_info obj;
31 struct adfs_dir dir;
32 int ret = 0;
34 if (filp->f_pos >> 32)
35 goto out;
37 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
38 if (ret)
39 goto out;
41 switch ((unsigned long)filp->f_pos) {
42 case 0:
43 if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0)
44 goto free_out;
45 filp->f_pos += 1;
47 case 1:
48 if (filldir(dirent, "..", 2, 1, dir.parent_id) < 0)
49 goto free_out;
50 filp->f_pos += 1;
52 default:
53 break;
56 read_lock(&adfs_dir_lock);
58 ret = ops->setpos(&dir, filp->f_pos - 2);
59 if (ret)
60 goto unlock_out;
61 while (ops->getnext(&dir, &obj) == 0) {
62 if (filldir(dirent, obj.name, obj.name_len,
63 filp->f_pos, obj.file_id) < 0)
64 goto unlock_out;
65 filp->f_pos += 1;
68 unlock_out:
69 read_unlock(&adfs_dir_lock);
71 free_out:
72 ops->free(&dir);
74 out:
75 return ret;
78 int
79 adfs_dir_update(struct super_block *sb, struct object_info *obj)
81 int ret = -EINVAL;
82 #ifdef CONFIG_ADFS_FS_RW
83 struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
84 struct adfs_dir dir;
86 printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
87 obj->file_id, obj->parent_id);
89 if (!ops->update) {
90 ret = -EINVAL;
91 goto out;
94 ret = ops->read(sb, obj->parent_id, 0, &dir);
95 if (ret)
96 goto out;
98 write_lock(&adfs_dir_lock);
99 ret = ops->update(&dir, obj);
100 write_unlock(&adfs_dir_lock);
102 ops->free(&dir);
103 out:
104 #endif
105 return ret;
108 static int
109 adfs_match(struct qstr *name, struct object_info *obj)
111 int i;
113 if (name->len != obj->name_len)
114 return 0;
116 for (i = 0; i < name->len; i++) {
117 char c1, c2;
119 c1 = name->name[i];
120 c2 = obj->name[i];
122 if (c1 >= 'A' && c1 <= 'Z')
123 c1 += 'a' - 'A';
124 if (c2 >= 'A' && c2 <= 'Z')
125 c2 += 'a' - 'A';
127 if (c1 != c2)
128 return 0;
130 return 1;
133 static int
134 adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj)
136 struct super_block *sb = inode->i_sb;
137 struct adfs_dir_ops *ops = sb->u.adfs_sb.s_dir;
138 struct adfs_dir dir;
139 int ret;
141 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
142 if (ret)
143 goto out;
145 if (inode->u.adfs_i.parent_id != dir.parent_id) {
146 adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n",
147 inode->u.adfs_i.parent_id, dir.parent_id);
148 ret = -EIO;
149 goto free_out;
152 obj->parent_id = inode->i_ino;
155 * '.' is handled by reserved_lookup() in fs/namei.c
157 if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') {
159 * Currently unable to fill in the rest of 'obj',
160 * but this is better than nothing. We need to
161 * ascend one level to find it's parent.
163 obj->name_len = 0;
164 obj->file_id = obj->parent_id;
165 goto free_out;
168 read_lock(&adfs_dir_lock);
170 ret = ops->setpos(&dir, 0);
171 if (ret)
172 goto unlock_out;
174 ret = -ENOENT;
175 while (ops->getnext(&dir, obj) == 0) {
176 if (adfs_match(name, obj)) {
177 ret = 0;
178 break;
182 unlock_out:
183 read_unlock(&adfs_dir_lock);
185 free_out:
186 ops->free(&dir);
187 out:
188 return ret;
191 struct file_operations adfs_dir_operations = {
192 read: generic_read_dir,
193 readdir: adfs_readdir,
194 fsync: file_fsync,
197 static int
198 adfs_hash(struct dentry *parent, struct qstr *qstr)
200 const unsigned int name_len = parent->d_sb->u.adfs_sb.s_namelen;
201 const unsigned char *name;
202 unsigned long hash;
203 int i;
205 if (qstr->len < name_len)
206 return 0;
209 * Truncate the name in place, avoids
210 * having to define a compare function.
212 qstr->len = i = name_len;
213 name = qstr->name;
214 hash = init_name_hash();
215 while (i--) {
216 char c;
218 c = *name++;
219 if (c >= 'A' && c <= 'Z')
220 c += 'a' - 'A';
222 hash = partial_name_hash(c, hash);
224 qstr->hash = end_name_hash(hash);
226 return 0;
230 * Compare two names, taking note of the name length
231 * requirements of the underlying filesystem.
233 static int
234 adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name)
236 int i;
238 if (entry->len != name->len)
239 return 1;
241 for (i = 0; i < name->len; i++) {
242 char a, b;
244 a = entry->name[i];
245 b = name->name[i];
247 if (a >= 'A' && a <= 'Z')
248 a += 'a' - 'A';
249 if (b >= 'A' && b <= 'Z')
250 b += 'a' - 'A';
252 if (a != b)
253 return 1;
255 return 0;
258 struct dentry_operations adfs_dentry_operations = {
259 d_hash: adfs_hash,
260 d_compare: adfs_compare,
263 struct dentry *adfs_lookup(struct inode *dir, struct dentry *dentry)
265 struct inode *inode = NULL;
266 struct object_info obj;
267 int error;
269 dentry->d_op = &adfs_dentry_operations;
270 error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
271 if (error == 0) {
272 error = -EACCES;
274 * This only returns NULL if get_empty_inode
275 * fails.
277 inode = adfs_iget(dir->i_sb, &obj);
278 if (inode)
279 error = 0;
281 d_add(dentry, inode);
282 return ERR_PTR(error);
286 * directories can handle most operations...
288 struct inode_operations adfs_dir_inode_operations = {
289 lookup: adfs_lookup,
290 setattr: adfs_notify_change,