Update ia64 patch to 2.5.69-030521, throwing away the parts included
[linux-flexiantxendom0-3.2.10.git] / arch / ia64 / sn / io / hwgfs / interface.c
1 /*
2  * Copyright (c) 2003 Silicon Graphics, Inc.  All Rights Reserved.
3  *
4  *  Portions based on Adam Richter's smalldevfs and thus
5  *  Copyright 2002-2003  Yggdrasil Computing, Inc.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of version 2 of the GNU General Public License as
9  * published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it would be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  *
15  * Further, this software is distributed without any warranty that it is
16  * free of the rightful claim of any third person regarding infringement
17  * or the like.  Any license provided herein, whether implied or
18  * otherwise, applies only to this software file.  Patent licenses, if
19  * any, provided herein do not apply to combinations of this program with
20  * other software, or any other product whatsoever.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, write the Free Software Foundation, Inc., 59
24  * Temple Place - Suite 330, Boston MA 02111-1307, USA.
25  *
26  * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
27  * Mountain View, CA  94043, or:
28  *
29  * http://www.sgi.com
30  *
31  * For further information regarding this notice, see:
32  *
33  * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
34  */
35
36 #include <linux/module.h>
37 #include <linux/kernel.h>
38 #include <linux/fs.h>
39 #include <linux/mount.h>
40 #include <linux/namei.h>
41 #include <linux/string.h>
42 #include <linux/slab.h>
43 #include <asm/sn/hwgfs.h>
44
45
46 extern struct vfsmount *hwgfs_vfsmount;
47
48 /* TODO: Move this to some .h file or, more likely, use a slightly
49    different interface from lookup_create. */
50 extern struct dentry *lookup_create(struct nameidata *nd, int is_dir);
51
52 static int
53 walk_parents_mkdir(
54         const char              **path,
55         struct nameidata        *nd,
56         int                     is_dir)
57 {
58         char                    *slash;
59         char                    buf[strlen(*path)+1];
60         int                     error;
61
62         while ((slash = strchr(*path, '/')) != NULL) {
63                 int len = slash - *path;
64                 memcpy(buf, *path, len);
65                 buf[len] = '\0';
66
67                 error = link_path_walk(buf, nd); 
68                 if (unlikely(error))
69                         return error;
70
71                 nd->dentry = lookup_create(nd, is_dir);
72                 if (unlikely(IS_ERR(nd->dentry)))
73                         return PTR_ERR(nd->dentry);
74
75                 if (!nd->dentry->d_inode)
76                         error = vfs_mkdir(nd->dentry->d_parent->d_inode,
77                                         nd->dentry, 0755);
78                 
79                 up(&nd->dentry->d_parent->d_inode->i_sem);
80                 if (unlikely(error))
81                         return error;
82
83                 *path += len + 1;
84         }
85
86         return 0;
87 }
88
89 /* On success, returns with parent_inode->i_sem taken. */
90 static int
91 hwgfs_decode(
92         hwgfs_handle_t          dir,
93         const char              *name,
94         int                     is_dir,
95         struct inode            **parent_inode,
96         struct dentry           **dentry)
97 {
98         struct nameidata        nd;
99         int                     error;
100
101         if (!dir)
102                 dir = hwgfs_vfsmount->mnt_sb->s_root;
103
104         memset(&nd, 0, sizeof(nd));
105         nd.flags = LOOKUP_PARENT;
106         nd.mnt = mntget(hwgfs_vfsmount);
107         nd.dentry = dget(dir);
108
109         error = walk_parents_mkdir(&name, &nd, is_dir);
110         if (unlikely(error))
111                 return error;
112
113         error = link_path_walk(name, &nd);
114         if (unlikely(error))
115                 return error;
116
117         *dentry = lookup_create(&nd, is_dir);
118
119         if (unlikely(IS_ERR(*dentry)))
120                 return PTR_ERR(*dentry);
121         *parent_inode = (*dentry)->d_parent->d_inode;
122         return 0;
123 }
124
125 static int
126 path_len(
127         struct dentry           *de,
128         struct dentry           *root)
129 {
130         int                     len = 0;
131
132         while (de != root) {
133                 len += de->d_name.len + 1;      /* count the '/' */
134                 de = de->d_parent;
135         }
136         return len;             /* -1 because we omit the leading '/',
137                                    +1 because we include trailing '\0' */
138 }
139
140 int
141 hwgfs_generate_path(
142         hwgfs_handle_t          de,
143         char                    *path,
144         int                     buflen)
145 {
146         struct dentry           *hwgfs_root;
147         int                     len;
148         char                    *path_orig = path;
149
150         if (unlikely(de == NULL))
151                 return -EINVAL;
152
153         hwgfs_root = hwgfs_vfsmount->mnt_sb->s_root;
154         if (unlikely(de == hwgfs_root))
155                 return -EINVAL;
156
157         spin_lock(&dcache_lock);
158         len = path_len(de, hwgfs_root);
159         if (len > buflen) {
160                 spin_unlock(&dcache_lock);
161                 return -ENAMETOOLONG;
162         }
163
164         path += len - 1;
165         *path = '\0';
166
167         for (;;) {
168                 path -= de->d_name.len;
169                 memcpy(path, de->d_name.name, de->d_name.len);
170                 de = de->d_parent;
171                 if (de == hwgfs_root)
172                         break;
173                 *(--path) = '/';
174         }
175                 
176         spin_unlock(&dcache_lock);
177         BUG_ON(path != path_orig);
178         return 0;
179 }
180
181 hwgfs_handle_t
182 hwgfs_register(
183         hwgfs_handle_t          dir,
184         const char              *name,
185         unsigned int            flags,
186         unsigned int            major,
187         unsigned int            minor,
188         umode_t                 mode,
189         void                    *ops,
190         void                    *info)
191 {
192         dev_t                   devnum = MKDEV(major, minor);
193         struct inode            *parent_inode;
194         struct dentry           *dentry;
195         int                     error;
196
197         error = hwgfs_decode(dir, name, 0, &parent_inode, &dentry);
198         if (likely(!error)) {
199                 error = vfs_mknod(parent_inode, dentry, mode, devnum);
200                 if (likely(!error)) {
201                         /*
202                          * Do this inside parents i_sem to avoid racing
203                          * with lookups.
204                          */
205                         if (S_ISCHR(mode))
206                                 dentry->d_inode->i_fop = ops;
207                         dentry->d_fsdata = info;
208                         up(&parent_inode->i_sem);
209                 } else {
210                         up(&parent_inode->i_sem);
211                         dput(dentry);
212                         dentry = NULL;
213                 }
214         }
215
216         return dentry;
217 }
218
219 int
220 hwgfs_mk_symlink(
221         hwgfs_handle_t          dir,
222         const char              *name,
223         unsigned int            flags,
224         const char              *link,
225         hwgfs_handle_t          *handle,
226         void                    *info)
227 {
228         struct inode            *parent_inode;
229         struct dentry           *dentry;
230         int                     error;
231
232         error = hwgfs_decode(dir, name, 0, &parent_inode, &dentry);
233         if (likely(!error)) {
234                 error = vfs_symlink(parent_inode, dentry, link);
235                 dentry->d_fsdata = info;
236                 if (handle)
237                         *handle = dentry;
238                 up(&parent_inode->i_sem);
239                 /* dput(dentry); */
240         }
241         return error;
242 }
243
244 hwgfs_handle_t
245 hwgfs_mk_dir(
246         hwgfs_handle_t          dir,
247         const char              *name,
248         void                    *info)
249 {
250         struct inode            *parent_inode;
251         struct dentry           *dentry;
252         int                     error;
253
254         error = hwgfs_decode(dir, name, 1, &parent_inode, &dentry);
255         if (likely(!error)) {
256                 error = vfs_mkdir(parent_inode, dentry, 0755);
257                 up(&parent_inode->i_sem);
258
259                 if (unlikely(error)) {
260                         dput(dentry);
261                         dentry = NULL;
262                 } else {
263                         dentry->d_fsdata = info;
264                 }
265         }
266         return dentry;
267 }
268
269 void
270 hwgfs_unregister(
271         hwgfs_handle_t          de)
272 {
273         struct inode            *parent_inode = de->d_parent->d_inode;
274
275         if (S_ISDIR(de->d_inode->i_mode))
276                 vfs_rmdir(parent_inode, de);
277         else
278                 vfs_unlink(parent_inode, de);
279 }
280
281 /* XXX: this function is utterly bogus.  Every use of it is racy and the
282         prototype is stupid.  You have been warned.  --hch.  */
283 hwgfs_handle_t
284 hwgfs_find_handle(
285         hwgfs_handle_t          base,
286         const char              *name,
287         unsigned int            major,          /* IGNORED */
288         unsigned int            minor,          /* IGNORED */
289         char                    type,           /* IGNORED */
290         int                     traverse_symlinks)
291 {
292         struct dentry           *dentry = NULL;
293         struct nameidata        nd;
294         int                     error;
295
296         BUG_ON(*name=='/');
297
298         memset(&nd, 0, sizeof(nd));
299
300         nd.mnt = mntget(hwgfs_vfsmount);
301         nd.dentry = dget(base ? base : hwgfs_vfsmount->mnt_sb->s_root);
302         if (traverse_symlinks)
303                 nd.flags = LOOKUP_FOLLOW;
304
305         error = link_path_walk(name, &nd);
306         if (likely(!error)) {
307                 dentry = nd.dentry;
308                 path_release(&nd);              /* stale data from here! */
309         }
310
311         return dentry;
312 }
313
314 hwgfs_handle_t
315 hwgfs_get_parent(
316         hwgfs_handle_t          de)
317 {
318         struct dentry           *parent;
319
320         spin_lock(&de->d_lock);
321         parent = de->d_parent;
322         spin_unlock(&de->d_lock);
323
324         return parent;
325 }
326
327 int
328 hwgfs_set_info(
329         hwgfs_handle_t          de,
330         void                    *info)
331 {
332         if (unlikely(de == NULL))
333                 return -EINVAL;
334         de->d_fsdata = info;
335         return 0;
336 }
337
338 void *
339 hwgfs_get_info(
340         hwgfs_handle_t          de)
341 {
342         return de->d_fsdata;
343 }
344
345 EXPORT_SYMBOL(hwgfs_generate_path);
346 EXPORT_SYMBOL(hwgfs_register);
347 EXPORT_SYMBOL(hwgfs_unregister);
348 EXPORT_SYMBOL(hwgfs_mk_symlink);
349 EXPORT_SYMBOL(hwgfs_mk_dir);
350 EXPORT_SYMBOL(hwgfs_find_handle);
351 EXPORT_SYMBOL(hwgfs_get_parent);
352 EXPORT_SYMBOL(hwgfs_set_info);
353 EXPORT_SYMBOL(hwgfs_get_info);