blob: aaf562b9f937460e07253f5c812cf25abafc0284 [file] [log] [blame]
Miklos Szeredie9be9d52014-10-24 00:14:38 +02001/*
2 *
3 * Copyright (C) 2011 Novell Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 */
9
10#include <linux/fs.h>
11#include <linux/namei.h>
12#include <linux/xattr.h>
13#include <linux/security.h>
14#include <linux/mount.h>
15#include <linux/slab.h>
16#include <linux/parser.h>
17#include <linux/module.h>
18#include <linux/sched.h>
Andy Whitcroftcc259632014-10-24 00:14:38 +020019#include <linux/statfs.h>
Miklos Szeredie9be9d52014-10-24 00:14:38 +020020#include "overlayfs.h"
21
22MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
23MODULE_DESCRIPTION("Overlay filesystem");
24MODULE_LICENSE("GPL");
25
Andy Whitcroftcc259632014-10-24 00:14:38 +020026#define OVERLAYFS_SUPER_MAGIC 0x794c764f
27
Miklos Szeredie9be9d52014-10-24 00:14:38 +020028/* private information held for overlayfs's superblock */
29struct ovl_fs {
30 struct vfsmount *upper_mnt;
31 struct vfsmount *lower_mnt;
32 struct dentry *workdir;
Andy Whitcroftcc259632014-10-24 00:14:38 +020033 long lower_namelen;
Miklos Szeredie9be9d52014-10-24 00:14:38 +020034};
35
36struct ovl_dir_cache;
37
38/* private information held for every overlayfs dentry */
39struct ovl_entry {
40 struct dentry *__upperdentry;
41 struct dentry *lowerdentry;
42 struct ovl_dir_cache *cache;
43 union {
44 struct {
45 u64 version;
46 bool opaque;
47 };
48 struct rcu_head rcu;
49 };
50};
51
52const char *ovl_opaque_xattr = "trusted.overlay.opaque";
53
54
55enum ovl_path_type ovl_path_type(struct dentry *dentry)
56{
57 struct ovl_entry *oe = dentry->d_fsdata;
58
59 if (oe->__upperdentry) {
60 if (oe->lowerdentry) {
61 if (S_ISDIR(dentry->d_inode->i_mode))
62 return OVL_PATH_MERGE;
63 else
64 return OVL_PATH_UPPER;
65 } else {
66 if (oe->opaque)
67 return OVL_PATH_UPPER;
68 else
69 return OVL_PATH_PURE_UPPER;
70 }
71 } else {
72 return OVL_PATH_LOWER;
73 }
74}
75
76static struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe)
77{
78 struct dentry *upperdentry = ACCESS_ONCE(oe->__upperdentry);
79 /*
80 * Make sure to order reads to upperdentry wrt ovl_dentry_update()
81 */
82 smp_read_barrier_depends();
83 return upperdentry;
84}
85
86void ovl_path_upper(struct dentry *dentry, struct path *path)
87{
88 struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
89 struct ovl_entry *oe = dentry->d_fsdata;
90
91 path->mnt = ofs->upper_mnt;
92 path->dentry = ovl_upperdentry_dereference(oe);
93}
94
95enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path)
96{
97
98 enum ovl_path_type type = ovl_path_type(dentry);
99
100 if (type == OVL_PATH_LOWER)
101 ovl_path_lower(dentry, path);
102 else
103 ovl_path_upper(dentry, path);
104
105 return type;
106}
107
108struct dentry *ovl_dentry_upper(struct dentry *dentry)
109{
110 struct ovl_entry *oe = dentry->d_fsdata;
111
112 return ovl_upperdentry_dereference(oe);
113}
114
115struct dentry *ovl_dentry_lower(struct dentry *dentry)
116{
117 struct ovl_entry *oe = dentry->d_fsdata;
118
119 return oe->lowerdentry;
120}
121
122struct dentry *ovl_dentry_real(struct dentry *dentry)
123{
124 struct ovl_entry *oe = dentry->d_fsdata;
125 struct dentry *realdentry;
126
127 realdentry = ovl_upperdentry_dereference(oe);
128 if (!realdentry)
129 realdentry = oe->lowerdentry;
130
131 return realdentry;
132}
133
134struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper)
135{
136 struct dentry *realdentry;
137
138 realdentry = ovl_upperdentry_dereference(oe);
139 if (realdentry) {
140 *is_upper = true;
141 } else {
142 realdentry = oe->lowerdentry;
143 *is_upper = false;
144 }
145 return realdentry;
146}
147
148struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
149{
150 struct ovl_entry *oe = dentry->d_fsdata;
151
152 return oe->cache;
153}
154
155void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache)
156{
157 struct ovl_entry *oe = dentry->d_fsdata;
158
159 oe->cache = cache;
160}
161
162void ovl_path_lower(struct dentry *dentry, struct path *path)
163{
164 struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
165 struct ovl_entry *oe = dentry->d_fsdata;
166
167 path->mnt = ofs->lower_mnt;
168 path->dentry = oe->lowerdentry;
169}
170
171int ovl_want_write(struct dentry *dentry)
172{
173 struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
174 return mnt_want_write(ofs->upper_mnt);
175}
176
177void ovl_drop_write(struct dentry *dentry)
178{
179 struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
180 mnt_drop_write(ofs->upper_mnt);
181}
182
183struct dentry *ovl_workdir(struct dentry *dentry)
184{
185 struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
186 return ofs->workdir;
187}
188
189bool ovl_dentry_is_opaque(struct dentry *dentry)
190{
191 struct ovl_entry *oe = dentry->d_fsdata;
192 return oe->opaque;
193}
194
195void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque)
196{
197 struct ovl_entry *oe = dentry->d_fsdata;
198 oe->opaque = opaque;
199}
200
201void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry)
202{
203 struct ovl_entry *oe = dentry->d_fsdata;
204
205 WARN_ON(!mutex_is_locked(&upperdentry->d_parent->d_inode->i_mutex));
206 WARN_ON(oe->__upperdentry);
207 BUG_ON(!upperdentry->d_inode);
208 /*
209 * Make sure upperdentry is consistent before making it visible to
210 * ovl_upperdentry_dereference().
211 */
212 smp_wmb();
213 oe->__upperdentry = upperdentry;
214}
215
216void ovl_dentry_version_inc(struct dentry *dentry)
217{
218 struct ovl_entry *oe = dentry->d_fsdata;
219
220 WARN_ON(!mutex_is_locked(&dentry->d_inode->i_mutex));
221 oe->version++;
222}
223
224u64 ovl_dentry_version_get(struct dentry *dentry)
225{
226 struct ovl_entry *oe = dentry->d_fsdata;
227
228 WARN_ON(!mutex_is_locked(&dentry->d_inode->i_mutex));
229 return oe->version;
230}
231
232bool ovl_is_whiteout(struct dentry *dentry)
233{
234 struct inode *inode = dentry->d_inode;
235
236 return inode && IS_WHITEOUT(inode);
237}
238
239static bool ovl_is_opaquedir(struct dentry *dentry)
240{
241 int res;
242 char val;
243 struct inode *inode = dentry->d_inode;
244
245 if (!S_ISDIR(inode->i_mode) || !inode->i_op->getxattr)
246 return false;
247
248 res = inode->i_op->getxattr(dentry, ovl_opaque_xattr, &val, 1);
249 if (res == 1 && val == 'y')
250 return true;
251
252 return false;
253}
254
255static void ovl_dentry_release(struct dentry *dentry)
256{
257 struct ovl_entry *oe = dentry->d_fsdata;
258
259 if (oe) {
260 dput(oe->__upperdentry);
261 dput(oe->lowerdentry);
262 kfree_rcu(oe, rcu);
263 }
264}
265
266static const struct dentry_operations ovl_dentry_operations = {
267 .d_release = ovl_dentry_release,
268};
269
270static struct ovl_entry *ovl_alloc_entry(void)
271{
272 return kzalloc(sizeof(struct ovl_entry), GFP_KERNEL);
273}
274
275static inline struct dentry *ovl_lookup_real(struct dentry *dir,
276 struct qstr *name)
277{
278 struct dentry *dentry;
279
280 mutex_lock(&dir->d_inode->i_mutex);
281 dentry = lookup_one_len(name->name, dir, name->len);
282 mutex_unlock(&dir->d_inode->i_mutex);
283
284 if (IS_ERR(dentry)) {
285 if (PTR_ERR(dentry) == -ENOENT)
286 dentry = NULL;
287 } else if (!dentry->d_inode) {
288 dput(dentry);
289 dentry = NULL;
290 }
291 return dentry;
292}
293
294struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
295 unsigned int flags)
296{
297 struct ovl_entry *oe;
298 struct dentry *upperdir;
299 struct dentry *lowerdir;
300 struct dentry *upperdentry = NULL;
301 struct dentry *lowerdentry = NULL;
302 struct inode *inode = NULL;
303 int err;
304
305 err = -ENOMEM;
306 oe = ovl_alloc_entry();
307 if (!oe)
308 goto out;
309
310 upperdir = ovl_dentry_upper(dentry->d_parent);
311 lowerdir = ovl_dentry_lower(dentry->d_parent);
312
313 if (upperdir) {
314 upperdentry = ovl_lookup_real(upperdir, &dentry->d_name);
315 err = PTR_ERR(upperdentry);
316 if (IS_ERR(upperdentry))
317 goto out_put_dir;
318
319 if (lowerdir && upperdentry) {
320 if (ovl_is_whiteout(upperdentry)) {
321 dput(upperdentry);
322 upperdentry = NULL;
323 oe->opaque = true;
324 } else if (ovl_is_opaquedir(upperdentry)) {
325 oe->opaque = true;
326 }
327 }
328 }
329 if (lowerdir && !oe->opaque) {
330 lowerdentry = ovl_lookup_real(lowerdir, &dentry->d_name);
331 err = PTR_ERR(lowerdentry);
332 if (IS_ERR(lowerdentry))
333 goto out_dput_upper;
334 }
335
336 if (lowerdentry && upperdentry &&
337 (!S_ISDIR(upperdentry->d_inode->i_mode) ||
338 !S_ISDIR(lowerdentry->d_inode->i_mode))) {
339 dput(lowerdentry);
340 lowerdentry = NULL;
341 oe->opaque = true;
342 }
343
344 if (lowerdentry || upperdentry) {
345 struct dentry *realdentry;
346
347 realdentry = upperdentry ? upperdentry : lowerdentry;
348 err = -ENOMEM;
349 inode = ovl_new_inode(dentry->d_sb, realdentry->d_inode->i_mode,
350 oe);
351 if (!inode)
352 goto out_dput;
353 ovl_copyattr(realdentry->d_inode, inode);
354 }
355
356 oe->__upperdentry = upperdentry;
357 oe->lowerdentry = lowerdentry;
358
359 dentry->d_fsdata = oe;
360 d_add(dentry, inode);
361
362 return NULL;
363
364out_dput:
365 dput(lowerdentry);
366out_dput_upper:
367 dput(upperdentry);
368out_put_dir:
369 kfree(oe);
370out:
371 return ERR_PTR(err);
372}
373
374struct file *ovl_path_open(struct path *path, int flags)
375{
376 return dentry_open(path, flags, current_cred());
377}
378
379static void ovl_put_super(struct super_block *sb)
380{
381 struct ovl_fs *ufs = sb->s_fs_info;
382
383 dput(ufs->workdir);
384 mntput(ufs->upper_mnt);
385 mntput(ufs->lower_mnt);
386
387 kfree(ufs);
388}
389
Andy Whitcroftcc259632014-10-24 00:14:38 +0200390/**
391 * ovl_statfs
392 * @sb: The overlayfs super block
393 * @buf: The struct kstatfs to fill in with stats
394 *
395 * Get the filesystem statistics. As writes always target the upper layer
396 * filesystem pass the statfs to the same filesystem.
397 */
398static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
399{
400 struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
401 struct dentry *root_dentry = dentry->d_sb->s_root;
402 struct path path;
403 int err;
404
405 ovl_path_upper(root_dentry, &path);
406
407 err = vfs_statfs(&path, buf);
408 if (!err) {
409 buf->f_namelen = max(buf->f_namelen, ofs->lower_namelen);
410 buf->f_type = OVERLAYFS_SUPER_MAGIC;
411 }
412
413 return err;
414}
415
Miklos Szeredie9be9d52014-10-24 00:14:38 +0200416static const struct super_operations ovl_super_operations = {
417 .put_super = ovl_put_super,
Andy Whitcroftcc259632014-10-24 00:14:38 +0200418 .statfs = ovl_statfs,
Miklos Szeredie9be9d52014-10-24 00:14:38 +0200419};
420
421struct ovl_config {
422 char *lowerdir;
423 char *upperdir;
424 char *workdir;
425};
426
427enum {
428 OPT_LOWERDIR,
429 OPT_UPPERDIR,
430 OPT_WORKDIR,
431 OPT_ERR,
432};
433
434static const match_table_t ovl_tokens = {
435 {OPT_LOWERDIR, "lowerdir=%s"},
436 {OPT_UPPERDIR, "upperdir=%s"},
437 {OPT_WORKDIR, "workdir=%s"},
438 {OPT_ERR, NULL}
439};
440
441static int ovl_parse_opt(char *opt, struct ovl_config *config)
442{
443 char *p;
444
445 config->upperdir = NULL;
446 config->lowerdir = NULL;
447 config->workdir = NULL;
448
449 while ((p = strsep(&opt, ",")) != NULL) {
450 int token;
451 substring_t args[MAX_OPT_ARGS];
452
453 if (!*p)
454 continue;
455
456 token = match_token(p, ovl_tokens, args);
457 switch (token) {
458 case OPT_UPPERDIR:
459 kfree(config->upperdir);
460 config->upperdir = match_strdup(&args[0]);
461 if (!config->upperdir)
462 return -ENOMEM;
463 break;
464
465 case OPT_LOWERDIR:
466 kfree(config->lowerdir);
467 config->lowerdir = match_strdup(&args[0]);
468 if (!config->lowerdir)
469 return -ENOMEM;
470 break;
471
472 case OPT_WORKDIR:
473 kfree(config->workdir);
474 config->workdir = match_strdup(&args[0]);
475 if (!config->workdir)
476 return -ENOMEM;
477 break;
478
479 default:
480 return -EINVAL;
481 }
482 }
483 return 0;
484}
485
486#define OVL_WORKDIR_NAME "work"
487
488static struct dentry *ovl_workdir_create(struct vfsmount *mnt,
489 struct dentry *dentry)
490{
491 struct inode *dir = dentry->d_inode;
492 struct dentry *work;
493 int err;
494 bool retried = false;
495
496 err = mnt_want_write(mnt);
497 if (err)
498 return ERR_PTR(err);
499
500 mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
501retry:
502 work = lookup_one_len(OVL_WORKDIR_NAME, dentry,
503 strlen(OVL_WORKDIR_NAME));
504
505 if (!IS_ERR(work)) {
506 struct kstat stat = {
507 .mode = S_IFDIR | 0,
508 };
509
510 if (work->d_inode) {
511 err = -EEXIST;
512 if (retried)
513 goto out_dput;
514
515 retried = true;
516 ovl_cleanup(dir, work);
517 dput(work);
518 goto retry;
519 }
520
521 err = ovl_create_real(dir, work, &stat, NULL, NULL, true);
522 if (err)
523 goto out_dput;
524 }
525out_unlock:
526 mutex_unlock(&dir->i_mutex);
527 mnt_drop_write(mnt);
528
529 return work;
530
531out_dput:
532 dput(work);
533 work = ERR_PTR(err);
534 goto out_unlock;
535}
536
537static int ovl_mount_dir(const char *name, struct path *path)
538{
539 int err;
540
541 err = kern_path(name, LOOKUP_FOLLOW, path);
542 if (err) {
543 pr_err("overlayfs: failed to resolve '%s': %i\n", name, err);
544 err = -EINVAL;
545 }
546 return err;
547}
548
549static bool ovl_is_allowed_fs_type(struct dentry *root)
550{
551 const struct dentry_operations *dop = root->d_op;
552
553 /*
554 * We don't support:
555 * - automount filesystems
556 * - filesystems with revalidate (FIXME for lower layer)
557 * - filesystems with case insensitive names
558 */
559 if (dop &&
560 (dop->d_manage || dop->d_automount ||
561 dop->d_revalidate || dop->d_weak_revalidate ||
562 dop->d_compare || dop->d_hash)) {
563 return false;
564 }
565 return true;
566}
567
568/* Workdir should not be subdir of upperdir and vice versa */
569static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
570{
571 bool ok = false;
572
573 if (workdir != upperdir) {
574 ok = (lock_rename(workdir, upperdir) == NULL);
575 unlock_rename(workdir, upperdir);
576 }
577 return ok;
578}
579
580static int ovl_fill_super(struct super_block *sb, void *data, int silent)
581{
582 struct path lowerpath;
583 struct path upperpath;
584 struct path workpath;
585 struct inode *root_inode;
586 struct dentry *root_dentry;
587 struct ovl_entry *oe;
588 struct ovl_fs *ufs;
589 struct ovl_config config;
Andy Whitcroftcc259632014-10-24 00:14:38 +0200590 struct kstatfs statfs;
Miklos Szeredie9be9d52014-10-24 00:14:38 +0200591 int err;
592
593 err = ovl_parse_opt((char *) data, &config);
594 if (err)
595 goto out;
596
597 /* FIXME: workdir is not needed for a R/O mount */
598 err = -EINVAL;
599 if (!config.upperdir || !config.lowerdir || !config.workdir) {
600 pr_err("overlayfs: missing upperdir or lowerdir or workdir\n");
601 goto out_free_config;
602 }
603
604 err = -ENOMEM;
605 ufs = kmalloc(sizeof(struct ovl_fs), GFP_KERNEL);
606 if (!ufs)
607 goto out_free_config;
608
609 oe = ovl_alloc_entry();
610 if (oe == NULL)
611 goto out_free_ufs;
612
613 err = ovl_mount_dir(config.upperdir, &upperpath);
614 if (err)
615 goto out_free_oe;
616
617 err = ovl_mount_dir(config.lowerdir, &lowerpath);
618 if (err)
619 goto out_put_upperpath;
620
621 err = ovl_mount_dir(config.workdir, &workpath);
622 if (err)
623 goto out_put_lowerpath;
624
625 err = -EINVAL;
626 if (!S_ISDIR(upperpath.dentry->d_inode->i_mode) ||
627 !S_ISDIR(lowerpath.dentry->d_inode->i_mode) ||
628 !S_ISDIR(workpath.dentry->d_inode->i_mode)) {
629 pr_err("overlayfs: upperdir or lowerdir or workdir not a directory\n");
630 goto out_put_workpath;
631 }
632
633 if (upperpath.mnt != workpath.mnt) {
634 pr_err("overlayfs: workdir and upperdir must reside under the same mount\n");
635 goto out_put_workpath;
636 }
637 if (!ovl_workdir_ok(workpath.dentry, upperpath.dentry)) {
638 pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
639 goto out_put_workpath;
640 }
641
642 if (!ovl_is_allowed_fs_type(upperpath.dentry)) {
643 pr_err("overlayfs: filesystem of upperdir is not supported\n");
644 goto out_put_workpath;
645 }
646
647 if (!ovl_is_allowed_fs_type(lowerpath.dentry)) {
648 pr_err("overlayfs: filesystem of lowerdir is not supported\n");
649 goto out_put_workpath;
650 }
651
Andy Whitcroftcc259632014-10-24 00:14:38 +0200652 err = vfs_statfs(&lowerpath, &statfs);
653 if (err) {
654 pr_err("overlayfs: statfs failed on lowerpath\n");
655 goto out_put_workpath;
656 }
657 ufs->lower_namelen = statfs.f_namelen;
658
Miklos Szeredie9be9d52014-10-24 00:14:38 +0200659 ufs->upper_mnt = clone_private_mount(&upperpath);
660 err = PTR_ERR(ufs->upper_mnt);
661 if (IS_ERR(ufs->upper_mnt)) {
662 pr_err("overlayfs: failed to clone upperpath\n");
663 goto out_put_workpath;
664 }
665
666 ufs->lower_mnt = clone_private_mount(&lowerpath);
667 err = PTR_ERR(ufs->lower_mnt);
668 if (IS_ERR(ufs->lower_mnt)) {
669 pr_err("overlayfs: failed to clone lowerpath\n");
670 goto out_put_upper_mnt;
671 }
672
673 ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry);
674 err = PTR_ERR(ufs->workdir);
675 if (IS_ERR(ufs->workdir)) {
676 pr_err("overlayfs: failed to create directory %s/%s\n",
677 config.workdir, OVL_WORKDIR_NAME);
678 goto out_put_lower_mnt;
679 }
680
681 /*
682 * Make lower_mnt R/O. That way fchmod/fchown on lower file
683 * will fail instead of modifying lower fs.
684 */
685 ufs->lower_mnt->mnt_flags |= MNT_READONLY;
686
687 /* If the upper fs is r/o, we mark overlayfs r/o too */
688 if (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY)
689 sb->s_flags |= MS_RDONLY;
690
691 sb->s_d_op = &ovl_dentry_operations;
692
693 err = -ENOMEM;
694 root_inode = ovl_new_inode(sb, S_IFDIR, oe);
695 if (!root_inode)
696 goto out_put_workdir;
697
698 root_dentry = d_make_root(root_inode);
699 if (!root_dentry)
700 goto out_put_workdir;
701
702 mntput(upperpath.mnt);
703 mntput(lowerpath.mnt);
704 path_put(&workpath);
705
706 oe->__upperdentry = upperpath.dentry;
707 oe->lowerdentry = lowerpath.dentry;
708
709 root_dentry->d_fsdata = oe;
710
Andy Whitcroftcc259632014-10-24 00:14:38 +0200711 sb->s_magic = OVERLAYFS_SUPER_MAGIC;
Miklos Szeredie9be9d52014-10-24 00:14:38 +0200712 sb->s_op = &ovl_super_operations;
713 sb->s_root = root_dentry;
714 sb->s_fs_info = ufs;
715
716 return 0;
717
718out_put_workdir:
719 dput(ufs->workdir);
720out_put_lower_mnt:
721 mntput(ufs->lower_mnt);
722out_put_upper_mnt:
723 mntput(ufs->upper_mnt);
724out_put_workpath:
725 path_put(&workpath);
726out_put_lowerpath:
727 path_put(&lowerpath);
728out_put_upperpath:
729 path_put(&upperpath);
730out_free_oe:
731 kfree(oe);
732out_free_ufs:
733 kfree(ufs);
734out_free_config:
735 kfree(config.lowerdir);
736 kfree(config.upperdir);
737 kfree(config.workdir);
738out:
739 return err;
740}
741
742static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags,
743 const char *dev_name, void *raw_data)
744{
745 return mount_nodev(fs_type, flags, raw_data, ovl_fill_super);
746}
747
748static struct file_system_type ovl_fs_type = {
749 .owner = THIS_MODULE,
750 .name = "overlayfs",
751 .mount = ovl_mount,
752 .kill_sb = kill_anon_super,
753};
754MODULE_ALIAS_FS("overlayfs");
755
756static int __init ovl_init(void)
757{
758 return register_filesystem(&ovl_fs_type);
759}
760
761static void __exit ovl_exit(void)
762{
763 unregister_filesystem(&ovl_fs_type);
764}
765
766module_init(ovl_init);
767module_exit(ovl_exit);