]> git.proxmox.com Git - mirror_spl.git/blame - module/spl/spl-vnode.c
Remove kern_path() wrapper
[mirror_spl.git] / module / spl / spl-vnode.c
CommitLineData
716154c5
BB
1/*****************************************************************************\
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
BB
22 * with the SPL. If not, see <http://www.gnu.org/licenses/>.
23 *****************************************************************************
24 * Solaris Porting Layer (SPL) Vnode Implementation.
25\*****************************************************************************/
715f6251 26
f7fd6ddd 27#include <sys/cred.h>
4b171585 28#include <sys/vnode.h>
bbdc6ae4 29#include <linux/falloc.h>
55abb092 30#include <spl-debug.h>
937879f1 31
b17edc10
BB
32#ifdef SS_DEBUG_SUBSYS
33#undef SS_DEBUG_SUBSYS
937879f1 34#endif
35
b17edc10 36#define SS_DEBUG_SUBSYS SS_VNODE
937879f1 37
51a727e9 38vnode_t *rootdir = (vnode_t *)0xabcd1234;
4b171585 39EXPORT_SYMBOL(rootdir);
40
7afde631 41static spl_kmem_cache_t *vn_cache;
42static spl_kmem_cache_t *vn_file_cache;
e4f1d29f 43
83c623aa 44static DEFINE_SPINLOCK(vn_file_lock);
e4f1d29f 45static LIST_HEAD(vn_file_list);
af828292 46
4295b530
BB
47vtype_t
48vn_mode_to_vtype(mode_t mode)
4b171585 49{
50 if (S_ISREG(mode))
51 return VREG;
52
53 if (S_ISDIR(mode))
54 return VDIR;
55
56 if (S_ISCHR(mode))
57 return VCHR;
58
59 if (S_ISBLK(mode))
60 return VBLK;
61
62 if (S_ISFIFO(mode))
63 return VFIFO;
64
65 if (S_ISLNK(mode))
66 return VLNK;
67
68 if (S_ISSOCK(mode))
69 return VSOCK;
70
71 if (S_ISCHR(mode))
72 return VCHR;
73
74 return VNON;
4295b530
BB
75} /* vn_mode_to_vtype() */
76EXPORT_SYMBOL(vn_mode_to_vtype);
77
78mode_t
79vn_vtype_to_mode(vtype_t vtype)
80{
81 if (vtype == VREG)
82 return S_IFREG;
83
84 if (vtype == VDIR)
85 return S_IFDIR;
86
87 if (vtype == VCHR)
88 return S_IFCHR;
89
90 if (vtype == VBLK)
91 return S_IFBLK;
92
93 if (vtype == VFIFO)
94 return S_IFIFO;
95
96 if (vtype == VLNK)
97 return S_IFLNK;
98
99 if (vtype == VSOCK)
100 return S_IFSOCK;
101
102 return VNON;
103} /* vn_vtype_to_mode() */
104EXPORT_SYMBOL(vn_vtype_to_mode);
4b171585 105
af828292 106vnode_t *
107vn_alloc(int flag)
108{
109 vnode_t *vp;
b17edc10 110 SENTRY;
af828292 111
112 vp = kmem_cache_alloc(vn_cache, flag);
af828292 113 if (vp != NULL) {
e4f1d29f 114 vp->v_file = NULL;
af828292 115 vp->v_type = 0;
116 }
117
b17edc10 118 SRETURN(vp);
af828292 119} /* vn_alloc() */
120EXPORT_SYMBOL(vn_alloc);
121
122void
123vn_free(vnode_t *vp)
124{
b17edc10 125 SENTRY;
af828292 126 kmem_cache_free(vn_cache, vp);
b17edc10 127 SEXIT;
af828292 128} /* vn_free() */
129EXPORT_SYMBOL(vn_free);
130
0b3cf046 131int
af828292 132vn_open(const char *path, uio_seg_t seg, int flags, int mode,
4b171585 133 vnode_t **vpp, int x1, void *x2)
0b3cf046 134{
f7e8739c
RC
135 struct file *fp;
136 struct kstat stat;
137 int rc, saved_umask = 0;
4be55565 138 gfp_t saved_gfp;
0b3cf046 139 vnode_t *vp;
b17edc10 140 SENTRY;
0b3cf046 141
937879f1 142 ASSERT(flags & (FWRITE | FREAD));
143 ASSERT(seg == UIO_SYSSPACE);
144 ASSERT(vpp);
4b171585 145 *vpp = NULL;
146
147 if (!(flags & FCREAT) && (flags & FWRITE))
148 flags |= FEXCL;
149
728b9dd8 150 /* Note for filp_open() the two low bits must be remapped to mean:
151 * 01 - read-only -> 00 read-only
152 * 10 - write-only -> 01 write-only
153 * 11 - read-write -> 10 read-write
154 */
155 flags--;
0b3cf046 156
157 if (flags & FCREAT)
4b171585 158 saved_umask = xchg(&current->fs->umask, 0);
0b3cf046 159
f7e8739c 160 fp = filp_open(path, flags, mode);
0b3cf046 161
162 if (flags & FCREAT)
4b171585 163 (void)xchg(&current->fs->umask, saved_umask);
0b3cf046 164
f7e8739c 165 if (IS_ERR(fp))
b17edc10 166 SRETURN(-PTR_ERR(fp));
0b3cf046 167
2a305c34
RY
168#ifdef HAVE_2ARGS_VFS_GETATTR
169 rc = vfs_getattr(&fp->f_path, &stat);
170#else
bc90df66 171 rc = vfs_getattr(fp->f_path.mnt, fp->f_dentry, &stat);
2a305c34 172#endif
4b171585 173 if (rc) {
174 filp_close(fp, 0);
b17edc10 175 SRETURN(-rc);
0b3cf046 176 }
177
af828292 178 vp = vn_alloc(KM_SLEEP);
4b171585 179 if (!vp) {
180 filp_close(fp, 0);
b17edc10 181 SRETURN(ENOMEM);
4b171585 182 }
0b3cf046 183
4be55565
LW
184 saved_gfp = mapping_gfp_mask(fp->f_mapping);
185 mapping_set_gfp_mask(fp->f_mapping, saved_gfp & ~(__GFP_IO|__GFP_FS));
186
e4f1d29f 187 mutex_enter(&vp->v_lock);
4295b530 188 vp->v_type = vn_mode_to_vtype(stat.mode);
e4f1d29f 189 vp->v_file = fp;
4be55565 190 vp->v_gfp_mask = saved_gfp;
4b171585 191 *vpp = vp;
e4f1d29f 192 mutex_exit(&vp->v_lock);
0b3cf046 193
b17edc10 194 SRETURN(0);
4b171585 195} /* vn_open() */
196EXPORT_SYMBOL(vn_open);
0b3cf046 197
0b3cf046 198int
af828292 199vn_openat(const char *path, uio_seg_t seg, int flags, int mode,
4b171585 200 vnode_t **vpp, int x1, void *x2, vnode_t *vp, int fd)
0b3cf046 201{
4b171585 202 char *realpath;
12018327 203 int len, rc;
b17edc10 204 SENTRY;
0b3cf046 205
937879f1 206 ASSERT(vp == rootdir);
0b3cf046 207
12018327 208 len = strlen(path) + 2;
209 realpath = kmalloc(len, GFP_KERNEL);
4b171585 210 if (!realpath)
b17edc10 211 SRETURN(ENOMEM);
0b3cf046 212
12018327 213 (void)snprintf(realpath, len, "/%s", path);
4b171585 214 rc = vn_open(realpath, seg, flags, mode, vpp, x1, x2);
4b171585 215 kfree(realpath);
216
b17edc10 217 SRETURN(rc);
4b171585 218} /* vn_openat() */
219EXPORT_SYMBOL(vn_openat);
0b3cf046 220
0b3cf046 221int
4b171585 222vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, offset_t off,
663e02a1 223 uio_seg_t seg, int ioflag, rlim64_t x2, void *x3, ssize_t *residp)
0b3cf046 224{
4b171585 225 loff_t offset;
226 mm_segment_t saved_fs;
227 struct file *fp;
228 int rc;
b17edc10 229 SENTRY;
4b171585 230
937879f1 231 ASSERT(uio == UIO_WRITE || uio == UIO_READ);
232 ASSERT(vp);
233 ASSERT(vp->v_file);
234 ASSERT(seg == UIO_SYSSPACE);
663e02a1 235 ASSERT((ioflag & ~FAPPEND) == 0);
937879f1 236 ASSERT(x2 == RLIM64_INFINITY);
4b171585 237
e4f1d29f 238 fp = vp->v_file;
4b171585 239
663e02a1
RC
240 offset = off;
241 if (ioflag & FAPPEND)
242 offset = fp->f_pos;
243
4b171585 244 /* Writable user data segment must be briefly increased for this
245 * process so we can use the user space read call paths to write
246 * in to memory allocated by the kernel. */
247 saved_fs = get_fs();
248 set_fs(get_ds());
249
250 if (uio & UIO_WRITE)
251 rc = vfs_write(fp, addr, len, &offset);
252 else
253 rc = vfs_read(fp, addr, len, &offset);
254
255 set_fs(saved_fs);
f3989ed3 256 fp->f_pos = offset;
4b171585 257
258 if (rc < 0)
b17edc10 259 SRETURN(-rc);
0b3cf046 260
4b171585 261 if (residp) {
262 *residp = len - rc;
0b3cf046 263 } else {
4b171585 264 if (rc != len)
b17edc10 265 SRETURN(EIO);
0b3cf046 266 }
267
b17edc10 268 SRETURN(0);
4b171585 269} /* vn_rdwr() */
270EXPORT_SYMBOL(vn_rdwr);
271
272int
2f5d55aa 273vn_close(vnode_t *vp, int flags, int x1, int x2, void *x3, void *x4)
4b171585 274{
275 int rc;
b17edc10 276 SENTRY;
4b171585 277
937879f1 278 ASSERT(vp);
279 ASSERT(vp->v_file);
4b171585 280
4be55565 281 mapping_set_gfp_mask(vp->v_file->f_mapping, vp->v_gfp_mask);
97735c39
BB
282 rc = filp_close(vp->v_file, 0);
283 vn_free(vp);
4b171585 284
b17edc10 285 SRETURN(-rc);
4b171585 286} /* vn_close() */
287EXPORT_SYMBOL(vn_close);
288
97735c39
BB
289/* vn_seek() does not actually seek it only performs bounds checking on the
290 * proposed seek. We perform minimal checking and allow vn_rdwr() to catch
291 * anything more serious. */
292int
47995fa6 293vn_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, void *ct)
97735c39
BB
294{
295 return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
296}
297EXPORT_SYMBOL(vn_seek);
298
ec18fe3c
RY
299/*
300 * spl_basename() takes a NULL-terminated string s as input containing a path.
301 * It returns a char pointer to a string and a length that describe the
302 * basename of the path. If the basename is not "." or "/", it will be an index
303 * into the string. While the string should be NULL terminated, the section
304 * referring to the basename is not. spl_basename is dual-licensed GPLv2+ and
305 * CC0. Anyone wishing to reuse it in another codebase may pick either license.
306 */
307static void
308spl_basename(const char *s, const char **str, int *len)
309{
310 size_t i, end;
311
312 ASSERT(str);
313 ASSERT(len);
314
315 if (!s || !*s) {
316 *str = ".";
317 *len = 1;
318 return;
319 }
320
321 i = strlen(s) - 1;
322
323 while (i && s[i--] == '/');
324
325 if (i == 0) {
326 *str = "/";
327 *len = 1;
328 return;
329 }
330
331 end = i;
332
333 for (end = i; i; i--) {
334 if (s[i] == '/') {
335 *str = &s[i+1];
336 *len = end - i + 1;
337 return;
338 }
339 }
340
341 *str = s;
342 *len = end + 1;
343}
344
345static struct dentry *
346spl_kern_path_locked(const char *name, struct path *path)
347{
348 struct path parent;
349 struct dentry *dentry;
350 const char *basename;
351 int len;
352 int rc;
353
354 ASSERT(name);
355 ASSERT(path);
356
357 spl_basename(name, &basename, &len);
358
359 /* We do not accept "." or ".." */
360 if (len <= 2 && basename[0] == '.')
361 if (len == 1 || basename[1] == '.')
362 return (ERR_PTR(-EACCES));
363
364 rc = kern_path(name, LOOKUP_PARENT, &parent);
365 if (rc)
366 return (ERR_PTR(rc));
367
2bc5666f 368 spl_inode_lock(parent.dentry->d_inode);
ec18fe3c
RY
369
370 dentry = lookup_one_len(basename, parent.dentry, len);
371 if (IS_ERR(dentry)) {
372 spl_inode_unlock(parent.dentry->d_inode);
373 path_put(&parent);
374 } else {
375 *path = parent;
376 }
377
378 return (dentry);
379}
380
bcb15891
YS
381/* Based on do_unlinkat() from linux/fs/namei.c */
382int
383vn_remove(const char *path, uio_seg_t seg, int flags)
384{
385 struct dentry *dentry;
386 struct path parent;
387 struct inode *inode = NULL;
388 int rc = 0;
389 SENTRY;
390
391 ASSERT(seg == UIO_SYSSPACE);
392 ASSERT(flags == RMFILE);
393
394 dentry = spl_kern_path_locked(path, &parent);
395 rc = PTR_ERR(dentry);
396 if (!IS_ERR(dentry)) {
397 if (parent.dentry->d_name.name[parent.dentry->d_name.len])
398 SGOTO(slashes, rc = 0);
399
400 inode = dentry->d_inode;
bcb15891 401 if (inode)
ec18fe3c
RY
402 atomic_inc(&inode->i_count);
403 else
404 SGOTO(slashes, rc = 0);
bcb15891 405
50a0749e 406#ifdef HAVE_2ARGS_VFS_UNLINK
bcb15891 407 rc = vfs_unlink(parent.dentry->d_inode, dentry);
50a0749e
RY
408#else
409 rc = vfs_unlink(parent.dentry->d_inode, dentry, NULL);
410#endif /* HAVE_2ARGS_VFS_UNLINK */
bcb15891
YS
411exit1:
412 dput(dentry);
053678f3
BB
413 } else {
414 return (-rc);
bcb15891
YS
415 }
416
417 spl_inode_unlock(parent.dentry->d_inode);
418 if (inode)
419 iput(inode); /* truncate the inode here */
420
421 path_put(&parent);
422 SRETURN(-rc);
423
424slashes:
425 rc = !dentry->d_inode ? -ENOENT :
426 S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
427 SGOTO(exit1, rc);
428} /* vn_remove() */
429EXPORT_SYMBOL(vn_remove);
430
431/* Based on do_rename() from linux/fs/namei.c */
432int
433vn_rename(const char *oldname, const char *newname, int x1)
434{
435 struct dentry *old_dir, *new_dir;
436 struct dentry *old_dentry, *new_dentry;
437 struct dentry *trap;
438 struct path old_parent, new_parent;
439 int rc = 0;
440 SENTRY;
441
442 old_dentry = spl_kern_path_locked(oldname, &old_parent);
443 if (IS_ERR(old_dentry))
444 SGOTO(exit, rc = PTR_ERR(old_dentry));
445
446 spl_inode_unlock(old_parent.dentry->d_inode);
447
448 new_dentry = spl_kern_path_locked(newname, &new_parent);
449 if (IS_ERR(new_dentry))
450 SGOTO(exit2, rc = PTR_ERR(new_dentry));
451
452 spl_inode_unlock(new_parent.dentry->d_inode);
453
454 rc = -EXDEV;
455 if (old_parent.mnt != new_parent.mnt)
456 SGOTO(exit3, rc);
457
458 old_dir = old_parent.dentry;
459 new_dir = new_parent.dentry;
460 trap = lock_rename(new_dir, old_dir);
461
462 /* source should not be ancestor of target */
463 rc = -EINVAL;
464 if (old_dentry == trap)
465 SGOTO(exit4, rc);
466
467 /* target should not be an ancestor of source */
468 rc = -ENOTEMPTY;
469 if (new_dentry == trap)
470 SGOTO(exit4, rc);
471
472 /* source must exist */
473 rc = -ENOENT;
474 if (!old_dentry->d_inode)
475 SGOTO(exit4, rc);
476
477 /* unless the source is a directory trailing slashes give -ENOTDIR */
478 if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
479 rc = -ENOTDIR;
480 if (old_dentry->d_name.name[old_dentry->d_name.len])
481 SGOTO(exit4, rc);
482 if (new_dentry->d_name.name[new_dentry->d_name.len])
483 SGOTO(exit4, rc);
484 }
485
ad3412ef 486#if defined(HAVE_4ARGS_VFS_RENAME)
bcb15891 487 rc = vfs_rename(old_dir->d_inode, old_dentry,
50a0749e 488 new_dir->d_inode, new_dentry);
ad3412ef 489#elif defined(HAVE_5ARGS_VFS_RENAME)
50a0749e
RY
490 rc = vfs_rename(old_dir->d_inode, old_dentry,
491 new_dir->d_inode, new_dentry, NULL);
ad3412ef
CC
492#else
493 rc = vfs_rename(old_dir->d_inode, old_dentry,
494 new_dir->d_inode, new_dentry, NULL, 0);
495#endif
bcb15891
YS
496exit4:
497 unlock_rename(new_dir, old_dir);
498exit3:
499 dput(new_dentry);
500 path_put(&new_parent);
501exit2:
502 dput(old_dentry);
503 path_put(&old_parent);
504exit:
505 SRETURN(-rc);
506}
507EXPORT_SYMBOL(vn_rename);
508
4b171585 509int
36e6f861 510vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4)
0b3cf046 511{
4b171585 512 struct file *fp;
dcd9cb5a 513 struct kstat stat;
4b171585 514 int rc;
b17edc10 515 SENTRY;
4b171585 516
937879f1 517 ASSERT(vp);
518 ASSERT(vp->v_file);
519 ASSERT(vap);
4b171585 520
e4f1d29f 521 fp = vp->v_file;
4b171585 522
2a305c34
RY
523#ifdef HAVE_2ARGS_VFS_GETATTR
524 rc = vfs_getattr(&fp->f_path, &stat);
525#else
526 rc = vfs_getattr(fp->f_path.mnt, fp->f_dentry, &stat);
527#endif
4b171585 528 if (rc)
b17edc10 529 SRETURN(-rc);
4b171585 530
4295b530 531 vap->va_type = vn_mode_to_vtype(stat.mode);
4b171585 532 vap->va_mode = stat.mode;
f7fd6ddd
RY
533 vap->va_uid = KUID_TO_SUID(stat.uid);
534 vap->va_gid = KGID_TO_SGID(stat.gid);
4b171585 535 vap->va_fsid = 0;
536 vap->va_nodeid = stat.ino;
537 vap->va_nlink = stat.nlink;
538 vap->va_size = stat.size;
47995fa6 539 vap->va_blksize = stat.blksize;
dcd9cb5a
BB
540 vap->va_atime = stat.atime;
541 vap->va_mtime = stat.mtime;
542 vap->va_ctime = stat.ctime;
4b171585 543 vap->va_rdev = stat.rdev;
dcd9cb5a 544 vap->va_nblocks = stat.blocks;
4b171585 545
dcd9cb5a 546 SRETURN(0);
0b3cf046 547}
4b171585 548EXPORT_SYMBOL(vn_getattr);
549
2f5d55aa 550int vn_fsync(vnode_t *vp, int flags, void *x3, void *x4)
4b171585 551{
36e6f861 552 int datasync = 0;
b17edc10 553 SENTRY;
36e6f861 554
937879f1 555 ASSERT(vp);
556 ASSERT(vp->v_file);
4b171585 557
36e6f861 558 if (flags & FDSYNC)
559 datasync = 1;
560
b17edc10 561 SRETURN(-spl_filp_fsync(vp->v_file, datasync));
4b171585 562} /* vn_fsync() */
563EXPORT_SYMBOL(vn_fsync);
af828292 564
bbdc6ae4
ED
565int vn_space(vnode_t *vp, int cmd, struct flock *bfp, int flag,
566 offset_t offset, void *x6, void *x7)
567{
568 int error = EOPNOTSUPP;
569 SENTRY;
570
571 if (cmd != F_FREESP || bfp->l_whence != 0)
572 SRETURN(EOPNOTSUPP);
573
574 ASSERT(vp);
575 ASSERT(vp->v_file);
576 ASSERT(bfp->l_start >= 0 && bfp->l_len > 0);
577
578#ifdef FALLOC_FL_PUNCH_HOLE
1c7b3eaf
BB
579 /*
580 * When supported by the underlying file system preferentially
581 * use the fallocate() callback to preallocate the space.
582 */
583 error = -spl_filp_fallocate(vp->v_file,
584 FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
585 bfp->l_start, bfp->l_len);
586 if (error == 0)
587 SRETURN(0);
bbdc6ae4
ED
588#endif
589
590#ifdef HAVE_INODE_TRUNCATE_RANGE
591 if (vp->v_file->f_dentry && vp->v_file->f_dentry->d_inode &&
592 vp->v_file->f_dentry->d_inode->i_op &&
593 vp->v_file->f_dentry->d_inode->i_op->truncate_range) {
594 off_t end = bfp->l_start + bfp->l_len;
595 /*
596 * Judging from the code in shmem_truncate_range(),
597 * it seems the kernel expects the end offset to be
598 * inclusive and aligned to the end of a page.
599 */
600 if (end % PAGE_SIZE != 0) {
601 end &= ~(off_t)(PAGE_SIZE - 1);
602 if (end <= bfp->l_start)
603 SRETURN(0);
604 }
605 --end;
606
607 vp->v_file->f_dentry->d_inode->i_op->truncate_range(
608 vp->v_file->f_dentry->d_inode,
609 bfp->l_start, end
610 );
611 SRETURN(0);
612 }
613#endif
614
615 SRETURN(error);
616}
617EXPORT_SYMBOL(vn_space);
618
e4f1d29f 619/* Function must be called while holding the vn_file_lock */
620static file_t *
621file_find(int fd)
622{
623 file_t *fp;
624
937879f1 625 ASSERT(spin_is_locked(&vn_file_lock));
e4f1d29f 626
627 list_for_each_entry(fp, &vn_file_list, f_list) {
763b2f3b 628 if (fd == fp->f_fd && fp->f_task == current) {
937879f1 629 ASSERT(atomic_read(&fp->f_ref) != 0);
e4f1d29f 630 return fp;
631 }
632 }
633
634 return NULL;
635} /* file_find() */
636
637file_t *
638vn_getf(int fd)
639{
640 struct kstat stat;
641 struct file *lfp;
642 file_t *fp;
643 vnode_t *vp;
937879f1 644 int rc = 0;
b17edc10 645 SENTRY;
e4f1d29f 646
647 /* Already open just take an extra reference */
648 spin_lock(&vn_file_lock);
649
650 fp = file_find(fd);
651 if (fp) {
652 atomic_inc(&fp->f_ref);
653 spin_unlock(&vn_file_lock);
b17edc10 654 SRETURN(fp);
e4f1d29f 655 }
656
657 spin_unlock(&vn_file_lock);
658
659 /* File was not yet opened create the object and setup */
4afaaefa 660 fp = kmem_cache_alloc(vn_file_cache, KM_SLEEP);
e4f1d29f 661 if (fp == NULL)
b17edc10 662 SGOTO(out, rc);
e4f1d29f 663
664 mutex_enter(&fp->f_lock);
665
666 fp->f_fd = fd;
763b2f3b 667 fp->f_task = current;
e4f1d29f 668 fp->f_offset = 0;
669 atomic_inc(&fp->f_ref);
670
671 lfp = fget(fd);
672 if (lfp == NULL)
b17edc10 673 SGOTO(out_mutex, rc);
e4f1d29f 674
675 vp = vn_alloc(KM_SLEEP);
676 if (vp == NULL)
b17edc10 677 SGOTO(out_fget, rc);
e4f1d29f 678
2a305c34
RY
679#ifdef HAVE_2ARGS_VFS_GETATTR
680 rc = vfs_getattr(&lfp->f_path, &stat);
681#else
682 rc = vfs_getattr(lfp->f_path.mnt, lfp->f_dentry, &stat);
683#endif
684 if (rc)
b17edc10 685 SGOTO(out_vnode, rc);
e4f1d29f 686
687 mutex_enter(&vp->v_lock);
4295b530 688 vp->v_type = vn_mode_to_vtype(stat.mode);
e4f1d29f 689 vp->v_file = lfp;
690 mutex_exit(&vp->v_lock);
691
692 fp->f_vnode = vp;
693 fp->f_file = lfp;
694
695 /* Put it on the tracking list */
696 spin_lock(&vn_file_lock);
697 list_add(&fp->f_list, &vn_file_list);
698 spin_unlock(&vn_file_lock);
699
700 mutex_exit(&fp->f_lock);
b17edc10 701 SRETURN(fp);
e4f1d29f 702
703out_vnode:
e4f1d29f 704 vn_free(vp);
705out_fget:
e4f1d29f 706 fput(lfp);
707out_mutex:
e4f1d29f 708 mutex_exit(&fp->f_lock);
709 kmem_cache_free(vn_file_cache, fp);
710out:
b17edc10 711 SRETURN(NULL);
e4f1d29f 712} /* getf() */
713EXPORT_SYMBOL(getf);
714
715static void releasef_locked(file_t *fp)
716{
937879f1 717 ASSERT(fp->f_file);
718 ASSERT(fp->f_vnode);
e4f1d29f 719
720 /* Unlinked from list, no refs, safe to free outside mutex */
721 fput(fp->f_file);
722 vn_free(fp->f_vnode);
723
724 kmem_cache_free(vn_file_cache, fp);
725}
726
727void
728vn_releasef(int fd)
729{
730 file_t *fp;
b17edc10 731 SENTRY;
e4f1d29f 732
733 spin_lock(&vn_file_lock);
734 fp = file_find(fd);
735 if (fp) {
736 atomic_dec(&fp->f_ref);
737 if (atomic_read(&fp->f_ref) > 0) {
738 spin_unlock(&vn_file_lock);
b17edc10 739 SEXIT;
e4f1d29f 740 return;
741 }
742
743 list_del(&fp->f_list);
744 releasef_locked(fp);
745 }
746 spin_unlock(&vn_file_lock);
747
b17edc10 748 SEXIT;
e4f1d29f 749 return;
750} /* releasef() */
751EXPORT_SYMBOL(releasef);
752
137af025
BB
753static void
754#ifdef HAVE_SET_FS_PWD_WITH_CONST
755vn_set_fs_pwd(struct fs_struct *fs, const struct path *path)
756#else
757vn_set_fs_pwd(struct fs_struct *fs, struct path *path)
758#endif /* HAVE_SET_FS_PWD_WITH_CONST */
51a727e9 759{
9b2048c2
BB
760 struct path old_pwd;
761
137af025 762#ifdef HAVE_FS_STRUCT_SPINLOCK
9b2048c2
BB
763 spin_lock(&fs->lock);
764 old_pwd = fs->pwd;
765 fs->pwd = *path;
766 path_get(path);
767 spin_unlock(&fs->lock);
137af025 768#else
9b2048c2
BB
769 write_lock(&fs->lock);
770 old_pwd = fs->pwd;
771 fs->pwd = *path;
772 path_get(path);
773 write_unlock(&fs->lock);
137af025 774#endif /* HAVE_FS_STRUCT_SPINLOCK */
9b2048c2
BB
775
776 if (old_pwd.dentry)
777 path_put(&old_pwd);
51a727e9 778}
51a727e9
BB
779
780int
781vn_set_pwd(const char *filename)
782{
51a727e9 783 struct path path;
82a358d9 784 mm_segment_t saved_fs;
51a727e9 785 int rc;
b17edc10 786 SENTRY;
51a727e9 787
82a358d9
BB
788 /*
789 * user_path_dir() and __user_walk() both expect 'filename' to be
790 * a user space address so we must briefly increase the data segment
791 * size to ensure strncpy_from_user() does not fail with -EFAULT.
792 */
793 saved_fs = get_fs();
794 set_fs(get_ds());
795
51a727e9
BB
796 rc = user_path_dir(filename, &path);
797 if (rc)
b17edc10 798 SGOTO(out, rc);
51a727e9
BB
799
800 rc = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS);
801 if (rc)
b17edc10 802 SGOTO(dput_and_out, rc);
51a727e9 803
137af025 804 vn_set_fs_pwd(current->fs, &path);
51a727e9
BB
805
806dput_and_out:
807 path_put(&path);
51a727e9 808out:
82a358d9
BB
809 set_fs(saved_fs);
810
b17edc10 811 SRETURN(-rc);
51a727e9
BB
812} /* vn_set_pwd() */
813EXPORT_SYMBOL(vn_set_pwd);
814
af828292 815static int
816vn_cache_constructor(void *buf, void *cdrarg, int kmflags)
817{
818 struct vnode *vp = buf;
819
820 mutex_init(&vp->v_lock, NULL, MUTEX_DEFAULT, NULL);
821
822 return (0);
823} /* vn_cache_constructor() */
824
825static void
826vn_cache_destructor(void *buf, void *cdrarg)
827{
828 struct vnode *vp = buf;
829
830 mutex_destroy(&vp->v_lock);
831} /* vn_cache_destructor() */
832
e4f1d29f 833static int
834vn_file_cache_constructor(void *buf, void *cdrarg, int kmflags)
835{
836 file_t *fp = buf;
837
838 atomic_set(&fp->f_ref, 0);
839 mutex_init(&fp->f_lock, NULL, MUTEX_DEFAULT, NULL);
4e62fd41 840 INIT_LIST_HEAD(&fp->f_list);
e4f1d29f 841
842 return (0);
843} /* file_cache_constructor() */
844
845static void
846vn_file_cache_destructor(void *buf, void *cdrarg)
847{
848 file_t *fp = buf;
849
850 mutex_destroy(&fp->f_lock);
851} /* vn_file_cache_destructor() */
852
af828292 853int
12ff95ff 854spl_vn_init(void)
af828292 855{
b17edc10 856 SENTRY;
57d86234 857 vn_cache = kmem_cache_create("spl_vn_cache",
858 sizeof(struct vnode), 64,
5d86345d 859 vn_cache_constructor,
860 vn_cache_destructor,
a5b40eed 861 NULL, NULL, NULL, KMC_KMEM);
e4f1d29f 862
863 vn_file_cache = kmem_cache_create("spl_vn_file_cache",
864 sizeof(file_t), 64,
865 vn_file_cache_constructor,
866 vn_file_cache_destructor,
a5b40eed 867 NULL, NULL, NULL, KMC_KMEM);
b17edc10 868 SRETURN(0);
af828292 869} /* vn_init() */
870
871void
12ff95ff 872spl_vn_fini(void)
af828292 873{
e4f1d29f 874 file_t *fp, *next_fp;
2fb9b26a 875 int leaked = 0;
b17edc10 876 SENTRY;
e4f1d29f 877
878 spin_lock(&vn_file_lock);
879
880 list_for_each_entry_safe(fp, next_fp, &vn_file_list, f_list) {
881 list_del(&fp->f_list);
882 releasef_locked(fp);
883 leaked++;
884 }
885
e4f1d29f 886 spin_unlock(&vn_file_lock);
887
888 if (leaked > 0)
b17edc10 889 SWARN("Warning %d files leaked\n", leaked);
e4f1d29f 890
2371321e 891 kmem_cache_destroy(vn_file_cache);
2fb9b26a 892 kmem_cache_destroy(vn_cache);
e4f1d29f 893
b17edc10 894 SEXIT;
e4f1d29f 895 return;
af828292 896} /* vn_fini() */