fs/adfs: fix filename fixup handling for "/" and "//" names
[linux-2.6/linux-2.6-arm.git] / fs / adfs / dir.c
blobfe39310c1a0abe90bd5c2b0f3040b9089d939dc2
1 /*
2 * linux/fs/adfs/dir.c
4 * Copyright (C) 1999-2000 Russell King
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.
10 * Common directory handling for ADFS
12 #include "adfs.h"
15 * For future. This should probably be per-directory.
17 static DEFINE_RWLOCK(adfs_dir_lock);
19 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
21 unsigned int dots, i;
24 * RISC OS allows the use of '/' in directory entry names, so we need
25 * to fix these up. '/' is typically used for FAT compatibility to
26 * represent '.', so do the same conversion here. In any case, '.'
27 * will never be in a RISC OS name since it is used as the pathname
28 * separator. Handle the case where we may generate a '.' or '..'
29 * name, replacing the first character with '^' (the RISC OS "parent
30 * directory" character.)
32 for (i = dots = 0; i < obj->name_len; i++)
33 if (obj->name[i] == '/') {
34 obj->name[i] = '.';
35 dots++;
38 if (obj->name_len <= 2 && dots == obj->name_len)
39 obj->name[0] = '^';
41 obj->filetype = -1;
44 * object is a file and is filetyped and timestamped?
45 * RISC OS 12-bit filetype is stored in load_address[19:8]
47 if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) &&
48 (0xfff00000 == (0xfff00000 & obj->loadaddr))) {
49 obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8);
51 /* optionally append the ,xyz hex filetype suffix */
52 if (ADFS_SB(dir->sb)->s_ftsuffix) {
53 __u16 filetype = obj->filetype;
55 obj->name[obj->name_len++] = ',';
56 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
57 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
58 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
63 static int
64 adfs_readdir(struct file *file, struct dir_context *ctx)
66 struct inode *inode = file_inode(file);
67 struct super_block *sb = inode->i_sb;
68 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
69 struct object_info obj;
70 struct adfs_dir dir;
71 int ret = 0;
73 if (ctx->pos >> 32)
74 return 0;
76 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
77 if (ret)
78 return ret;
80 if (ctx->pos == 0) {
81 if (!dir_emit_dot(file, ctx))
82 goto free_out;
83 ctx->pos = 1;
85 if (ctx->pos == 1) {
86 if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
87 goto free_out;
88 ctx->pos = 2;
91 read_lock(&adfs_dir_lock);
93 ret = ops->setpos(&dir, ctx->pos - 2);
94 if (ret)
95 goto unlock_out;
96 while (ops->getnext(&dir, &obj) == 0) {
97 if (!dir_emit(ctx, obj.name, obj.name_len,
98 obj.file_id, DT_UNKNOWN))
99 break;
100 ctx->pos++;
103 unlock_out:
104 read_unlock(&adfs_dir_lock);
106 free_out:
107 ops->free(&dir);
108 return ret;
112 adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
114 int ret = -EINVAL;
115 #ifdef CONFIG_ADFS_FS_RW
116 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
117 struct adfs_dir dir;
119 printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
120 obj->file_id, obj->parent_id);
122 if (!ops->update) {
123 ret = -EINVAL;
124 goto out;
127 ret = ops->read(sb, obj->parent_id, 0, &dir);
128 if (ret)
129 goto out;
131 write_lock(&adfs_dir_lock);
132 ret = ops->update(&dir, obj);
133 write_unlock(&adfs_dir_lock);
135 if (wait) {
136 int err = ops->sync(&dir);
137 if (!ret)
138 ret = err;
141 ops->free(&dir);
142 out:
143 #endif
144 return ret;
147 static unsigned char adfs_tolower(unsigned char c)
149 if (c >= 'A' && c <= 'Z')
150 c += 'a' - 'A';
151 return c;
154 static int __adfs_compare(const unsigned char *qstr, u32 qlen,
155 const char *str, u32 len)
157 u32 i;
159 if (qlen != len)
160 return 1;
162 for (i = 0; i < qlen; i++)
163 if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
164 return 1;
166 return 0;
169 static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
170 struct object_info *obj)
172 struct super_block *sb = inode->i_sb;
173 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
174 const unsigned char *name;
175 struct adfs_dir dir;
176 u32 name_len;
177 int ret;
179 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
180 if (ret)
181 goto out;
183 if (ADFS_I(inode)->parent_id != dir.parent_id) {
184 adfs_error(sb, "parent directory changed under me! (%lx but got %x)\n",
185 ADFS_I(inode)->parent_id, dir.parent_id);
186 ret = -EIO;
187 goto free_out;
190 obj->parent_id = inode->i_ino;
192 read_lock(&adfs_dir_lock);
194 ret = ops->setpos(&dir, 0);
195 if (ret)
196 goto unlock_out;
198 ret = -ENOENT;
199 name = qstr->name;
200 name_len = qstr->len;
201 while (ops->getnext(&dir, obj) == 0) {
202 if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
203 ret = 0;
204 break;
208 unlock_out:
209 read_unlock(&adfs_dir_lock);
211 free_out:
212 ops->free(&dir);
213 out:
214 return ret;
217 const struct file_operations adfs_dir_operations = {
218 .read = generic_read_dir,
219 .llseek = generic_file_llseek,
220 .iterate = adfs_readdir,
221 .fsync = generic_file_fsync,
224 static int
225 adfs_hash(const struct dentry *parent, struct qstr *qstr)
227 const unsigned char *name;
228 unsigned long hash;
229 u32 len;
231 if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
232 return -ENAMETOOLONG;
234 len = qstr->len;
235 name = qstr->name;
236 hash = init_name_hash(parent);
237 while (len--)
238 hash = partial_name_hash(adfs_tolower(*name++), hash);
239 qstr->hash = end_name_hash(hash);
241 return 0;
245 * Compare two names, taking note of the name length
246 * requirements of the underlying filesystem.
248 static int adfs_compare(const struct dentry *dentry, unsigned int len,
249 const char *str, const struct qstr *qstr)
251 return __adfs_compare(qstr->name, qstr->len, str, len);
254 const struct dentry_operations adfs_dentry_operations = {
255 .d_hash = adfs_hash,
256 .d_compare = adfs_compare,
259 static struct dentry *
260 adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
262 struct inode *inode = NULL;
263 struct object_info obj;
264 int error;
266 error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
267 if (error == 0) {
269 * This only returns NULL if get_empty_inode
270 * fails.
272 inode = adfs_iget(dir->i_sb, &obj);
273 if (!inode)
274 inode = ERR_PTR(-EACCES);
275 } else if (error != -ENOENT) {
276 inode = ERR_PTR(error);
278 return d_splice_alias(inode, dentry);
282 * directories can handle most operations...
284 const struct inode_operations adfs_dir_inode_operations = {
285 .lookup = adfs_lookup,
286 .setattr = adfs_notify_change,