]> git.proxmox.com Git - mirror_spl-debian.git/blame - modules/spl/spl-vnode.c
Frist minor batch of fixes. Catch a dropped ;, and use SBUG instead of BUG.
[mirror_spl-debian.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
e4f1d29f 8static kmem_cache_t *vn_cache;
9static kmem_cache_t *vn_file_cache;
10
11static spinlock_t vn_file_lock = SPIN_LOCK_UNLOCKED;
12static LIST_HEAD(vn_file_list);
af828292 13
4b171585 14static vtype_t
15vn_get_sol_type(umode_t mode)
16{
17 if (S_ISREG(mode))
18 return VREG;
19
20 if (S_ISDIR(mode))
21 return VDIR;
22
23 if (S_ISCHR(mode))
24 return VCHR;
25
26 if (S_ISBLK(mode))
27 return VBLK;
28
29 if (S_ISFIFO(mode))
30 return VFIFO;
31
32 if (S_ISLNK(mode))
33 return VLNK;
34
35 if (S_ISSOCK(mode))
36 return VSOCK;
37
38 if (S_ISCHR(mode))
39 return VCHR;
40
41 return VNON;
42} /* vn_get_sol_type() */
43
af828292 44vnode_t *
45vn_alloc(int flag)
46{
47 vnode_t *vp;
48
49 vp = kmem_cache_alloc(vn_cache, flag);
af828292 50 if (vp != NULL) {
e4f1d29f 51 vp->v_file = NULL;
af828292 52 vp->v_type = 0;
53 }
54
728b9dd8 55 return vp;
af828292 56} /* vn_alloc() */
57EXPORT_SYMBOL(vn_alloc);
58
59void
60vn_free(vnode_t *vp)
61{
62 kmem_cache_free(vn_cache, vp);
63} /* vn_free() */
64EXPORT_SYMBOL(vn_free);
65
0b3cf046 66int
af828292 67vn_open(const char *path, uio_seg_t seg, int flags, int mode,
4b171585 68 vnode_t **vpp, int x1, void *x2)
0b3cf046 69{
4b171585 70 struct file *fp;
71 struct kstat stat;
728b9dd8 72 int rc, saved_umask;
0b3cf046 73 vnode_t *vp;
0b3cf046 74
728b9dd8 75 BUG_ON(!(flags & (FWRITE | FREAD)));
4b171585 76 BUG_ON(seg != UIO_SYSSPACE);
77 BUG_ON(!vpp);
78 *vpp = NULL;
79
80 if (!(flags & FCREAT) && (flags & FWRITE))
81 flags |= FEXCL;
82
728b9dd8 83 /* Note for filp_open() the two low bits must be remapped to mean:
84 * 01 - read-only -> 00 read-only
85 * 10 - write-only -> 01 write-only
86 * 11 - read-write -> 10 read-write
87 */
88 flags--;
0b3cf046 89
90 if (flags & FCREAT)
4b171585 91 saved_umask = xchg(&current->fs->umask, 0);
0b3cf046 92
4b171585 93 fp = filp_open(path, flags, mode);
0b3cf046 94
95 if (flags & FCREAT)
4b171585 96 (void)xchg(&current->fs->umask, saved_umask);
0b3cf046 97
4b171585 98 if (IS_ERR(fp))
728b9dd8 99 return -PTR_ERR(fp);
0b3cf046 100
4b171585 101 rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat);
102 if (rc) {
103 filp_close(fp, 0);
728b9dd8 104 return -rc;
0b3cf046 105 }
106
af828292 107 vp = vn_alloc(KM_SLEEP);
4b171585 108 if (!vp) {
109 filp_close(fp, 0);
728b9dd8 110 return ENOMEM;
4b171585 111 }
0b3cf046 112
e4f1d29f 113 mutex_enter(&vp->v_lock);
4b171585 114 vp->v_type = vn_get_sol_type(stat.mode);
e4f1d29f 115 vp->v_file = fp;
4b171585 116 *vpp = vp;
e4f1d29f 117 mutex_exit(&vp->v_lock);
0b3cf046 118
4b171585 119 return 0;
120} /* vn_open() */
121EXPORT_SYMBOL(vn_open);
0b3cf046 122
0b3cf046 123int
af828292 124vn_openat(const char *path, uio_seg_t seg, int flags, int mode,
4b171585 125 vnode_t **vpp, int x1, void *x2, vnode_t *vp, int fd)
0b3cf046 126{
4b171585 127 char *realpath;
128 int rc;
0b3cf046 129
4b171585 130 BUG_ON(vp != rootdir);
0b3cf046 131
4b171585 132 realpath = kmalloc(strlen(path) + 2, GFP_KERNEL);
133 if (!realpath)
728b9dd8 134 return ENOMEM;
0b3cf046 135
4b171585 136 sprintf(realpath, "/%s", path);
137 rc = vn_open(realpath, seg, flags, mode, vpp, x1, x2);
0b3cf046 138
4b171585 139 kfree(realpath);
140
141 return rc;
142} /* vn_openat() */
143EXPORT_SYMBOL(vn_openat);
0b3cf046 144
0b3cf046 145int
4b171585 146vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, offset_t off,
af828292 147 uio_seg_t seg, int x1, rlim64_t x2, void *x3, ssize_t *residp)
0b3cf046 148{
4b171585 149 loff_t offset;
150 mm_segment_t saved_fs;
151 struct file *fp;
152 int rc;
153
154 BUG_ON(!(uio == UIO_WRITE || uio == UIO_READ));
155 BUG_ON(!vp);
e4f1d29f 156 BUG_ON(!vp->v_file);
4b171585 157 BUG_ON(seg != UIO_SYSSPACE);
158 BUG_ON(x1 != 0);
159 BUG_ON(x2 != RLIM64_INFINITY);
160
161 offset = off;
e4f1d29f 162 fp = vp->v_file;
4b171585 163
164 /* Writable user data segment must be briefly increased for this
165 * process so we can use the user space read call paths to write
166 * in to memory allocated by the kernel. */
167 saved_fs = get_fs();
168 set_fs(get_ds());
169
170 if (uio & UIO_WRITE)
171 rc = vfs_write(fp, addr, len, &offset);
172 else
173 rc = vfs_read(fp, addr, len, &offset);
174
175 set_fs(saved_fs);
176
177 if (rc < 0)
728b9dd8 178 return -rc;
0b3cf046 179
4b171585 180 if (residp) {
181 *residp = len - rc;
0b3cf046 182 } else {
4b171585 183 if (rc != len)
728b9dd8 184 return EIO;
0b3cf046 185 }
186
4b171585 187 return 0;
188} /* vn_rdwr() */
189EXPORT_SYMBOL(vn_rdwr);
190
191int
2f5d55aa 192vn_close(vnode_t *vp, int flags, int x1, int x2, void *x3, void *x4)
4b171585 193{
194 int rc;
195
196 BUG_ON(!vp);
e4f1d29f 197 BUG_ON(!vp->v_file);
4b171585 198
e4f1d29f 199 rc = filp_close(vp->v_file, 0);
af828292 200 vn_free(vp);
4b171585 201
728b9dd8 202 return -rc;
4b171585 203} /* vn_close() */
204EXPORT_SYMBOL(vn_close);
205
206static struct dentry *lookup_hash(struct nameidata *nd)
207{
208 return __lookup_hash(&nd->last, nd->dentry, nd);
209} /* lookup_hash() */
210
211/* Modified do_unlinkat() from linux/fs/namei.c, only uses exported symbols */
212int
af828292 213vn_remove(const char *path, uio_seg_t seg, int flags)
4b171585 214{
215 struct dentry *dentry;
216 struct nameidata nd;
217 struct inode *inode = NULL;
218 int rc = 0;
219
2f5d55aa 220 BUG_ON(seg != UIO_SYSSPACE);
221 BUG_ON(flags != RMFILE);
222
4b171585 223 rc = path_lookup(path, LOOKUP_PARENT, &nd);
224 if (rc)
225 goto exit;
226
227 rc = -EISDIR;
228 if (nd.last_type != LAST_NORM)
229 goto exit1;
230
231 mutex_lock_nested(&nd.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
232 dentry = lookup_hash(&nd);
233 rc = PTR_ERR(dentry);
234 if (!IS_ERR(dentry)) {
235 /* Why not before? Because we want correct rc value */
236 if (nd.last.name[nd.last.len])
237 goto slashes;
238 inode = dentry->d_inode;
239 if (inode)
240 atomic_inc(&inode->i_count);
241 rc = vfs_unlink(nd.dentry->d_inode, dentry);
242exit2:
243 dput(dentry);
244 }
245 mutex_unlock(&nd.dentry->d_inode->i_mutex);
246 if (inode)
247 iput(inode); /* truncate the inode here */
248exit1:
249 path_release(&nd);
250exit:
728b9dd8 251 return -rc;
4b171585 252
253slashes:
254 rc = !dentry->d_inode ? -ENOENT :
255 S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
256 goto exit2;
257} /* vn_remove() */
258EXPORT_SYMBOL(vn_remove);
259
260/* Modified do_rename() from linux/fs/namei.c, only uses exported symbols */
261int
262vn_rename(const char *oldname, const char *newname, int x1)
263{
264 struct dentry * old_dir, * new_dir;
265 struct dentry * old_dentry, *new_dentry;
266 struct dentry * trap;
267 struct nameidata oldnd, newnd;
268 int rc = 0;
269
270 rc = path_lookup(oldname, LOOKUP_PARENT, &oldnd);
271 if (rc)
272 goto exit;
273
274 rc = path_lookup(newname, LOOKUP_PARENT, &newnd);
275 if (rc)
276 goto exit1;
277
278 rc = -EXDEV;
279 if (oldnd.mnt != newnd.mnt)
280 goto exit2;
281
282 old_dir = oldnd.dentry;
283 rc = -EBUSY;
284 if (oldnd.last_type != LAST_NORM)
285 goto exit2;
286
287 new_dir = newnd.dentry;
288 if (newnd.last_type != LAST_NORM)
289 goto exit2;
290
291 trap = lock_rename(new_dir, old_dir);
292
293 old_dentry = lookup_hash(&oldnd);
294
295 rc = PTR_ERR(old_dentry);
296 if (IS_ERR(old_dentry))
297 goto exit3;
298
299 /* source must exist */
300 rc = -ENOENT;
301 if (!old_dentry->d_inode)
302 goto exit4;
303
304 /* unless the source is a directory trailing slashes give -ENOTDIR */
305 if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
306 rc = -ENOTDIR;
307 if (oldnd.last.name[oldnd.last.len])
308 goto exit4;
309 if (newnd.last.name[newnd.last.len])
310 goto exit4;
311 }
312
313 /* source should not be ancestor of target */
314 rc = -EINVAL;
315 if (old_dentry == trap)
316 goto exit4;
317
318 new_dentry = lookup_hash(&newnd);
319 rc = PTR_ERR(new_dentry);
320 if (IS_ERR(new_dentry))
321 goto exit4;
322
323 /* target should not be an ancestor of source */
324 rc = -ENOTEMPTY;
325 if (new_dentry == trap)
326 goto exit5;
327
328 rc = vfs_rename(old_dir->d_inode, old_dentry,
329 new_dir->d_inode, new_dentry);
330exit5:
331 dput(new_dentry);
332exit4:
333 dput(old_dentry);
334exit3:
335 unlock_rename(new_dir, old_dir);
336exit2:
337 path_release(&newnd);
338exit1:
339 path_release(&oldnd);
340exit:
728b9dd8 341 return -rc;
0b3cf046 342}
4b171585 343EXPORT_SYMBOL(vn_rename);
0b3cf046 344
4b171585 345int
36e6f861 346vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4)
0b3cf046 347{
4b171585 348 struct file *fp;
349 struct kstat stat;
350 int rc;
351
352 BUG_ON(!vp);
e4f1d29f 353 BUG_ON(!vp->v_file);
4b171585 354 BUG_ON(!vap);
355
e4f1d29f 356 fp = vp->v_file;
4b171585 357
358 rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat);
359 if (rc)
728b9dd8 360 return -rc;
4b171585 361
362 vap->va_type = vn_get_sol_type(stat.mode);
363 vap->va_mode = stat.mode;
364 vap->va_uid = stat.uid;
365 vap->va_gid = stat.gid;
366 vap->va_fsid = 0;
367 vap->va_nodeid = stat.ino;
368 vap->va_nlink = stat.nlink;
369 vap->va_size = stat.size;
370 vap->va_blocksize = stat.blksize;
371 vap->va_atime.tv_sec = stat.atime.tv_sec;
372 vap->va_atime.tv_usec = stat.atime.tv_nsec / NSEC_PER_USEC;
373 vap->va_mtime.tv_sec = stat.mtime.tv_sec;
374 vap->va_mtime.tv_usec = stat.mtime.tv_nsec / NSEC_PER_USEC;
375 vap->va_ctime.tv_sec = stat.ctime.tv_sec;
376 vap->va_ctime.tv_usec = stat.ctime.tv_nsec / NSEC_PER_USEC;
377 vap->va_rdev = stat.rdev;
378 vap->va_blocks = stat.blocks;
379
728b9dd8 380 return 0;
0b3cf046 381}
4b171585 382EXPORT_SYMBOL(vn_getattr);
383
2f5d55aa 384int vn_fsync(vnode_t *vp, int flags, void *x3, void *x4)
4b171585 385{
36e6f861 386 int datasync = 0;
387
4b171585 388 BUG_ON(!vp);
e4f1d29f 389 BUG_ON(!vp->v_file);
4b171585 390
36e6f861 391 if (flags & FDSYNC)
392 datasync = 1;
393
728b9dd8 394 return -file_fsync(vp->v_file, vp->v_file->f_dentry, datasync);
4b171585 395} /* vn_fsync() */
396EXPORT_SYMBOL(vn_fsync);
af828292 397
e4f1d29f 398/* Function must be called while holding the vn_file_lock */
399static file_t *
400file_find(int fd)
401{
402 file_t *fp;
403
404 BUG_ON(!spin_is_locked(&vn_file_lock));
405
406 list_for_each_entry(fp, &vn_file_list, f_list) {
407 if (fd == fp->f_fd) {
408 BUG_ON(atomic_read(&fp->f_ref) == 0);
409 return fp;
410 }
411 }
412
413 return NULL;
414} /* file_find() */
415
416file_t *
417vn_getf(int fd)
418{
419 struct kstat stat;
420 struct file *lfp;
421 file_t *fp;
422 vnode_t *vp;
423
424 /* Already open just take an extra reference */
425 spin_lock(&vn_file_lock);
426
427 fp = file_find(fd);
428 if (fp) {
429 atomic_inc(&fp->f_ref);
430 spin_unlock(&vn_file_lock);
e4f1d29f 431 return fp;
432 }
433
434 spin_unlock(&vn_file_lock);
435
436 /* File was not yet opened create the object and setup */
437 fp = kmem_cache_alloc(vn_file_cache, 0);
438 if (fp == NULL)
439 goto out;
440
441 mutex_enter(&fp->f_lock);
442
443 fp->f_fd = fd;
444 fp->f_offset = 0;
445 atomic_inc(&fp->f_ref);
446
447 lfp = fget(fd);
448 if (lfp == NULL)
449 goto out_mutex;
450
451 vp = vn_alloc(KM_SLEEP);
452 if (vp == NULL)
453 goto out_fget;
454
455 if (vfs_getattr(lfp->f_vfsmnt, lfp->f_dentry, &stat))
456 goto out_vnode;
457
458 mutex_enter(&vp->v_lock);
459 vp->v_type = vn_get_sol_type(stat.mode);
460 vp->v_file = lfp;
461 mutex_exit(&vp->v_lock);
462
463 fp->f_vnode = vp;
464 fp->f_file = lfp;
465
466 /* Put it on the tracking list */
467 spin_lock(&vn_file_lock);
468 list_add(&fp->f_list, &vn_file_list);
469 spin_unlock(&vn_file_lock);
470
471 mutex_exit(&fp->f_lock);
472 return fp;
473
474out_vnode:
e4f1d29f 475 vn_free(vp);
476out_fget:
e4f1d29f 477 fput(lfp);
478out_mutex:
e4f1d29f 479 mutex_exit(&fp->f_lock);
480 kmem_cache_free(vn_file_cache, fp);
481out:
e4f1d29f 482 return NULL;
483} /* getf() */
484EXPORT_SYMBOL(getf);
485
486static void releasef_locked(file_t *fp)
487{
488 BUG_ON(fp->f_file == NULL);
489 BUG_ON(fp->f_vnode == NULL);
490
491 /* Unlinked from list, no refs, safe to free outside mutex */
492 fput(fp->f_file);
493 vn_free(fp->f_vnode);
494
495 kmem_cache_free(vn_file_cache, fp);
496}
497
498void
499vn_releasef(int fd)
500{
501 file_t *fp;
502
503 spin_lock(&vn_file_lock);
504 fp = file_find(fd);
505 if (fp) {
506 atomic_dec(&fp->f_ref);
507 if (atomic_read(&fp->f_ref) > 0) {
508 spin_unlock(&vn_file_lock);
509 return;
510 }
511
512 list_del(&fp->f_list);
513 releasef_locked(fp);
514 }
515 spin_unlock(&vn_file_lock);
516
517 return;
518} /* releasef() */
519EXPORT_SYMBOL(releasef);
520
af828292 521static int
522vn_cache_constructor(void *buf, void *cdrarg, int kmflags)
523{
524 struct vnode *vp = buf;
525
526 mutex_init(&vp->v_lock, NULL, MUTEX_DEFAULT, NULL);
527
528 return (0);
529} /* vn_cache_constructor() */
530
531static void
532vn_cache_destructor(void *buf, void *cdrarg)
533{
534 struct vnode *vp = buf;
535
536 mutex_destroy(&vp->v_lock);
537} /* vn_cache_destructor() */
538
e4f1d29f 539static int
540vn_file_cache_constructor(void *buf, void *cdrarg, int kmflags)
541{
542 file_t *fp = buf;
543
544 atomic_set(&fp->f_ref, 0);
545 mutex_init(&fp->f_lock, NULL, MUTEX_DEFAULT, NULL);
4e62fd41 546 INIT_LIST_HEAD(&fp->f_list);
e4f1d29f 547
548 return (0);
549} /* file_cache_constructor() */
550
551static void
552vn_file_cache_destructor(void *buf, void *cdrarg)
553{
554 file_t *fp = buf;
555
556 mutex_destroy(&fp->f_lock);
557} /* vn_file_cache_destructor() */
558
af828292 559int
560vn_init(void)
561{
5d86345d 562 vn_cache = kmem_cache_create("spl_vn_cache", sizeof(struct vnode), 64,
563 vn_cache_constructor,
564 vn_cache_destructor,
af828292 565 NULL, NULL, NULL, 0);
e4f1d29f 566
567 vn_file_cache = kmem_cache_create("spl_vn_file_cache",
568 sizeof(file_t), 64,
569 vn_file_cache_constructor,
570 vn_file_cache_destructor,
571 NULL, NULL, NULL, 0);
af828292 572 return 0;
573} /* vn_init() */
574
575void
576vn_fini(void)
577{
e4f1d29f 578 file_t *fp, *next_fp;
579 int rc, leaked = 0;
580
581 spin_lock(&vn_file_lock);
582
583 list_for_each_entry_safe(fp, next_fp, &vn_file_list, f_list) {
584 list_del(&fp->f_list);
585 releasef_locked(fp);
586 leaked++;
587 }
588
589 rc = kmem_cache_destroy(vn_file_cache);
590 if (rc)
57d1b188 591 printk("spl: Warning leaked vn_file_cache objects, %d\n", rc);
e4f1d29f 592
593 vn_file_cache = NULL;
594 spin_unlock(&vn_file_lock);
595
596 if (leaked > 0)
8d0f1ee9 597 printk("spl: Warning %d files leaked\n", leaked);
e4f1d29f 598
599 rc = kmem_cache_destroy(vn_cache);
600 if (rc)
57d1b188 601 printk("spl: Warning leaked vn_cache objects, %d\n", rc);
e4f1d29f 602
603 return;
af828292 604} /* vn_fini() */