Merge git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client
[platform/adaptation/renesas_rcar/renesas_kernel.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 static int
20 adfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
21 {
22         struct inode *inode = filp->f_path.dentry->d_inode;
23         struct super_block *sb = inode->i_sb;
24         struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
25         struct object_info obj;
26         struct adfs_dir dir;
27         int ret = 0;
28
29         if (filp->f_pos >> 32)
30                 goto out;
31
32         ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
33         if (ret)
34                 goto out;
35
36         switch ((unsigned long)filp->f_pos) {
37         case 0:
38                 if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0)
39                         goto free_out;
40                 filp->f_pos += 1;
41
42         case 1:
43                 if (filldir(dirent, "..", 2, 1, dir.parent_id, DT_DIR) < 0)
44                         goto free_out;
45                 filp->f_pos += 1;
46
47         default:
48                 break;
49         }
50
51         read_lock(&adfs_dir_lock);
52
53         ret = ops->setpos(&dir, filp->f_pos - 2);
54         if (ret)
55                 goto unlock_out;
56         while (ops->getnext(&dir, &obj) == 0) {
57                 if (filldir(dirent, obj.name, obj.name_len,
58                             filp->f_pos, obj.file_id, DT_UNKNOWN) < 0)
59                         goto unlock_out;
60                 filp->f_pos += 1;
61         }
62
63 unlock_out:
64         read_unlock(&adfs_dir_lock);
65
66 free_out:
67         ops->free(&dir);
68
69 out:
70         return ret;
71 }
72
73 int
74 adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
75 {
76         int ret = -EINVAL;
77 #ifdef CONFIG_ADFS_FS_RW
78         struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
79         struct adfs_dir dir;
80
81         printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
82                  obj->file_id, obj->parent_id);
83
84         if (!ops->update) {
85                 ret = -EINVAL;
86                 goto out;
87         }
88
89         ret = ops->read(sb, obj->parent_id, 0, &dir);
90         if (ret)
91                 goto out;
92
93         write_lock(&adfs_dir_lock);
94         ret = ops->update(&dir, obj);
95         write_unlock(&adfs_dir_lock);
96
97         if (wait) {
98                 int err = ops->sync(&dir);
99                 if (!ret)
100                         ret = err;
101         }
102
103         ops->free(&dir);
104 out:
105 #endif
106         return ret;
107 }
108
109 static int
110 adfs_match(struct qstr *name, struct object_info *obj)
111 {
112         int i;
113
114         if (name->len != obj->name_len)
115                 return 0;
116
117         for (i = 0; i < name->len; i++) {
118                 char c1, c2;
119
120                 c1 = name->name[i];
121                 c2 = obj->name[i];
122
123                 if (c1 >= 'A' && c1 <= 'Z')
124                         c1 += 'a' - 'A';
125                 if (c2 >= 'A' && c2 <= 'Z')
126                         c2 += 'a' - 'A';
127
128                 if (c1 != c2)
129                         return 0;
130         }
131         return 1;
132 }
133
134 static int
135 adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj)
136 {
137         struct super_block *sb = inode->i_sb;
138         struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
139         struct adfs_dir dir;
140         int ret;
141
142         ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
143         if (ret)
144                 goto out;
145
146         if (ADFS_I(inode)->parent_id != dir.parent_id) {
147                 adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n",
148                            ADFS_I(inode)->parent_id, dir.parent_id);
149                 ret = -EIO;
150                 goto free_out;
151         }
152
153         obj->parent_id = inode->i_ino;
154
155         /*
156          * '.' is handled by reserved_lookup() in fs/namei.c
157          */
158         if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') {
159                 /*
160                  * Currently unable to fill in the rest of 'obj',
161                  * but this is better than nothing.  We need to
162                  * ascend one level to find it's parent.
163                  */
164                 obj->name_len = 0;
165                 obj->file_id  = obj->parent_id;
166                 goto free_out;
167         }
168
169         read_lock(&adfs_dir_lock);
170
171         ret = ops->setpos(&dir, 0);
172         if (ret)
173                 goto unlock_out;
174
175         ret = -ENOENT;
176         while (ops->getnext(&dir, obj) == 0) {
177                 if (adfs_match(name, obj)) {
178                         ret = 0;
179                         break;
180                 }
181         }
182
183 unlock_out:
184         read_unlock(&adfs_dir_lock);
185
186 free_out:
187         ops->free(&dir);
188 out:
189         return ret;
190 }
191
192 const struct file_operations adfs_dir_operations = {
193         .read           = generic_read_dir,
194         .llseek         = generic_file_llseek,
195         .readdir        = adfs_readdir,
196         .fsync          = generic_file_fsync,
197 };
198
199 static int
200 adfs_hash(const struct dentry *parent, const struct inode *inode,
201                 struct qstr *qstr)
202 {
203         const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;
204         const unsigned char *name;
205         unsigned long hash;
206         int i;
207
208         if (qstr->len < name_len)
209                 return 0;
210
211         /*
212          * Truncate the name in place, avoids
213          * having to define a compare function.
214          */
215         qstr->len = i = name_len;
216         name = qstr->name;
217         hash = init_name_hash();
218         while (i--) {
219                 char c;
220
221                 c = *name++;
222                 if (c >= 'A' && c <= 'Z')
223                         c += 'a' - 'A';
224
225                 hash = partial_name_hash(c, hash);
226         }
227         qstr->hash = end_name_hash(hash);
228
229         return 0;
230 }
231
232 /*
233  * Compare two names, taking note of the name length
234  * requirements of the underlying filesystem.
235  */
236 static int
237 adfs_compare(const struct dentry *parent, const struct inode *pinode,
238                 const struct dentry *dentry, const struct inode *inode,
239                 unsigned int len, const char *str, const struct qstr *name)
240 {
241         int i;
242
243         if (len != name->len)
244                 return 1;
245
246         for (i = 0; i < name->len; i++) {
247                 char a, b;
248
249                 a = str[i];
250                 b = name->name[i];
251
252                 if (a >= 'A' && a <= 'Z')
253                         a += 'a' - 'A';
254                 if (b >= 'A' && b <= 'Z')
255                         b += 'a' - 'A';
256
257                 if (a != b)
258                         return 1;
259         }
260         return 0;
261 }
262
263 const struct dentry_operations adfs_dentry_operations = {
264         .d_hash         = adfs_hash,
265         .d_compare      = adfs_compare,
266 };
267
268 static struct dentry *
269 adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
270 {
271         struct inode *inode = NULL;
272         struct object_info obj;
273         int error;
274
275         error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
276         if (error == 0) {
277                 error = -EACCES;
278                 /*
279                  * This only returns NULL if get_empty_inode
280                  * fails.
281                  */
282                 inode = adfs_iget(dir->i_sb, &obj);
283                 if (inode)
284                         error = 0;
285         }
286         d_add(dentry, inode);
287         return ERR_PTR(error);
288 }
289
290 /*
291  * directories can handle most operations...
292  */
293 const struct inode_operations adfs_dir_inode_operations = {
294         .lookup         = adfs_lookup,
295         .setattr        = adfs_notify_change,
296 };