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