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