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