commented early_printk patch because of rejects.
[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 <linux/dcache.h>
44 #include <asm/sn/hwgfs.h>
45
46
47 extern struct vfsmount *hwgfs_vfsmount;
48
49 static int
50 walk_parents_mkdir(
51         const char              **path,
52         struct nameidata        *nd,
53         int                     is_dir)
54 {
55         char                    *slash;
56         char                    buf[strlen(*path)+1];
57         int                     error;
58
59         while ((slash = strchr(*path, '/')) != NULL) {
60                 int len = slash - *path;
61                 memcpy(buf, *path, len);
62                 buf[len] = '\0';
63
64                 error = link_path_walk(buf, nd); 
65                 if (unlikely(error))
66                         return error;
67
68                 nd->dentry = lookup_create(nd, is_dir);
69                 nd->flags |= LOOKUP_PARENT;
70                 if (unlikely(IS_ERR(nd->dentry)))
71                         return PTR_ERR(nd->dentry);
72
73                 if (!nd->dentry->d_inode)
74                         error = vfs_mkdir(nd->dentry->d_parent->d_inode,
75                                         nd->dentry, 0755);
76                 
77                 up(&nd->dentry->d_parent->d_inode->i_sem);
78                 if (unlikely(error))
79                         return error;
80
81                 *path += len + 1;
82         }
83
84         return 0;
85 }
86
87 /* On success, returns with parent_inode->i_sem taken. */
88 static int
89 hwgfs_decode(
90         hwgfs_handle_t          dir,
91         const char              *name,
92         int                     is_dir,
93         struct inode            **parent_inode,
94         struct dentry           **dentry)
95 {
96         struct nameidata        nd;
97         int                     error;
98
99         if (!dir)
100                 dir = hwgfs_vfsmount->mnt_sb->s_root;
101
102         memset(&nd, 0, sizeof(nd));
103         nd.flags = LOOKUP_PARENT;
104         nd.mnt = mntget(hwgfs_vfsmount);
105         nd.dentry = dget(dir);
106
107         error = walk_parents_mkdir(&name, &nd, is_dir);
108         if (unlikely(error))
109                 return error;
110
111         error = link_path_walk(name, &nd);
112         if (unlikely(error))
113                 return error;
114
115         *dentry = lookup_create(&nd, is_dir);
116
117         if (unlikely(IS_ERR(*dentry)))
118                 return PTR_ERR(*dentry);
119         *parent_inode = (*dentry)->d_parent->d_inode;
120         return 0;
121 }
122
123 static int
124 path_len(
125         struct dentry           *de,
126         struct dentry           *root)
127 {
128         int                     len = 0;
129
130         while (de != root) {
131                 len += de->d_name.len + 1;      /* count the '/' */
132                 de = de->d_parent;
133         }
134         return len;             /* -1 because we omit the leading '/',
135                                    +1 because we include trailing '\0' */
136 }
137
138 int
139 hwgfs_generate_path(
140         hwgfs_handle_t          de,
141         char                    *path,
142         int                     buflen)
143 {
144         struct dentry           *hwgfs_root;
145         int                     len;
146         char                    *path_orig = path;
147
148         if (unlikely(de == NULL))
149                 return -EINVAL;
150
151         hwgfs_root = hwgfs_vfsmount->mnt_sb->s_root;
152         if (unlikely(de == hwgfs_root))
153                 return -EINVAL;
154
155         spin_lock(&dcache_lock);
156         len = path_len(de, hwgfs_root);
157         if (len > buflen) {
158                 spin_unlock(&dcache_lock);
159                 return -ENAMETOOLONG;
160         }
161
162         path += len - 1;
163         *path = '\0';
164
165         for (;;) {
166                 path -= de->d_name.len;
167                 memcpy(path, de->d_name.name, de->d_name.len);
168                 de = de->d_parent;
169                 if (de == hwgfs_root)
170                         break;
171                 *(--path) = '/';
172         }
173                 
174         spin_unlock(&dcache_lock);
175         BUG_ON(path != path_orig);
176         return 0;
177 }
178
179 hwgfs_handle_t
180 hwgfs_register(
181         hwgfs_handle_t          dir,
182         const char              *name,
183         unsigned int            flags,
184         unsigned int            major,
185         unsigned int            minor,
186         umode_t                 mode,
187         void                    *ops,
188         void                    *info)
189 {
190         dev_t                   devnum = MKDEV(major, minor);
191         struct inode            *parent_inode;
192         struct dentry           *dentry;
193         int                     error;
194
195         error = hwgfs_decode(dir, name, 0, &parent_inode, &dentry);
196         if (likely(!error)) {
197                 error = vfs_mknod(parent_inode, dentry, mode, devnum);
198                 if (likely(!error)) {
199                         /*
200                          * Do this inside parents i_sem to avoid racing
201                          * with lookups.
202                          */
203                         if (S_ISCHR(mode))
204                                 dentry->d_inode->i_fop = ops;
205                         dentry->d_fsdata = info;
206                         up(&parent_inode->i_sem);
207                 } else {
208                         up(&parent_inode->i_sem);
209                         dput(dentry);
210                         dentry = NULL;
211                 }
212         }
213
214         return dentry;
215 }
216
217 int
218 hwgfs_mk_symlink(
219         hwgfs_handle_t          dir,
220         const char              *name,
221         unsigned int            flags,
222         const char              *link,
223         hwgfs_handle_t          *handle,
224         void                    *info)
225 {
226         struct inode            *parent_inode;
227         struct dentry           *dentry;
228         int                     error;
229
230         error = hwgfs_decode(dir, name, 0, &parent_inode, &dentry);
231         if (likely(!error)) {
232                 error = vfs_symlink(parent_inode, dentry, link);
233                 dentry->d_fsdata = info;
234                 if (handle)
235                         *handle = dentry;
236                 up(&parent_inode->i_sem);
237                 /* dput(dentry); */
238         }
239         return error;
240 }
241
242 hwgfs_handle_t
243 hwgfs_mk_dir(
244         hwgfs_handle_t          dir,
245         const char              *name,
246         void                    *info)
247 {
248         struct inode            *parent_inode;
249         struct dentry           *dentry;
250         int                     error;
251
252         error = hwgfs_decode(dir, name, 1, &parent_inode, &dentry);
253         if (likely(!error)) {
254                 error = vfs_mkdir(parent_inode, dentry, 0755);
255                 up(&parent_inode->i_sem);
256
257                 if (unlikely(error)) {
258                         dput(dentry);
259                         dentry = NULL;
260                 } else {
261                         dentry->d_fsdata = info;
262                 }
263         }
264         return dentry;
265 }
266
267 void
268 hwgfs_unregister(
269         hwgfs_handle_t          de)
270 {
271         struct inode            *parent_inode = de->d_parent->d_inode;
272
273         if (S_ISDIR(de->d_inode->i_mode))
274                 vfs_rmdir(parent_inode, de);
275         else
276                 vfs_unlink(parent_inode, de);
277 }
278
279 /* XXX: this function is utterly bogus.  Every use of it is racy and the
280         prototype is stupid.  You have been warned.  --hch.  */
281 hwgfs_handle_t
282 hwgfs_find_handle(
283         hwgfs_handle_t          base,
284         const char              *name,
285         unsigned int            major,          /* IGNORED */
286         unsigned int            minor,          /* IGNORED */
287         char                    type,           /* IGNORED */
288         int                     traverse_symlinks)
289 {
290         struct dentry           *dentry = NULL;
291         struct nameidata        nd;
292         int                     error;
293
294         BUG_ON(*name=='/');
295
296         memset(&nd, 0, sizeof(nd));
297
298         nd.mnt = mntget(hwgfs_vfsmount);
299         nd.dentry = dget(base ? base : hwgfs_vfsmount->mnt_sb->s_root);
300         if (traverse_symlinks)
301                 nd.flags = LOOKUP_FOLLOW;
302
303         error = link_path_walk(name, &nd);
304         if (likely(!error)) {
305                 dentry = nd.dentry;
306                 path_release(&nd);              /* stale data from here! */
307         }
308
309         return dentry;
310 }
311
312 hwgfs_handle_t
313 hwgfs_get_parent(
314         hwgfs_handle_t          de)
315 {
316         struct dentry           *parent;
317
318         spin_lock(&de->d_lock);
319         parent = de->d_parent;
320         spin_unlock(&de->d_lock);
321
322         return parent;
323 }
324
325 int
326 hwgfs_set_info(
327         hwgfs_handle_t          de,
328         void                    *info)
329 {
330         if (unlikely(de == NULL))
331                 return -EINVAL;
332         de->d_fsdata = info;
333         return 0;
334 }
335
336 void *
337 hwgfs_get_info(
338         hwgfs_handle_t          de)
339 {
340         return de->d_fsdata;
341 }
342
343 EXPORT_SYMBOL(hwgfs_generate_path);
344 EXPORT_SYMBOL(hwgfs_register);
345 EXPORT_SYMBOL(hwgfs_unregister);
346 EXPORT_SYMBOL(hwgfs_mk_symlink);
347 EXPORT_SYMBOL(hwgfs_mk_dir);
348 EXPORT_SYMBOL(hwgfs_find_handle);
349 EXPORT_SYMBOL(hwgfs_get_parent);
350 EXPORT_SYMBOL(hwgfs_set_info);
351 EXPORT_SYMBOL(hwgfs_get_info);