]> git.proxmox.com Git - mirror_spl.git/blame - modules/spl/spl-vnode.c
Initial pass at a file API getf/releasef hooks
[mirror_spl.git] / modules / spl / spl-vnode.c
CommitLineData
0b3cf046 1#include <sys/sysmacros.h>
4b171585 2#include <sys/vnode.h>
0b3cf046 3#include "config.h"
4
4b171585 5void *rootdir = NULL;
6EXPORT_SYMBOL(rootdir);
7
af828292 8kmem_cache_t *vn_cache;
9
4b171585 10static vtype_t
11vn_get_sol_type(umode_t mode)
12{
13 if (S_ISREG(mode))
14 return VREG;
15
16 if (S_ISDIR(mode))
17 return VDIR;
18
19 if (S_ISCHR(mode))
20 return VCHR;
21
22 if (S_ISBLK(mode))
23 return VBLK;
24
25 if (S_ISFIFO(mode))
26 return VFIFO;
27
28 if (S_ISLNK(mode))
29 return VLNK;
30
31 if (S_ISSOCK(mode))
32 return VSOCK;
33
34 if (S_ISCHR(mode))
35 return VCHR;
36
37 return VNON;
38} /* vn_get_sol_type() */
39
af828292 40vnode_t *
41vn_alloc(int flag)
42{
43 vnode_t *vp;
44
45 vp = kmem_cache_alloc(vn_cache, flag);
af828292 46 if (vp != NULL) {
47 vp->v_fp = NULL;
48 vp->v_type = 0;
49 }
50
51 return (vp);
52} /* vn_alloc() */
53EXPORT_SYMBOL(vn_alloc);
54
55void
56vn_free(vnode_t *vp)
57{
58 kmem_cache_free(vn_cache, vp);
59} /* vn_free() */
60EXPORT_SYMBOL(vn_free);
61
0b3cf046 62int
af828292 63vn_open(const char *path, uio_seg_t seg, int flags, int mode,
4b171585 64 vnode_t **vpp, int x1, void *x2)
0b3cf046 65{
4b171585 66 struct file *fp;
67 struct kstat stat;
68 int rc, saved_umask, flags_rw;
0b3cf046 69 vnode_t *vp;
0b3cf046 70
4b171585 71 BUG_ON(seg != UIO_SYSSPACE);
72 BUG_ON(!vpp);
73 *vpp = NULL;
74
75 if (!(flags & FCREAT) && (flags & FWRITE))
76 flags |= FEXCL;
77
78 flags_rw = flags & (FWRITE | FREAD);
79 flags &= ~(FWRITE | FREAD);
80 switch (flags_rw) {
81 case FWRITE: flags |= O_WRONLY;
82 case FREAD: flags |= O_RDONLY;
83 case (FWRITE | FREAD): flags |= O_RDWR;
0b3cf046 84 }
0b3cf046 85
86 if (flags & FCREAT)
4b171585 87 saved_umask = xchg(&current->fs->umask, 0);
0b3cf046 88
4b171585 89 fp = filp_open(path, flags, mode);
0b3cf046 90
91 if (flags & FCREAT)
4b171585 92 (void)xchg(&current->fs->umask, saved_umask);
0b3cf046 93
4b171585 94 if (IS_ERR(fp))
95 return PTR_ERR(fp);
0b3cf046 96
4b171585 97 rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat);
98 if (rc) {
99 filp_close(fp, 0);
100 return rc;
0b3cf046 101 }
102
af828292 103 vp = vn_alloc(KM_SLEEP);
4b171585 104 if (!vp) {
105 filp_close(fp, 0);
106 return -ENOMEM;
107 }
0b3cf046 108
4b171585 109 vp->v_type = vn_get_sol_type(stat.mode);
110 vp->v_fp = fp;
111 *vpp = vp;
0b3cf046 112
4b171585 113 return 0;
114} /* vn_open() */
115EXPORT_SYMBOL(vn_open);
0b3cf046 116
0b3cf046 117int
af828292 118vn_openat(const char *path, uio_seg_t seg, int flags, int mode,
4b171585 119 vnode_t **vpp, int x1, void *x2, vnode_t *vp, int fd)
0b3cf046 120{
4b171585 121 char *realpath;
122 int rc;
0b3cf046 123
4b171585 124 BUG_ON(vp != rootdir);
0b3cf046 125
4b171585 126 realpath = kmalloc(strlen(path) + 2, GFP_KERNEL);
127 if (!realpath)
128 return -ENOMEM;
0b3cf046 129
4b171585 130 sprintf(realpath, "/%s", path);
131 rc = vn_open(realpath, seg, flags, mode, vpp, x1, x2);
0b3cf046 132
4b171585 133 kfree(realpath);
134
135 return rc;
136} /* vn_openat() */
137EXPORT_SYMBOL(vn_openat);
0b3cf046 138
0b3cf046 139int
4b171585 140vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, offset_t off,
af828292 141 uio_seg_t seg, int x1, rlim64_t x2, void *x3, ssize_t *residp)
0b3cf046 142{
4b171585 143 loff_t offset;
144 mm_segment_t saved_fs;
145 struct file *fp;
146 int rc;
147
148 BUG_ON(!(uio == UIO_WRITE || uio == UIO_READ));
149 BUG_ON(!vp);
150 BUG_ON(!vp->v_fp);
151 BUG_ON(seg != UIO_SYSSPACE);
152 BUG_ON(x1 != 0);
153 BUG_ON(x2 != RLIM64_INFINITY);
154
155 offset = off;
156 fp = vp->v_fp;
157
158 /* Writable user data segment must be briefly increased for this
159 * process so we can use the user space read call paths to write
160 * in to memory allocated by the kernel. */
161 saved_fs = get_fs();
162 set_fs(get_ds());
163
164 if (uio & UIO_WRITE)
165 rc = vfs_write(fp, addr, len, &offset);
166 else
167 rc = vfs_read(fp, addr, len, &offset);
168
169 set_fs(saved_fs);
170
171 if (rc < 0)
172 return rc;
0b3cf046 173
4b171585 174 if (residp) {
175 *residp = len - rc;
0b3cf046 176 } else {
4b171585 177 if (rc != len)
178 return -EIO;
0b3cf046 179 }
180
4b171585 181 return 0;
182} /* vn_rdwr() */
183EXPORT_SYMBOL(vn_rdwr);
184
185int
2f5d55aa 186vn_close(vnode_t *vp, int flags, int x1, int x2, void *x3, void *x4)
4b171585 187{
188 int rc;
189
190 BUG_ON(!vp);
191 BUG_ON(!vp->v_fp);
192
193 rc = filp_close(vp->v_fp, 0);
af828292 194 vn_free(vp);
4b171585 195
196 return rc;
197} /* vn_close() */
198EXPORT_SYMBOL(vn_close);
199
200static struct dentry *lookup_hash(struct nameidata *nd)
201{
202 return __lookup_hash(&nd->last, nd->dentry, nd);
203} /* lookup_hash() */
204
205/* Modified do_unlinkat() from linux/fs/namei.c, only uses exported symbols */
206int
af828292 207vn_remove(const char *path, uio_seg_t seg, int flags)
4b171585 208{
209 struct dentry *dentry;
210 struct nameidata nd;
211 struct inode *inode = NULL;
212 int rc = 0;
213
2f5d55aa 214 BUG_ON(seg != UIO_SYSSPACE);
215 BUG_ON(flags != RMFILE);
216
4b171585 217 rc = path_lookup(path, LOOKUP_PARENT, &nd);
218 if (rc)
219 goto exit;
220
221 rc = -EISDIR;
222 if (nd.last_type != LAST_NORM)
223 goto exit1;
224
225 mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
226 dentry = lookup_hash(&nd);
227 rc = PTR_ERR(dentry);
228 if (!IS_ERR(dentry)) {
229 /* Why not before? Because we want correct rc value */
230 if (nd.last.name[nd.last.len])
231 goto slashes;
232 inode = dentry->d_inode;
233 if (inode)
234 atomic_inc(&inode->i_count);
235 rc = vfs_unlink(nd.dentry->d_inode, dentry);
236exit2:
237 dput(dentry);
238 }
239 mutex_unlock(&nd.dentry->d_inode->i_mutex);
240 if (inode)
241 iput(inode); /* truncate the inode here */
242exit1:
243 path_release(&nd);
244exit:
245 return rc;
246
247slashes:
248 rc = !dentry->d_inode ? -ENOENT :
249 S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
250 goto exit2;
251} /* vn_remove() */
252EXPORT_SYMBOL(vn_remove);
253
254/* Modified do_rename() from linux/fs/namei.c, only uses exported symbols */
255int
256vn_rename(const char *oldname, const char *newname, int x1)
257{
258 struct dentry * old_dir, * new_dir;
259 struct dentry * old_dentry, *new_dentry;
260 struct dentry * trap;
261 struct nameidata oldnd, newnd;
262 int rc = 0;
263
264 rc = path_lookup(oldname, LOOKUP_PARENT, &oldnd);
265 if (rc)
266 goto exit;
267
268 rc = path_lookup(newname, LOOKUP_PARENT, &newnd);
269 if (rc)
270 goto exit1;
271
272 rc = -EXDEV;
273 if (oldnd.mnt != newnd.mnt)
274 goto exit2;
275
276 old_dir = oldnd.dentry;
277 rc = -EBUSY;
278 if (oldnd.last_type != LAST_NORM)
279 goto exit2;
280
281 new_dir = newnd.dentry;
282 if (newnd.last_type != LAST_NORM)
283 goto exit2;
284
285 trap = lock_rename(new_dir, old_dir);
286
287 old_dentry = lookup_hash(&oldnd);
288
289 rc = PTR_ERR(old_dentry);
290 if (IS_ERR(old_dentry))
291 goto exit3;
292
293 /* source must exist */
294 rc = -ENOENT;
295 if (!old_dentry->d_inode)
296 goto exit4;
297
298 /* unless the source is a directory trailing slashes give -ENOTDIR */
299 if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
300 rc = -ENOTDIR;
301 if (oldnd.last.name[oldnd.last.len])
302 goto exit4;
303 if (newnd.last.name[newnd.last.len])
304 goto exit4;
305 }
306
307 /* source should not be ancestor of target */
308 rc = -EINVAL;
309 if (old_dentry == trap)
310 goto exit4;
311
312 new_dentry = lookup_hash(&newnd);
313 rc = PTR_ERR(new_dentry);
314 if (IS_ERR(new_dentry))
315 goto exit4;
316
317 /* target should not be an ancestor of source */
318 rc = -ENOTEMPTY;
319 if (new_dentry == trap)
320 goto exit5;
321
322 rc = vfs_rename(old_dir->d_inode, old_dentry,
323 new_dir->d_inode, new_dentry);
324exit5:
325 dput(new_dentry);
326exit4:
327 dput(old_dentry);
328exit3:
329 unlock_rename(new_dir, old_dir);
330exit2:
331 path_release(&newnd);
332exit1:
333 path_release(&oldnd);
334exit:
335 return rc;
0b3cf046 336}
4b171585 337EXPORT_SYMBOL(vn_rename);
0b3cf046 338
4b171585 339int
36e6f861 340vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4)
0b3cf046 341{
4b171585 342 struct file *fp;
343 struct kstat stat;
344 int rc;
345
346 BUG_ON(!vp);
347 BUG_ON(!vp->v_fp);
348 BUG_ON(!vap);
349
350 fp = vp->v_fp;
351
352 rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat);
353 if (rc)
354 return rc;
355
356 vap->va_type = vn_get_sol_type(stat.mode);
357 vap->va_mode = stat.mode;
358 vap->va_uid = stat.uid;
359 vap->va_gid = stat.gid;
360 vap->va_fsid = 0;
361 vap->va_nodeid = stat.ino;
362 vap->va_nlink = stat.nlink;
363 vap->va_size = stat.size;
364 vap->va_blocksize = stat.blksize;
365 vap->va_atime.tv_sec = stat.atime.tv_sec;
366 vap->va_atime.tv_usec = stat.atime.tv_nsec / NSEC_PER_USEC;
367 vap->va_mtime.tv_sec = stat.mtime.tv_sec;
368 vap->va_mtime.tv_usec = stat.mtime.tv_nsec / NSEC_PER_USEC;
369 vap->va_ctime.tv_sec = stat.ctime.tv_sec;
370 vap->va_ctime.tv_usec = stat.ctime.tv_nsec / NSEC_PER_USEC;
371 vap->va_rdev = stat.rdev;
372 vap->va_blocks = stat.blocks;
373
374 return rc;
0b3cf046 375}
4b171585 376EXPORT_SYMBOL(vn_getattr);
377
2f5d55aa 378int vn_fsync(vnode_t *vp, int flags, void *x3, void *x4)
4b171585 379{
36e6f861 380 int datasync = 0;
381
4b171585 382 BUG_ON(!vp);
383 BUG_ON(!vp->v_fp);
384
36e6f861 385 if (flags & FDSYNC)
386 datasync = 1;
387
388 return file_fsync(vp->v_fp, vp->v_fp->f_dentry, datasync);
4b171585 389} /* vn_fsync() */
390EXPORT_SYMBOL(vn_fsync);
af828292 391
392static int
393vn_cache_constructor(void *buf, void *cdrarg, int kmflags)
394{
395 struct vnode *vp = buf;
396
397 mutex_init(&vp->v_lock, NULL, MUTEX_DEFAULT, NULL);
398
399 return (0);
400} /* vn_cache_constructor() */
401
402static void
403vn_cache_destructor(void *buf, void *cdrarg)
404{
405 struct vnode *vp = buf;
406
407 mutex_destroy(&vp->v_lock);
408} /* vn_cache_destructor() */
409
410int
411vn_init(void)
412{
5d86345d 413 vn_cache = kmem_cache_create("spl_vn_cache", sizeof(struct vnode), 64,
414 vn_cache_constructor,
415 vn_cache_destructor,
af828292 416 NULL, NULL, NULL, 0);
417 return 0;
418} /* vn_init() */
419
420void
421vn_fini(void)
422{
423 kmem_cache_destroy(vn_cache);
424} /* vn_fini() */