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