]> git.proxmox.com Git - mirror_spl-debian.git/blob - modules/spl/spl-vnode.c
OK, some pretty substantial rework here. I've merged the spl-file
[mirror_spl-debian.git] / modules / spl / spl-vnode.c
1 #include <sys/sysmacros.h>
2 #include <sys/vnode.h>
3 #include "config.h"
4
5 void *rootdir = NULL;
6 EXPORT_SYMBOL(rootdir);
7
8 static kmem_cache_t *vn_cache;
9 static kmem_cache_t *vn_file_cache;
10
11 static spinlock_t vn_file_lock = SPIN_LOCK_UNLOCKED;
12 static LIST_HEAD(vn_file_list);
13
14 static vtype_t
15 vn_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
44 vnode_t *
45 vn_alloc(int flag)
46 {
47 vnode_t *vp;
48
49 vp = kmem_cache_alloc(vn_cache, flag);
50 if (vp != NULL) {
51 vp->v_file = NULL;
52 vp->v_type = 0;
53 }
54
55 return (vp);
56 } /* vn_alloc() */
57 EXPORT_SYMBOL(vn_alloc);
58
59 void
60 vn_free(vnode_t *vp)
61 {
62 kmem_cache_free(vn_cache, vp);
63 } /* vn_free() */
64 EXPORT_SYMBOL(vn_free);
65
66 int
67 vn_open(const char *path, uio_seg_t seg, int flags, int mode,
68 vnode_t **vpp, int x1, void *x2)
69 {
70 struct file *fp;
71 struct kstat stat;
72 int rc, saved_umask, flags_rw;
73 vnode_t *vp;
74
75 BUG_ON(seg != UIO_SYSSPACE);
76 BUG_ON(!vpp);
77 *vpp = NULL;
78
79 if (!(flags & FCREAT) && (flags & FWRITE))
80 flags |= FEXCL;
81
82 flags_rw = flags & (FWRITE | FREAD);
83 flags &= ~(FWRITE | FREAD);
84 switch (flags_rw) {
85 case FWRITE: flags |= O_WRONLY;
86 case FREAD: flags |= O_RDONLY;
87 case (FWRITE | FREAD): flags |= O_RDWR;
88 }
89
90 if (flags & FCREAT)
91 saved_umask = xchg(&current->fs->umask, 0);
92
93 fp = filp_open(path, flags, mode);
94
95 if (flags & FCREAT)
96 (void)xchg(&current->fs->umask, saved_umask);
97
98 if (IS_ERR(fp))
99 return PTR_ERR(fp);
100
101 rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat);
102 if (rc) {
103 filp_close(fp, 0);
104 return rc;
105 }
106
107 vp = vn_alloc(KM_SLEEP);
108 if (!vp) {
109 filp_close(fp, 0);
110 return -ENOMEM;
111 }
112
113 mutex_enter(&vp->v_lock);
114 vp->v_type = vn_get_sol_type(stat.mode);
115 vp->v_file = fp;
116 *vpp = vp;
117 mutex_exit(&vp->v_lock);
118
119 return 0;
120 } /* vn_open() */
121 EXPORT_SYMBOL(vn_open);
122
123 int
124 vn_openat(const char *path, uio_seg_t seg, int flags, int mode,
125 vnode_t **vpp, int x1, void *x2, vnode_t *vp, int fd)
126 {
127 char *realpath;
128 int rc;
129
130 BUG_ON(vp != rootdir);
131
132 realpath = kmalloc(strlen(path) + 2, GFP_KERNEL);
133 if (!realpath)
134 return -ENOMEM;
135
136 sprintf(realpath, "/%s", path);
137 rc = vn_open(realpath, seg, flags, mode, vpp, x1, x2);
138
139 kfree(realpath);
140
141 return rc;
142 } /* vn_openat() */
143 EXPORT_SYMBOL(vn_openat);
144
145 int
146 vn_rdwr(uio_rw_t uio, vnode_t *vp, void *addr, ssize_t len, offset_t off,
147 uio_seg_t seg, int x1, rlim64_t x2, void *x3, ssize_t *residp)
148 {
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);
156 BUG_ON(!vp->v_file);
157 BUG_ON(seg != UIO_SYSSPACE);
158 BUG_ON(x1 != 0);
159 BUG_ON(x2 != RLIM64_INFINITY);
160
161 offset = off;
162 fp = vp->v_file;
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)
178 return rc;
179
180 if (residp) {
181 *residp = len - rc;
182 } else {
183 if (rc != len)
184 return -EIO;
185 }
186
187 return 0;
188 } /* vn_rdwr() */
189 EXPORT_SYMBOL(vn_rdwr);
190
191 int
192 vn_close(vnode_t *vp, int flags, int x1, int x2, void *x3, void *x4)
193 {
194 int rc;
195
196 BUG_ON(!vp);
197 BUG_ON(!vp->v_file);
198
199 rc = filp_close(vp->v_file, 0);
200 vn_free(vp);
201
202 return rc;
203 } /* vn_close() */
204 EXPORT_SYMBOL(vn_close);
205
206 static 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 */
212 int
213 vn_remove(const char *path, uio_seg_t seg, int flags)
214 {
215 struct dentry *dentry;
216 struct nameidata nd;
217 struct inode *inode = NULL;
218 int rc = 0;
219
220 BUG_ON(seg != UIO_SYSSPACE);
221 BUG_ON(flags != RMFILE);
222
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);
242 exit2:
243 dput(dentry);
244 }
245 mutex_unlock(&nd.dentry->d_inode->i_mutex);
246 if (inode)
247 iput(inode); /* truncate the inode here */
248 exit1:
249 path_release(&nd);
250 exit:
251 return rc;
252
253 slashes:
254 rc = !dentry->d_inode ? -ENOENT :
255 S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
256 goto exit2;
257 } /* vn_remove() */
258 EXPORT_SYMBOL(vn_remove);
259
260 /* Modified do_rename() from linux/fs/namei.c, only uses exported symbols */
261 int
262 vn_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);
330 exit5:
331 dput(new_dentry);
332 exit4:
333 dput(old_dentry);
334 exit3:
335 unlock_rename(new_dir, old_dir);
336 exit2:
337 path_release(&newnd);
338 exit1:
339 path_release(&oldnd);
340 exit:
341 return rc;
342 }
343 EXPORT_SYMBOL(vn_rename);
344
345 int
346 vn_getattr(vnode_t *vp, vattr_t *vap, int flags, void *x3, void *x4)
347 {
348 struct file *fp;
349 struct kstat stat;
350 int rc;
351
352 BUG_ON(!vp);
353 BUG_ON(!vp->v_file);
354 BUG_ON(!vap);
355
356 fp = vp->v_file;
357
358 rc = vfs_getattr(fp->f_vfsmnt, fp->f_dentry, &stat);
359 if (rc)
360 return rc;
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
380 return rc;
381 }
382 EXPORT_SYMBOL(vn_getattr);
383
384 int vn_fsync(vnode_t *vp, int flags, void *x3, void *x4)
385 {
386 int datasync = 0;
387
388 BUG_ON(!vp);
389 BUG_ON(!vp->v_file);
390
391 if (flags & FDSYNC)
392 datasync = 1;
393
394 return file_fsync(vp->v_file, vp->v_file->f_dentry, datasync);
395 } /* vn_fsync() */
396 EXPORT_SYMBOL(vn_fsync);
397
398 /* Function must be called while holding the vn_file_lock */
399 static file_t *
400 file_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
416 file_t *
417 vn_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);
431 printk("found file\n");
432 return fp;
433 }
434
435 spin_unlock(&vn_file_lock);
436
437 /* File was not yet opened create the object and setup */
438 fp = kmem_cache_alloc(vn_file_cache, 0);
439 if (fp == NULL)
440 goto out;
441
442 mutex_enter(&fp->f_lock);
443
444 fp->f_fd = fd;
445 fp->f_offset = 0;
446 atomic_inc(&fp->f_ref);
447
448 lfp = fget(fd);
449 if (lfp == NULL)
450 goto out_mutex;
451
452 vp = vn_alloc(KM_SLEEP);
453 if (vp == NULL)
454 goto out_fget;
455
456 if (vfs_getattr(lfp->f_vfsmnt, lfp->f_dentry, &stat))
457 goto out_vnode;
458
459 mutex_enter(&vp->v_lock);
460 vp->v_type = vn_get_sol_type(stat.mode);
461 vp->v_file = lfp;
462 mutex_exit(&vp->v_lock);
463
464 fp->f_vnode = vp;
465 fp->f_file = lfp;
466
467 /* Put it on the tracking list */
468 spin_lock(&vn_file_lock);
469 list_add(&fp->f_list, &vn_file_list);
470 spin_unlock(&vn_file_lock);
471
472 mutex_exit(&fp->f_lock);
473 return fp;
474
475 out_vnode:
476 printk("out_vnode\n");
477 vn_free(vp);
478 out_fget:
479 printk("out_fget\n");
480 fput(lfp);
481 out_mutex:
482 printk("out_mutex\n");
483 mutex_exit(&fp->f_lock);
484 kmem_cache_free(vn_file_cache, fp);
485 out:
486 printk("out\n");
487 return NULL;
488 } /* getf() */
489 EXPORT_SYMBOL(getf);
490
491 static void releasef_locked(file_t *fp)
492 {
493 BUG_ON(fp->f_file == NULL);
494 BUG_ON(fp->f_vnode == NULL);
495
496 /* Unlinked from list, no refs, safe to free outside mutex */
497 fput(fp->f_file);
498 vn_free(fp->f_vnode);
499
500 kmem_cache_free(vn_file_cache, fp);
501 }
502
503 void
504 vn_releasef(int fd)
505 {
506 file_t *fp;
507
508 spin_lock(&vn_file_lock);
509 fp = file_find(fd);
510 if (fp) {
511 atomic_dec(&fp->f_ref);
512 if (atomic_read(&fp->f_ref) > 0) {
513 spin_unlock(&vn_file_lock);
514 return;
515 }
516
517 list_del(&fp->f_list);
518 releasef_locked(fp);
519 }
520 spin_unlock(&vn_file_lock);
521
522 return;
523 } /* releasef() */
524 EXPORT_SYMBOL(releasef);
525
526 static int
527 vn_cache_constructor(void *buf, void *cdrarg, int kmflags)
528 {
529 struct vnode *vp = buf;
530
531 mutex_init(&vp->v_lock, NULL, MUTEX_DEFAULT, NULL);
532
533 return (0);
534 } /* vn_cache_constructor() */
535
536 static void
537 vn_cache_destructor(void *buf, void *cdrarg)
538 {
539 struct vnode *vp = buf;
540
541 mutex_destroy(&vp->v_lock);
542 } /* vn_cache_destructor() */
543
544 static int
545 vn_file_cache_constructor(void *buf, void *cdrarg, int kmflags)
546 {
547 file_t *fp = buf;
548
549 atomic_set(&fp->f_ref, 0);
550 mutex_init(&fp->f_lock, NULL, MUTEX_DEFAULT, NULL);
551
552 return (0);
553 } /* file_cache_constructor() */
554
555 static void
556 vn_file_cache_destructor(void *buf, void *cdrarg)
557 {
558 file_t *fp = buf;
559
560 mutex_destroy(&fp->f_lock);
561 } /* vn_file_cache_destructor() */
562
563 int
564 vn_init(void)
565 {
566 vn_cache = kmem_cache_create("spl_vn_cache", sizeof(struct vnode), 64,
567 vn_cache_constructor,
568 vn_cache_destructor,
569 NULL, NULL, NULL, 0);
570
571 vn_file_cache = kmem_cache_create("spl_vn_file_cache",
572 sizeof(file_t), 64,
573 vn_file_cache_constructor,
574 vn_file_cache_destructor,
575 NULL, NULL, NULL, 0);
576 return 0;
577 } /* vn_init() */
578
579 void
580 vn_fini(void)
581 {
582 file_t *fp, *next_fp;
583 int rc, leaked = 0;
584
585 spin_lock(&vn_file_lock);
586
587 list_for_each_entry_safe(fp, next_fp, &vn_file_list, f_list) {
588 list_del(&fp->f_list);
589 releasef_locked(fp);
590 leaked++;
591 }
592
593 rc = kmem_cache_destroy(vn_file_cache);
594 if (rc)
595 printk("Warning leaked vn_file_cache objects\n");
596
597 vn_file_cache = NULL;
598 spin_unlock(&vn_file_lock);
599
600 if (leaked > 0)
601 printk("Warning: %d files leaked\n", leaked);
602
603 rc = kmem_cache_destroy(vn_cache);
604 if (rc)
605 printk("Warning leaked vn_cache objects\n");
606
607 return;
608 } /* vn_fini() */