]> git.proxmox.com Git - mirror_spl.git/blame - module/spl/spl-vnode.c
Prepare SPL repo to merge with ZFS repo
[mirror_spl.git] / module / spl / spl-vnode.c
CommitLineData
4b393c50 1/*
716154c5
BB
2 * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
3 * Copyright (C) 2007 The Regents of the University of California.
4 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
5 * Written by Brian Behlendorf <behlendorf1@llnl.gov>.
715f6251 6 * UCRL-CODE-235197
7 *
716154c5 8 * This file is part of the SPL, Solaris Porting Layer.
3d6af2dd 9 * For details, see <http://zfsonlinux.org/>.
716154c5
BB
10 *
11 * The SPL is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
715f6251 15 *
716154c5 16 * The SPL is distributed in the hope that it will be useful, but WITHOUT
715f6251 17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * for more details.
20 *
21 * You should have received a copy of the GNU General Public License along
716154c5 22 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
5461eefe 23 *
716154c5 24 * Solaris Porting Layer (SPL) Vnode Implementation.
4b393c50 25 */
715f6251 26
f7fd6ddd 27#include <sys/cred.h>
4b171585 28#include <sys/vnode.h>
e5b9b344 29#include <sys/kmem_cache.h>
bbdc6ae4 30#include <linux/falloc.h>
5712fade
BB
31#include <linux/fs.h>
32#include <linux/uaccess.h>
33#ifdef HAVE_FDTABLE_HEADER
34#include <linux/fdtable.h>
35#endif
937879f1 36
51a727e9 37vnode_t *rootdir = (vnode_t *)0xabcd1234;
4b171585 38EXPORT_SYMBOL(rootdir);
39
7afde631 40static spl_kmem_cache_t *vn_cache;
41static spl_kmem_cache_t *vn_file_cache;
e4f1d29f 42
83c623aa 43static DEFINE_SPINLOCK(vn_file_lock);
e4f1d29f 44static LIST_HEAD(vn_file_list);
af828292 45
5712fade
BB
46static int
47spl_filp_fallocate(struct file *fp, int mode, loff_t offset, loff_t len)
48{
49 int error = -EOPNOTSUPP;
50
51#ifdef HAVE_FILE_FALLOCATE
52 if (fp->f_op->fallocate)
53 error = fp->f_op->fallocate(fp, mode, offset, len);
54#else
55#ifdef HAVE_INODE_FALLOCATE
56 if (fp->f_dentry && fp->f_dentry->d_inode &&
57 fp->f_dentry->d_inode->i_op->fallocate)
58 error = fp->f_dentry->d_inode->i_op->fallocate(
59 fp->f_dentry->d_inode, mode, offset, len);
60#endif /* HAVE_INODE_FALLOCATE */
61#endif /* HAVE_FILE_FALLOCATE */
62
63 return (error);
64}
65
66static int
67spl_filp_fsync(struct file *fp, int sync)
68{
69#ifdef HAVE_2ARGS_VFS_FSYNC
70 return (vfs_fsync(fp, sync));
71#else
72 return (vfs_fsync(fp, (fp)->f_dentry, sync));
73#endif /* HAVE_2ARGS_VFS_FSYNC */
74}
75
76static ssize_t
77spl_kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos)
78{
79#if defined(HAVE_KERNEL_WRITE_PPOS)
80 return (kernel_write(file, buf, count, pos));
81#else
82 mm_segment_t saved_fs;
83 ssize_t ret;
84
85 saved_fs = get_fs();
86 set_fs(get_ds());
87
88 ret = vfs_write(file, (__force const char __user *)buf, count, pos);
89
90 set_fs(saved_fs);
91
92 return (ret);
93#endif
94}
95
96static ssize_t
97spl_kernel_read(struct file *file, void *buf, size_t count, loff_t *pos)
98{
99#if defined(HAVE_KERNEL_READ_PPOS)
100 return (kernel_read(file, buf, count, pos));
101#else
102 mm_segment_t saved_fs;
103 ssize_t ret;
104
105 saved_fs = get_fs();
106 set_fs(get_ds());
107
108 ret = vfs_read(file, (void __user *)buf, count, pos);
109
110 set_fs(saved_fs);
111
112 return (ret);
113#endif
114}
115
4295b530
BB
116vtype_t
117vn_mode_to_vtype(mode_t mode)
4b171585 118{
119 if (S_ISREG(mode))
5461eefe 120 return (VREG);
4b171585 121
122 if (S_ISDIR(mode))
5461eefe 123 return (VDIR);
4b171585 124
125 if (S_ISCHR(mode))
5461eefe 126 return (VCHR);
4b171585 127
128 if (S_ISBLK(mode))
5461eefe 129 return (VBLK);
4b171585 130
131 if (S_ISFIFO(mode))
5461eefe 132 return (VFIFO);
4b171585 133
134 if (S_ISLNK(mode))
5461eefe 135 return (VLNK);
4b171585 136
137 if (S_ISSOCK(mode))
5461eefe 138 return (VSOCK);
4b171585 139
5461eefe 140 return (VNON);
4295b530
BB
141} /* vn_mode_to_vtype() */
142EXPORT_SYMBOL(vn_mode_to_vtype);
143
144mode_t
145vn_vtype_to_mode(vtype_t vtype)
146{
147 if (vtype == VREG)
5461eefe 148 return (S_IFREG);
4295b530
BB
149
150 if (vtype == VDIR)
5461eefe 151 return (S_IFDIR);
4295b530
BB
152
153 if (vtype == VCHR)
5461eefe 154 return (S_IFCHR);
4295b530
BB
155
156 if (vtype == VBLK)
5461eefe 157 return (S_IFBLK);
4295b530
BB
158
159 if (vtype == VFIFO)
5461eefe 160 return (S_IFIFO);
4295b530
BB
161
162 if (vtype == VLNK)
5461eefe 163 return (S_IFLNK);
4295b530
BB
164
165 if (vtype == VSOCK)
5461eefe 166 return (S_IFSOCK);
4295b530 167
5461eefe 168 return (VNON);
4295b530
BB
169} /* vn_vtype_to_mode() */
170EXPORT_SYMBOL(vn_vtype_to_mode);
4b171585 171
af828292 172vnode_t *
173vn_alloc(int flag)
174{
175 vnode_t *vp;
176
177 vp = kmem_cache_alloc(vn_cache, flag);
af828292 178 if (vp != NULL) {
e4f1d29f 179 vp->v_file = NULL;
af828292 180 vp->v_type = 0;
181 }
182
8d9a23e8 183 return (vp);
af828292 184} /* vn_alloc() */
185EXPORT_SYMBOL(vn_alloc);
186
187void
188vn_free(vnode_t *vp)
189{
190 kmem_cache_free(vn_cache, vp);
191} /* vn_free() */
192EXPORT_SYMBOL(vn_free);
193
0b3cf046 194int
3673d032
BB
195vn_open(const char *path, uio_seg_t seg, int flags, int mode, vnode_t **vpp,
196 int x1, void *x2)
0b3cf046 197{
f7e8739c
RC
198 struct file *fp;
199 struct kstat stat;
200 int rc, saved_umask = 0;
4be55565 201 gfp_t saved_gfp;
0b3cf046 202 vnode_t *vp;
0b3cf046 203
937879f1 204 ASSERT(flags & (FWRITE | FREAD));
205 ASSERT(seg == UIO_SYSSPACE);
206 ASSERT(vpp);
4b171585 207 *vpp = NULL;
208
209 if (!(flags & FCREAT) && (flags & FWRITE))
210 flags |= FEXCL;
211
5461eefe
BB
212 /*
213 * Note for filp_open() the two low bits must be remapped to mean:
728b9dd8 214 * 01 - read-only -> 00 read-only
215 * 10 - write-only -> 01 write-only
216 * 11 - read-write -> 10 read-write
217 */
218 flags--;
0b3cf046 219
220 if (flags & FCREAT)
4b171585 221 saved_umask = xchg(&current->fs->umask, 0);
0b3cf046 222
f7e8739c 223 fp = filp_open(path, flags, mode);
0b3cf046 224
225 if (flags & FCREAT)
5461eefe 226 (void) xchg(&current->fs->umask, saved_umask);
0b3cf046 227
f7e8739c 228 if (IS_ERR(fp))
8d9a23e8 229 return (-PTR_ERR(fp));
0b3cf046 230
94b1ab2a
OF
231#if defined(HAVE_4ARGS_VFS_GETATTR)
232 rc = vfs_getattr(&fp->f_path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
233#elif defined(HAVE_2ARGS_VFS_GETATTR)
2a305c34
RY
234 rc = vfs_getattr(&fp->f_path, &stat);
235#else
bc90df66 236 rc = vfs_getattr(fp->f_path.mnt, fp->f_dentry, &stat);
2a305c34 237#endif
4b171585 238 if (rc) {
239 filp_close(fp, 0);
8d9a23e8 240 return (-rc);
0b3cf046 241 }
242
af828292 243 vp = vn_alloc(KM_SLEEP);
4b171585 244 if (!vp) {
245 filp_close(fp, 0);
8d9a23e8 246 return (ENOMEM);
4b171585 247 }
0b3cf046 248
4be55565
LW
249 saved_gfp = mapping_gfp_mask(fp->f_mapping);
250 mapping_set_gfp_mask(fp->f_mapping, saved_gfp & ~(__GFP_IO|__GFP_FS));
251
e4f1d29f 252 mutex_enter(&vp->v_lock);
4295b530 253 vp->v_type = vn_mode_to_vtype(stat.mode);
e4f1d29f 254 vp->v_file = fp;
4be55565 255 vp->v_gfp_mask = saved_gfp;
4b171585 256 *vpp = vp;
e4f1d29f 257 mutex_exit(&vp->v_lock);
0b3cf046 258
8d9a23e8 259 return (0);
4b171585 260} /* vn_open() */
261EXPORT_SYMBOL(vn_open);
0b3cf046 262
0b3cf046 263int
af828292 264vn_openat(const char *path, uio_seg_t seg, int flags, int mode,
5461eefe 265 vnode_t **vpp, int x1, void *x2, vnode_t *vp, int fd)
0b3cf046 266{
4b171585 267 char *realpath;
12018327 268 int len, rc;
0b3cf046 269
937879f1 270 ASSERT(vp == rootdir);
0b3cf046 271
12018327 272 len = strlen(path) + 2;
54cccfc2 273 realpath = kmalloc(len, kmem_flags_convert(KM_SLEEP));
4b171585 274 if (!realpath)
8d9a23e8 275 return (ENOMEM);
0b3cf046 276
5461eefe 277 (void) snprintf(realpath, len, "/%s", path);
4b171585 278 rc = vn_open(realpath, seg, flags, mode, vpp, x1, x2);
4b171585 279 kfree(realpath);
280
8d9a23e8 281 return (rc);
4b171585 282} /* vn_openat() */
283EXPORT_SYMBOL(vn_openat);
0b3cf046 284
0b3cf046 285int
4b171585 286vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, offset_t off,
3673d032 287 uio_seg_t seg, int ioflag, rlim64_t x2, void *x3, ssize_t *residp)
0b3cf046 288{
ed19bccf
BB
289 struct file *fp = vp->v_file;
290 loff_t offset = off;
4b171585 291 int rc;
292
937879f1 293 ASSERT(uio == UIO_WRITE || uio == UIO_READ);
937879f1 294 ASSERT(seg == UIO_SYSSPACE);
663e02a1 295 ASSERT((ioflag & ~FAPPEND) == 0);
4b171585 296
663e02a1
RC
297 if (ioflag & FAPPEND)
298 offset = fp->f_pos;
299
4b171585 300 if (uio & UIO_WRITE)
ed19bccf 301 rc = spl_kernel_write(fp, addr, len, &offset);
4b171585 302 else
ed19bccf 303 rc = spl_kernel_read(fp, addr, len, &offset);
4b171585 304
f3989ed3 305 fp->f_pos = offset;
4b171585 306
307 if (rc < 0)
8d9a23e8 308 return (-rc);
0b3cf046 309
4b171585 310 if (residp) {
311 *residp = len - rc;
0b3cf046 312 } else {
4b171585 313 if (rc != len)
8d9a23e8 314 return (EIO);
0b3cf046 315 }
316
8d9a23e8 317 return (0);
4b171585 318} /* vn_rdwr() */
319EXPORT_SYMBOL(vn_rdwr);
320
321int
2f5d55aa 322vn_close(vnode_t *vp, int flags, int x1, int x2, void *x3, void *x4)
4b171585 323{
324 int rc;
325
937879f1 326 ASSERT(vp);
327 ASSERT(vp->v_file);
4b171585 328
4be55565 329 mapping_set_gfp_mask(vp->v_file->f_mapping, vp->v_gfp_mask);
97735c39
BB
330 rc = filp_close(vp->v_file, 0);
331 vn_free(vp);
4b171585 332
8d9a23e8 333 return (-rc);
4b171585 334} /* vn_close() */
335EXPORT_SYMBOL(vn_close);
336
5461eefe
BB
337/*
338 * vn_seek() does not actually seek it only performs bounds checking on the
97735c39 339 * proposed seek. We perform minimal checking and allow vn_rdwr() to catch
5461eefe
BB
340 * anything more serious.
341 */
97735c39 342int
47995fa6 343vn_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, void *ct)
97735c39
BB
344{
345 return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
346}
347EXPORT_SYMBOL(vn_seek);
348
4b171585 349int
36e6f861 350vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4)
0b3cf046 351{
4b171585 352 struct file *fp;
dcd9cb5a 353 struct kstat stat;
4b171585 354 int rc;
355
937879f1 356 ASSERT(vp);
357 ASSERT(vp->v_file);
358 ASSERT(vap);
4b171585 359
e4f1d29f 360 fp = vp->v_file;
4b171585 361
94b1ab2a
OF
362#if defined(HAVE_4ARGS_VFS_GETATTR)
363 rc = vfs_getattr(&fp->f_path, &stat, STATX_BASIC_STATS,
364 AT_STATX_SYNC_AS_STAT);
365#elif defined(HAVE_2ARGS_VFS_GETATTR)
2a305c34
RY
366 rc = vfs_getattr(&fp->f_path, &stat);
367#else
368 rc = vfs_getattr(fp->f_path.mnt, fp->f_dentry, &stat);
369#endif
4b171585 370 if (rc)
8d9a23e8 371 return (-rc);
4b171585 372
5461eefe
BB
373 vap->va_type = vn_mode_to_vtype(stat.mode);
374 vap->va_mode = stat.mode;
375 vap->va_uid = KUID_TO_SUID(stat.uid);
376 vap->va_gid = KGID_TO_SGID(stat.gid);
377 vap->va_fsid = 0;
378 vap->va_nodeid = stat.ino;
379 vap->va_nlink = stat.nlink;
380 vap->va_size = stat.size;
381 vap->va_blksize = stat.blksize;
382 vap->va_atime = stat.atime;
383 vap->va_mtime = stat.mtime;
384 vap->va_ctime = stat.ctime;
385 vap->va_rdev = stat.rdev;
386 vap->va_nblocks = stat.blocks;
4b171585 387
8d9a23e8 388 return (0);
0b3cf046 389}
4b171585 390EXPORT_SYMBOL(vn_getattr);
391
5461eefe
BB
392int
393vn_fsync(vnode_t *vp, int flags, void *x3, void *x4)
4b171585 394{
36e6f861 395 int datasync = 0;
2a5d574e
BB
396 int error;
397 int fstrans;
36e6f861 398
937879f1 399 ASSERT(vp);
400 ASSERT(vp->v_file);
4b171585 401
36e6f861 402 if (flags & FDSYNC)
403 datasync = 1;
404
2a5d574e
BB
405 /*
406 * May enter XFS which generates a warning when PF_FSTRANS is set.
407 * To avoid this the flag is cleared over vfs_sync() and then reset.
408 */
8f87971e 409 fstrans = __spl_pf_fstrans_check();
2a5d574e 410 if (fstrans)
8f87971e 411 current->flags &= ~(__SPL_PF_FSTRANS);
2a5d574e
BB
412
413 error = -spl_filp_fsync(vp->v_file, datasync);
414 if (fstrans)
8f87971e 415 current->flags |= __SPL_PF_FSTRANS;
2a5d574e
BB
416
417 return (error);
4b171585 418} /* vn_fsync() */
419EXPORT_SYMBOL(vn_fsync);
af828292 420
bbdc6ae4
ED
421int vn_space(vnode_t *vp, int cmd, struct flock *bfp, int flag,
422 offset_t offset, void *x6, void *x7)
423{
424 int error = EOPNOTSUPP;
ea2633ad
TC
425#ifdef FALLOC_FL_PUNCH_HOLE
426 int fstrans;
427#endif
bbdc6ae4
ED
428
429 if (cmd != F_FREESP || bfp->l_whence != 0)
8d9a23e8 430 return (EOPNOTSUPP);
bbdc6ae4
ED
431
432 ASSERT(vp);
433 ASSERT(vp->v_file);
434 ASSERT(bfp->l_start >= 0 && bfp->l_len > 0);
435
436#ifdef FALLOC_FL_PUNCH_HOLE
ea2633ad
TC
437 /*
438 * May enter XFS which generates a warning when PF_FSTRANS is set.
439 * To avoid this the flag is cleared over vfs_sync() and then reset.
440 */
8f87971e 441 fstrans = __spl_pf_fstrans_check();
ea2633ad 442 if (fstrans)
8f87971e 443 current->flags &= ~(__SPL_PF_FSTRANS);
ea2633ad 444
1c7b3eaf
BB
445 /*
446 * When supported by the underlying file system preferentially
447 * use the fallocate() callback to preallocate the space.
448 */
449 error = -spl_filp_fallocate(vp->v_file,
450 FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
451 bfp->l_start, bfp->l_len);
ea2633ad
TC
452
453 if (fstrans)
8f87971e 454 current->flags |= __SPL_PF_FSTRANS;
ea2633ad 455
1c7b3eaf 456 if (error == 0)
8d9a23e8 457 return (0);
bbdc6ae4
ED
458#endif
459
460#ifdef HAVE_INODE_TRUNCATE_RANGE
461 if (vp->v_file->f_dentry && vp->v_file->f_dentry->d_inode &&
462 vp->v_file->f_dentry->d_inode->i_op &&
463 vp->v_file->f_dentry->d_inode->i_op->truncate_range) {
464 off_t end = bfp->l_start + bfp->l_len;
465 /*
466 * Judging from the code in shmem_truncate_range(),
467 * it seems the kernel expects the end offset to be
468 * inclusive and aligned to the end of a page.
469 */
470 if (end % PAGE_SIZE != 0) {
471 end &= ~(off_t)(PAGE_SIZE - 1);
472 if (end <= bfp->l_start)
8d9a23e8 473 return (0);
bbdc6ae4
ED
474 }
475 --end;
476
477 vp->v_file->f_dentry->d_inode->i_op->truncate_range(
3673d032
BB
478 vp->v_file->f_dentry->d_inode, bfp->l_start, end);
479
8d9a23e8 480 return (0);
bbdc6ae4
ED
481 }
482#endif
483
8d9a23e8 484 return (error);
bbdc6ae4
ED
485}
486EXPORT_SYMBOL(vn_space);
487
e4f1d29f 488/* Function must be called while holding the vn_file_lock */
489static file_t *
d3c677bc 490file_find(int fd, struct task_struct *task)
e4f1d29f 491{
5461eefe 492 file_t *fp;
e4f1d29f 493
5461eefe 494 list_for_each_entry(fp, &vn_file_list, f_list) {
d3c677bc 495 if (fd == fp->f_fd && fp->f_task == task) {
937879f1 496 ASSERT(atomic_read(&fp->f_ref) != 0);
5461eefe 497 return (fp);
e4f1d29f 498 }
499 }
500
5461eefe 501 return (NULL);
e4f1d29f 502} /* file_find() */
503
504file_t *
505vn_getf(int fd)
506{
5461eefe 507 struct kstat stat;
e4f1d29f 508 struct file *lfp;
509 file_t *fp;
510 vnode_t *vp;
937879f1 511 int rc = 0;
e4f1d29f 512
313b1ea6
RY
513 if (fd < 0)
514 return (NULL);
515
e4f1d29f 516 /* Already open just take an extra reference */
517 spin_lock(&vn_file_lock);
518
d3c677bc 519 fp = file_find(fd, current);
e4f1d29f 520 if (fp) {
1683e75e
RY
521 lfp = fget(fd);
522 fput(fp->f_file);
523 /*
524 * areleasef() can cause us to see a stale reference when
525 * userspace has reused a file descriptor before areleasef()
526 * has run. fput() the stale reference and replace it. We
527 * retain the original reference count such that the concurrent
528 * areleasef() will decrement its reference and terminate.
529 */
530 if (lfp != fp->f_file) {
531 fp->f_file = lfp;
532 fp->f_vnode->v_file = lfp;
533 }
e4f1d29f 534 atomic_inc(&fp->f_ref);
535 spin_unlock(&vn_file_lock);
8d9a23e8 536 return (fp);
e4f1d29f 537 }
538
539 spin_unlock(&vn_file_lock);
540
541 /* File was not yet opened create the object and setup */
4afaaefa 542 fp = kmem_cache_alloc(vn_file_cache, KM_SLEEP);
e4f1d29f 543 if (fp == NULL)
8d9a23e8 544 goto out;
e4f1d29f 545
546 mutex_enter(&fp->f_lock);
547
548 fp->f_fd = fd;
763b2f3b 549 fp->f_task = current;
e4f1d29f 550 fp->f_offset = 0;
551 atomic_inc(&fp->f_ref);
552
553 lfp = fget(fd);
554 if (lfp == NULL)
8d9a23e8 555 goto out_mutex;
e4f1d29f 556
557 vp = vn_alloc(KM_SLEEP);
558 if (vp == NULL)
8d9a23e8 559 goto out_fget;
e4f1d29f 560
94b1ab2a 561#if defined(HAVE_4ARGS_VFS_GETATTR)
5461eefe
BB
562 rc = vfs_getattr(&lfp->f_path, &stat, STATX_TYPE,
563 AT_STATX_SYNC_AS_STAT);
94b1ab2a 564#elif defined(HAVE_2ARGS_VFS_GETATTR)
2a305c34
RY
565 rc = vfs_getattr(&lfp->f_path, &stat);
566#else
567 rc = vfs_getattr(lfp->f_path.mnt, lfp->f_dentry, &stat);
568#endif
5461eefe 569 if (rc)
8d9a23e8 570 goto out_vnode;
e4f1d29f 571
572 mutex_enter(&vp->v_lock);
4295b530 573 vp->v_type = vn_mode_to_vtype(stat.mode);
e4f1d29f 574 vp->v_file = lfp;
575 mutex_exit(&vp->v_lock);
576
577 fp->f_vnode = vp;
578 fp->f_file = lfp;
579
580 /* Put it on the tracking list */
581 spin_lock(&vn_file_lock);
582 list_add(&fp->f_list, &vn_file_list);
583 spin_unlock(&vn_file_lock);
584
585 mutex_exit(&fp->f_lock);
8d9a23e8 586 return (fp);
e4f1d29f 587
588out_vnode:
e4f1d29f 589 vn_free(vp);
590out_fget:
e4f1d29f 591 fput(lfp);
592out_mutex:
e4f1d29f 593 mutex_exit(&fp->f_lock);
594 kmem_cache_free(vn_file_cache, fp);
595out:
5461eefe 596 return (NULL);
e4f1d29f 597} /* getf() */
598EXPORT_SYMBOL(getf);
599
600static void releasef_locked(file_t *fp)
601{
937879f1 602 ASSERT(fp->f_file);
603 ASSERT(fp->f_vnode);
e4f1d29f 604
605 /* Unlinked from list, no refs, safe to free outside mutex */
606 fput(fp->f_file);
607 vn_free(fp->f_vnode);
608
609 kmem_cache_free(vn_file_cache, fp);
610}
611
612void
613vn_releasef(int fd)
d3c677bc
RY
614{
615 areleasef(fd, P_FINFO(current));
616}
617EXPORT_SYMBOL(releasef);
618
619void
620vn_areleasef(int fd, uf_info_t *fip)
e4f1d29f 621{
622 file_t *fp;
d3c677bc 623 struct task_struct *task = (struct task_struct *)fip;
e4f1d29f 624
313b1ea6
RY
625 if (fd < 0)
626 return;
627
e4f1d29f 628 spin_lock(&vn_file_lock);
d3c677bc 629 fp = file_find(fd, task);
e4f1d29f 630 if (fp) {
631 atomic_dec(&fp->f_ref);
632 if (atomic_read(&fp->f_ref) > 0) {
633 spin_unlock(&vn_file_lock);
634 return;
635 }
636
5461eefe 637 list_del(&fp->f_list);
e4f1d29f 638 releasef_locked(fp);
639 }
640 spin_unlock(&vn_file_lock);
e4f1d29f 641} /* releasef() */
d3c677bc
RY
642EXPORT_SYMBOL(areleasef);
643
e4f1d29f 644
137af025
BB
645static void
646#ifdef HAVE_SET_FS_PWD_WITH_CONST
647vn_set_fs_pwd(struct fs_struct *fs, const struct path *path)
648#else
649vn_set_fs_pwd(struct fs_struct *fs, struct path *path)
650#endif /* HAVE_SET_FS_PWD_WITH_CONST */
51a727e9 651{
9b2048c2
BB
652 struct path old_pwd;
653
137af025 654#ifdef HAVE_FS_STRUCT_SPINLOCK
9b2048c2
BB
655 spin_lock(&fs->lock);
656 old_pwd = fs->pwd;
657 fs->pwd = *path;
658 path_get(path);
659 spin_unlock(&fs->lock);
137af025 660#else
9b2048c2
BB
661 write_lock(&fs->lock);
662 old_pwd = fs->pwd;
663 fs->pwd = *path;
664 path_get(path);
665 write_unlock(&fs->lock);
137af025 666#endif /* HAVE_FS_STRUCT_SPINLOCK */
9b2048c2
BB
667
668 if (old_pwd.dentry)
669 path_put(&old_pwd);
51a727e9 670}
51a727e9
BB
671
672int
673vn_set_pwd(const char *filename)
674{
5461eefe
BB
675 struct path path;
676 mm_segment_t saved_fs;
677 int rc;
678
679 /*
680 * user_path_dir() and __user_walk() both expect 'filename' to be
681 * a user space address so we must briefly increase the data segment
682 * size to ensure strncpy_from_user() does not fail with -EFAULT.
683 */
684 saved_fs = get_fs();
685 set_fs(get_ds());
686
687 rc = user_path_dir(filename, &path);
688 if (rc)
8d9a23e8 689 goto out;
51a727e9 690
5461eefe
BB
691 rc = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS);
692 if (rc)
8d9a23e8 693 goto dput_and_out;
51a727e9 694
5461eefe 695 vn_set_fs_pwd(current->fs, &path);
51a727e9
BB
696
697dput_and_out:
5461eefe 698 path_put(&path);
51a727e9 699out:
82a358d9
BB
700 set_fs(saved_fs);
701
5461eefe 702 return (-rc);
51a727e9
BB
703} /* vn_set_pwd() */
704EXPORT_SYMBOL(vn_set_pwd);
705
af828292 706static int
707vn_cache_constructor(void *buf, void *cdrarg, int kmflags)
708{
709 struct vnode *vp = buf;
710
711 mutex_init(&vp->v_lock, NULL, MUTEX_DEFAULT, NULL);
712
713 return (0);
714} /* vn_cache_constructor() */
715
716static void
717vn_cache_destructor(void *buf, void *cdrarg)
718{
719 struct vnode *vp = buf;
720
721 mutex_destroy(&vp->v_lock);
722} /* vn_cache_destructor() */
723
e4f1d29f 724static int
725vn_file_cache_constructor(void *buf, void *cdrarg, int kmflags)
726{
727 file_t *fp = buf;
728
729 atomic_set(&fp->f_ref, 0);
5461eefe 730 mutex_init(&fp->f_lock, NULL, MUTEX_DEFAULT, NULL);
4e62fd41 731 INIT_LIST_HEAD(&fp->f_list);
e4f1d29f 732
5461eefe 733 return (0);
378c6ed5 734} /* vn_file_cache_constructor() */
e4f1d29f 735
736static void
737vn_file_cache_destructor(void *buf, void *cdrarg)
738{
739 file_t *fp = buf;
740
741 mutex_destroy(&fp->f_lock);
742} /* vn_file_cache_destructor() */
743
af828292 744int
12ff95ff 745spl_vn_init(void)
af828292 746{
57d86234 747 vn_cache = kmem_cache_create("spl_vn_cache",
5461eefe
BB
748 sizeof (struct vnode), 64, vn_cache_constructor,
749 vn_cache_destructor, NULL, NULL, NULL, 0);
e4f1d29f 750
751 vn_file_cache = kmem_cache_create("spl_vn_file_cache",
5461eefe
BB
752 sizeof (file_t), 64, vn_file_cache_constructor,
753 vn_file_cache_destructor, NULL, NULL, NULL, 0);
754
8d9a23e8 755 return (0);
378c6ed5 756} /* spl_vn_init() */
af828292 757
758void
12ff95ff 759spl_vn_fini(void)
af828292 760{
5461eefe 761 file_t *fp, *next_fp;
2fb9b26a 762 int leaked = 0;
e4f1d29f 763
764 spin_lock(&vn_file_lock);
765
5461eefe
BB
766 list_for_each_entry_safe(fp, next_fp, &vn_file_list, f_list) {
767 list_del(&fp->f_list);
e4f1d29f 768 releasef_locked(fp);
769 leaked++;
770 }
771
e4f1d29f 772 spin_unlock(&vn_file_lock);
773
774 if (leaked > 0)
8d9a23e8 775 printk(KERN_WARNING "WARNING: %d vnode files leaked\n", leaked);
e4f1d29f 776
2371321e 777 kmem_cache_destroy(vn_file_cache);
2fb9b26a 778 kmem_cache_destroy(vn_cache);
378c6ed5 779} /* spl_vn_fini() */