patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / arch / ia64 / sn / io / hwgfs / interface.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (c) 2003 Silicon Graphics, Inc.  All Rights Reserved.
7  *
8  *  Portions based on Adam Richter's smalldevfs and thus
9  *  Copyright 2002-2003  Yggdrasil Computing, Inc.
10  */
11
12 #include <linux/module.h>
13 #include <linux/kernel.h>
14 #include <linux/fs.h>
15 #include <linux/mount.h>
16 #include <linux/namei.h>
17 #include <linux/string.h>
18 #include <linux/slab.h>
19 #include <asm/sn/hwgfs.h>
20
21
22 extern struct vfsmount *hwgfs_vfsmount;
23
24 static int
25 walk_parents_mkdir(
26         const char              **path,
27         struct nameidata        *nd,
28         int                     is_dir)
29 {
30         char                    *slash;
31         char                    buf[strlen(*path)+1];
32         int                     error;
33
34         while ((slash = strchr(*path, '/')) != NULL) {
35                 int len = slash - *path;
36                 memcpy(buf, *path, len);
37                 buf[len] = '\0';
38
39                 error = path_walk(buf, nd);
40                 if (unlikely(error))
41                         return error;
42
43                 nd->dentry = lookup_create(nd, is_dir);
44                 nd->flags |= LOOKUP_PARENT;
45                 if (unlikely(IS_ERR(nd->dentry)))
46                         return PTR_ERR(nd->dentry);
47
48                 if (!nd->dentry->d_inode)
49                         error = vfs_mkdir(nd->dentry->d_parent->d_inode,
50                                         nd->dentry, 0755);
51                 
52                 up(&nd->dentry->d_parent->d_inode->i_sem);
53                 if (unlikely(error))
54                         return error;
55
56                 *path += len + 1;
57         }
58
59         return 0;
60 }
61
62 /* On success, returns with parent_inode->i_sem taken. */
63 static int
64 hwgfs_decode(
65         hwgfs_handle_t          dir,
66         const char              *name,
67         int                     is_dir,
68         struct inode            **parent_inode,
69         struct dentry           **dentry)
70 {
71         struct nameidata        nd;
72         int                     error;
73
74         if (!dir)
75                 dir = hwgfs_vfsmount->mnt_sb->s_root;
76
77         memset(&nd, 0, sizeof(nd));
78         nd.flags = LOOKUP_PARENT;
79         nd.mnt = mntget(hwgfs_vfsmount);
80         nd.dentry = dget(dir);
81
82         error = walk_parents_mkdir(&name, &nd, is_dir);
83         if (unlikely(error))
84                 return error;
85
86         error = path_walk(name, &nd);
87         if (unlikely(error))
88                 return error;
89
90         *dentry = lookup_create(&nd, is_dir);
91
92         if (unlikely(IS_ERR(*dentry)))
93                 return PTR_ERR(*dentry);
94         *parent_inode = (*dentry)->d_parent->d_inode;
95         return 0;
96 }
97
98 static int
99 path_len(
100         struct dentry           *de,
101         struct dentry           *root)
102 {
103         int                     len = 0;
104
105         while (de != root) {
106                 len += de->d_name.len + 1;      /* count the '/' */
107                 de = de->d_parent;
108         }
109         return len;             /* -1 because we omit the leading '/',
110                                    +1 because we include trailing '\0' */
111 }
112
113 int
114 hwgfs_generate_path(
115         hwgfs_handle_t          de,
116         char                    *path,
117         int                     buflen)
118 {
119         struct dentry           *hwgfs_root;
120         int                     len;
121         char                    *path_orig = path;
122
123         if (unlikely(de == NULL))
124                 return -EINVAL;
125
126         hwgfs_root = hwgfs_vfsmount->mnt_sb->s_root;
127         if (unlikely(de == hwgfs_root))
128                 return -EINVAL;
129
130         spin_lock(&dcache_lock);
131         len = path_len(de, hwgfs_root);
132         if (len > buflen) {
133                 spin_unlock(&dcache_lock);
134                 return -ENAMETOOLONG;
135         }
136
137         path += len - 1;
138         *path = '\0';
139
140         for (;;) {
141                 path -= de->d_name.len;
142                 memcpy(path, de->d_name.name, de->d_name.len);
143                 de = de->d_parent;
144                 if (de == hwgfs_root)
145                         break;
146                 *(--path) = '/';
147         }
148                 
149         spin_unlock(&dcache_lock);
150         BUG_ON(path != path_orig);
151         return 0;
152 }
153
154 hwgfs_handle_t
155 hwgfs_register(
156         hwgfs_handle_t          dir,
157         const char              *name,
158         unsigned int            flags,
159         unsigned int            major,
160         unsigned int            minor,
161         umode_t                 mode,
162         void                    *ops,
163         void                    *info)
164 {
165         dev_t                   devnum = MKDEV(major, minor);
166         struct inode            *parent_inode;
167         struct dentry           *dentry;
168         int                     error;
169
170         error = hwgfs_decode(dir, name, 0, &parent_inode, &dentry);
171         if (likely(!error)) {
172                 error = vfs_mknod(parent_inode, dentry, mode, devnum);
173                 if (likely(!error)) {
174                         /*
175                          * Do this inside parents i_sem to avoid racing
176                          * with lookups.
177                          */
178                         if (S_ISCHR(mode))
179                                 dentry->d_inode->i_fop = ops;
180                         dentry->d_fsdata = info;
181                         up(&parent_inode->i_sem);
182                 } else {
183                         up(&parent_inode->i_sem);
184                         dput(dentry);
185                         dentry = NULL;
186                 }
187         }
188
189         return dentry;
190 }
191
192 int
193 hwgfs_mk_symlink(
194         hwgfs_handle_t          dir,
195         const char              *name,
196         unsigned int            flags,
197         const char              *link,
198         hwgfs_handle_t          *handle,
199         void                    *info)
200 {
201         struct inode            *parent_inode;
202         struct dentry           *dentry;
203         int                     error;
204
205         error = hwgfs_decode(dir, name, 0, &parent_inode, &dentry);
206         if (likely(!error)) {
207                 error = vfs_symlink(parent_inode, dentry, link, S_IALLUGO);
208                 dentry->d_fsdata = info;
209                 if (handle)
210                         *handle = dentry;
211                 up(&parent_inode->i_sem);
212                 /* dput(dentry); */
213         }
214         return error;
215 }
216
217 hwgfs_handle_t
218 hwgfs_mk_dir(
219         hwgfs_handle_t          dir,
220         const char              *name,
221         void                    *info)
222 {
223         struct inode            *parent_inode;
224         struct dentry           *dentry;
225         int                     error;
226
227         error = hwgfs_decode(dir, name, 1, &parent_inode, &dentry);
228         if (likely(!error)) {
229                 error = vfs_mkdir(parent_inode, dentry, 0755);
230                 up(&parent_inode->i_sem);
231
232                 if (unlikely(error)) {
233                         dput(dentry);
234                         dentry = NULL;
235                 } else {
236                         dentry->d_fsdata = info;
237                 }
238         }
239         return dentry;
240 }
241
242 void
243 hwgfs_unregister(
244         hwgfs_handle_t          de)
245 {
246         struct inode            *parent_inode = de->d_parent->d_inode;
247
248         if (S_ISDIR(de->d_inode->i_mode))
249                 vfs_rmdir(parent_inode, de);
250         else
251                 vfs_unlink(parent_inode, de);
252 }
253
254 /* XXX: this function is utterly bogus.  Every use of it is racy and the
255         prototype is stupid.  You have been warned.  --hch.  */
256 hwgfs_handle_t
257 hwgfs_find_handle(
258         hwgfs_handle_t          base,
259         const char              *name,
260         unsigned int            major,          /* IGNORED */
261         unsigned int            minor,          /* IGNORED */
262         char                    type,           /* IGNORED */
263         int                     traverse_symlinks)
264 {
265         struct dentry           *dentry = NULL;
266         struct nameidata        nd;
267         int                     error;
268
269         BUG_ON(*name=='/');
270
271         memset(&nd, 0, sizeof(nd));
272
273         nd.mnt = mntget(hwgfs_vfsmount);
274         nd.dentry = dget(base ? base : hwgfs_vfsmount->mnt_sb->s_root);
275         nd.flags = (traverse_symlinks ? LOOKUP_FOLLOW : 0);
276
277         error = path_walk(name, &nd);
278         if (likely(!error)) {
279                 dentry = nd.dentry;
280                 path_release(&nd);              /* stale data from here! */
281         }
282
283         return dentry;
284 }
285
286 hwgfs_handle_t
287 hwgfs_get_parent(
288         hwgfs_handle_t          de)
289 {
290         struct dentry           *parent;
291
292         spin_lock(&de->d_lock);
293         parent = de->d_parent;
294         spin_unlock(&de->d_lock);
295
296         return parent;
297 }
298
299 int
300 hwgfs_set_info(
301         hwgfs_handle_t          de,
302         void                    *info)
303 {
304         if (unlikely(de == NULL))
305                 return -EINVAL;
306         de->d_fsdata = info;
307         return 0;
308 }
309
310 void *
311 hwgfs_get_info(
312         hwgfs_handle_t          de)
313 {
314         return de->d_fsdata;
315 }
316
317 EXPORT_SYMBOL(hwgfs_generate_path);
318 EXPORT_SYMBOL(hwgfs_register);
319 EXPORT_SYMBOL(hwgfs_unregister);
320 EXPORT_SYMBOL(hwgfs_mk_symlink);
321 EXPORT_SYMBOL(hwgfs_mk_dir);
322 EXPORT_SYMBOL(hwgfs_find_handle);
323 EXPORT_SYMBOL(hwgfs_get_parent);
324 EXPORT_SYMBOL(hwgfs_set_info);
325 EXPORT_SYMBOL(hwgfs_get_info);