2005-04-17 06:20:36 +08:00
|
|
|
/*
|
2007-05-08 15:23:18 +08:00
|
|
|
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
2005-04-17 06:20:36 +08:00
|
|
|
* Licensed under the GPL
|
|
|
|
*
|
|
|
|
* Ported the filesystem routines to 2.5.
|
|
|
|
* 2003-02-10 Petr Baudis <pasky@ucw.cz>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/fs.h>
|
2013-03-27 18:47:13 +08:00
|
|
|
#include <linux/magic.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/module.h>
|
2007-10-16 16:27:13 +08:00
|
|
|
#include <linux/mm.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/pagemap.h>
|
|
|
|
#include <linux/statfs.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2008-02-08 20:21:43 +08:00
|
|
|
#include <linux/seq_file.h>
|
2022-02-10 04:22:03 +08:00
|
|
|
#include <linux/writeback.h>
|
2008-02-09 16:10:14 +08:00
|
|
|
#include <linux/mount.h>
|
2010-06-07 09:51:16 +08:00
|
|
|
#include <linux/namei.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include "hostfs.h"
|
2012-10-08 10:27:32 +08:00
|
|
|
#include <init.h>
|
|
|
|
#include <kern.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
struct hostfs_inode_info {
|
|
|
|
int fd;
|
2008-09-03 03:28:45 +08:00
|
|
|
fmode_t mode;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct inode vfs_inode;
|
2015-02-28 05:55:20 +08:00
|
|
|
struct mutex open_mutex;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode)
|
|
|
|
{
|
2007-05-08 15:23:18 +08:00
|
|
|
return list_entry(inode, struct hostfs_inode_info, vfs_inode);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-01-24 06:07:38 +08:00
|
|
|
#define FILE_HOSTFS_I(file) HOSTFS_I(file_inode(file))
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-01-14 04:31:55 +08:00
|
|
|
static struct kmem_cache *hostfs_inode_cache;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Changed in hostfs_args before the kernel starts running */
|
2007-03-08 12:41:08 +08:00
|
|
|
static char *root_ino = "";
|
2005-04-17 06:20:36 +08:00
|
|
|
static int append = 0;
|
|
|
|
|
2007-02-12 16:55:39 +08:00
|
|
|
static const struct inode_operations hostfs_iops;
|
|
|
|
static const struct inode_operations hostfs_dir_iops;
|
2010-06-07 09:51:16 +08:00
|
|
|
static const struct inode_operations hostfs_link_iops;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifndef MODULE
|
|
|
|
static int __init hostfs_args(char *options, int *add)
|
|
|
|
{
|
|
|
|
char *ptr;
|
|
|
|
|
|
|
|
ptr = strchr(options, ',');
|
2007-10-16 16:27:13 +08:00
|
|
|
if (ptr != NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
*ptr++ = '\0';
|
2007-10-16 16:27:13 +08:00
|
|
|
if (*options != '\0')
|
2005-04-17 06:20:36 +08:00
|
|
|
root_ino = options;
|
|
|
|
|
|
|
|
options = ptr;
|
2007-10-16 16:27:13 +08:00
|
|
|
while (options) {
|
2005-04-17 06:20:36 +08:00
|
|
|
ptr = strchr(options, ',');
|
2007-10-16 16:27:13 +08:00
|
|
|
if (ptr != NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
*ptr++ = '\0';
|
2007-10-16 16:27:13 +08:00
|
|
|
if (*options != '\0') {
|
|
|
|
if (!strcmp(options, "append"))
|
2005-04-17 06:20:36 +08:00
|
|
|
append = 1;
|
|
|
|
else printf("hostfs_args - unsupported option - %s\n",
|
|
|
|
options);
|
|
|
|
}
|
|
|
|
options = ptr;
|
|
|
|
}
|
2007-05-08 15:23:18 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
__uml_setup("hostfs=", hostfs_args,
|
|
|
|
"hostfs=<root dir>,<flags>,...\n"
|
|
|
|
" This is used to set hostfs parameters. The root directory argument\n"
|
|
|
|
" is used to confine all hostfs mounts to within the specified directory\n"
|
|
|
|
" tree on the host. If this isn't specified, then a user inside UML can\n"
|
|
|
|
" mount anything on the host that's accessible to the user that's running\n"
|
|
|
|
" it.\n"
|
|
|
|
" The only flag currently supported is 'append', which specifies that all\n"
|
|
|
|
" files opened by hostfs will be opened in append mode.\n\n"
|
|
|
|
);
|
|
|
|
#endif
|
|
|
|
|
2010-06-07 11:16:34 +08:00
|
|
|
static char *__dentry_name(struct dentry *dentry, char *name)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-01-07 14:49:29 +08:00
|
|
|
char *p = dentry_path_raw(dentry, name, PATH_MAX);
|
2010-06-07 11:16:34 +08:00
|
|
|
char *root;
|
|
|
|
size_t len;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-07 11:16:34 +08:00
|
|
|
root = dentry->d_sb->s_fs_info;
|
|
|
|
len = strlen(root);
|
|
|
|
if (IS_ERR(p)) {
|
|
|
|
__putname(name);
|
2007-05-08 15:23:18 +08:00
|
|
|
return NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2015-03-04 06:41:52 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This function relies on the fact that dentry_path_raw() will place
|
|
|
|
* the path name at the end of the provided buffer.
|
|
|
|
*/
|
|
|
|
BUG_ON(p + strlen(p) + 1 != name + PATH_MAX);
|
|
|
|
|
2022-08-19 05:01:17 +08:00
|
|
|
strscpy(name, root, PATH_MAX);
|
2010-06-07 11:16:34 +08:00
|
|
|
if (len > p - name) {
|
|
|
|
__putname(name);
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-03-04 06:42:25 +08:00
|
|
|
|
|
|
|
if (p > name + len)
|
|
|
|
strcpy(name + len, p);
|
|
|
|
|
2007-05-08 15:23:18 +08:00
|
|
|
return name;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2010-06-07 11:16:34 +08:00
|
|
|
static char *dentry_name(struct dentry *dentry)
|
|
|
|
{
|
|
|
|
char *name = __getname();
|
|
|
|
if (!name)
|
|
|
|
return NULL;
|
|
|
|
|
2013-03-27 18:47:12 +08:00
|
|
|
return __dentry_name(dentry, name);
|
2010-06-07 11:16:34 +08:00
|
|
|
}
|
|
|
|
|
2010-06-07 08:42:10 +08:00
|
|
|
static char *inode_name(struct inode *ino)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct dentry *dentry;
|
2011-01-07 14:49:29 +08:00
|
|
|
char *name;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-01-07 14:49:29 +08:00
|
|
|
dentry = d_find_alias(ino);
|
|
|
|
if (!dentry)
|
2010-06-07 11:16:34 +08:00
|
|
|
return NULL;
|
2011-01-07 14:49:29 +08:00
|
|
|
|
|
|
|
name = dentry_name(dentry);
|
|
|
|
|
|
|
|
dput(dentry);
|
|
|
|
|
|
|
|
return name;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static char *follow_link(char *link)
|
|
|
|
{
|
|
|
|
char *name, *resolved, *end;
|
2020-03-20 21:07:35 +08:00
|
|
|
int n;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-03-26 02:12:34 +08:00
|
|
|
name = kmalloc(PATH_MAX, GFP_KERNEL);
|
2015-03-04 06:55:49 +08:00
|
|
|
if (!name) {
|
2005-04-17 06:20:36 +08:00
|
|
|
n = -ENOMEM;
|
2015-03-04 06:55:49 +08:00
|
|
|
goto out_free;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2015-03-04 06:55:49 +08:00
|
|
|
|
|
|
|
n = hostfs_do_readlink(link, name, PATH_MAX);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (n < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out_free;
|
2015-03-04 06:55:49 +08:00
|
|
|
else if (n == PATH_MAX) {
|
|
|
|
n = -E2BIG;
|
|
|
|
goto out_free;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-10-16 16:27:13 +08:00
|
|
|
if (*name == '/')
|
2007-05-08 15:23:18 +08:00
|
|
|
return name;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
end = strrchr(link, '/');
|
2007-10-16 16:27:13 +08:00
|
|
|
if (end == NULL)
|
2007-05-08 15:23:18 +08:00
|
|
|
return name;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
*(end + 1) = '\0';
|
|
|
|
|
2020-03-20 21:07:35 +08:00
|
|
|
resolved = kasprintf(GFP_KERNEL, "%s%s", link, name);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (resolved == NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
n = -ENOMEM;
|
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
2021-03-26 02:12:34 +08:00
|
|
|
kfree(name);
|
2007-05-08 15:23:18 +08:00
|
|
|
return resolved;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
out_free:
|
2021-03-26 02:12:34 +08:00
|
|
|
kfree(name);
|
2007-05-08 15:23:18 +08:00
|
|
|
return ERR_PTR(n);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-02-07 16:15:50 +08:00
|
|
|
static struct inode *hostfs_iget(struct super_block *sb)
|
|
|
|
{
|
2010-06-07 06:43:19 +08:00
|
|
|
struct inode *inode = new_inode(sb);
|
2008-02-07 16:15:50 +08:00
|
|
|
if (!inode)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
return inode;
|
|
|
|
}
|
|
|
|
|
2013-11-15 05:15:13 +08:00
|
|
|
static int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-10-16 16:27:13 +08:00
|
|
|
/*
|
|
|
|
* do_statfs uses struct statfs64 internally, but the linux kernel
|
2005-04-17 06:20:36 +08:00
|
|
|
* struct statfs still has 32-bit versions for most of these fields,
|
|
|
|
* so we convert them here
|
|
|
|
*/
|
|
|
|
int err;
|
|
|
|
long long f_blocks;
|
|
|
|
long long f_bfree;
|
|
|
|
long long f_bavail;
|
|
|
|
long long f_files;
|
|
|
|
long long f_ffree;
|
|
|
|
|
2010-06-07 05:53:01 +08:00
|
|
|
err = do_statfs(dentry->d_sb->s_fs_info,
|
2005-04-17 06:20:36 +08:00
|
|
|
&sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files,
|
|
|
|
&f_ffree, &sf->f_fsid, sizeof(sf->f_fsid),
|
2010-10-27 05:21:18 +08:00
|
|
|
&sf->f_namelen);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (err)
|
2007-05-08 15:23:18 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
sf->f_blocks = f_blocks;
|
|
|
|
sf->f_bfree = f_bfree;
|
|
|
|
sf->f_bavail = f_bavail;
|
|
|
|
sf->f_files = f_files;
|
|
|
|
sf->f_ffree = f_ffree;
|
|
|
|
sf->f_type = HOSTFS_SUPER_MAGIC;
|
2007-05-08 15:23:18 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct inode *hostfs_alloc_inode(struct super_block *sb)
|
|
|
|
{
|
|
|
|
struct hostfs_inode_info *hi;
|
|
|
|
|
2022-03-23 05:41:03 +08:00
|
|
|
hi = alloc_inode_sb(sb, hostfs_inode_cache, GFP_KERNEL_ACCOUNT);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (hi == NULL)
|
2007-05-08 15:23:18 +08:00
|
|
|
return NULL;
|
2010-06-07 05:53:01 +08:00
|
|
|
hi->fd = -1;
|
2013-03-27 18:47:14 +08:00
|
|
|
hi->mode = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
inode_init_once(&hi->vfs_inode);
|
2015-02-28 05:55:20 +08:00
|
|
|
mutex_init(&hi->open_mutex);
|
2007-05-08 15:23:18 +08:00
|
|
|
return &hi->vfs_inode;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2010-06-07 03:16:17 +08:00
|
|
|
static void hostfs_evict_inode(struct inode *inode)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2014-04-04 05:47:49 +08:00
|
|
|
truncate_inode_pages_final(&inode->i_data);
|
2012-05-03 20:48:02 +08:00
|
|
|
clear_inode(inode);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (HOSTFS_I(inode)->fd != -1) {
|
2005-04-17 06:20:36 +08:00
|
|
|
close_file(&HOSTFS_I(inode)->fd);
|
|
|
|
HOSTFS_I(inode)->fd = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-16 08:12:11 +08:00
|
|
|
static void hostfs_free_inode(struct inode *inode)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2021-01-14 04:31:55 +08:00
|
|
|
kmem_cache_free(hostfs_inode_cache, HOSTFS_I(inode));
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-01-07 14:49:49 +08:00
|
|
|
|
2011-12-09 10:32:45 +08:00
|
|
|
static int hostfs_show_options(struct seq_file *seq, struct dentry *root)
|
2008-02-08 20:21:43 +08:00
|
|
|
{
|
2011-12-09 10:32:45 +08:00
|
|
|
const char *root_path = root->d_sb->s_fs_info;
|
2008-02-08 20:21:43 +08:00
|
|
|
size_t offset = strlen(root_ino) + 1;
|
|
|
|
|
|
|
|
if (strlen(root_path) > offset)
|
fs: create and use seq_show_option for escaping
Many file systems that implement the show_options hook fail to correctly
escape their output which could lead to unescaped characters (e.g. new
lines) leaking into /proc/mounts and /proc/[pid]/mountinfo files. This
could lead to confusion, spoofed entries (resulting in things like
systemd issuing false d-bus "mount" notifications), and who knows what
else. This looks like it would only be the root user stepping on
themselves, but it's possible weird things could happen in containers or
in other situations with delegated mount privileges.
Here's an example using overlay with setuid fusermount trusting the
contents of /proc/mounts (via the /etc/mtab symlink). Imagine the use
of "sudo" is something more sneaky:
$ BASE="ovl"
$ MNT="$BASE/mnt"
$ LOW="$BASE/lower"
$ UP="$BASE/upper"
$ WORK="$BASE/work/ 0 0
none /proc fuse.pwn user_id=1000"
$ mkdir -p "$LOW" "$UP" "$WORK"
$ sudo mount -t overlay -o "lowerdir=$LOW,upperdir=$UP,workdir=$WORK" none /mnt
$ cat /proc/mounts
none /root/ovl/mnt overlay rw,relatime,lowerdir=ovl/lower,upperdir=ovl/upper,workdir=ovl/work/ 0 0
none /proc fuse.pwn user_id=1000 0 0
$ fusermount -u /proc
$ cat /proc/mounts
cat: /proc/mounts: No such file or directory
This fixes the problem by adding new seq_show_option and
seq_show_option_n helpers, and updating the vulnerable show_option
handlers to use them as needed. Some, like SELinux, need to be open
coded due to unusual existing escape mechanisms.
[akpm@linux-foundation.org: add lost chunk, per Kees]
[keescook@chromium.org: seq_show_option should be using const parameters]
Signed-off-by: Kees Cook <keescook@chromium.org>
Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Acked-by: Jan Kara <jack@suse.com>
Acked-by: Paul Moore <paul@paul-moore.com>
Cc: J. R. Okajima <hooanon05g@gmail.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2015-09-05 06:44:57 +08:00
|
|
|
seq_show_option(seq, root_path + offset, NULL);
|
2008-02-08 20:21:43 +08:00
|
|
|
|
2015-03-04 07:00:54 +08:00
|
|
|
if (append)
|
|
|
|
seq_puts(seq, ",append");
|
|
|
|
|
2008-02-08 20:21:43 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-12 16:55:41 +08:00
|
|
|
static const struct super_operations hostfs_sbops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.alloc_inode = hostfs_alloc_inode,
|
2019-04-16 08:12:11 +08:00
|
|
|
.free_inode = hostfs_free_inode,
|
2010-06-07 03:16:17 +08:00
|
|
|
.evict_inode = hostfs_evict_inode,
|
2005-04-17 06:20:36 +08:00
|
|
|
.statfs = hostfs_statfs,
|
2008-02-08 20:21:43 +08:00
|
|
|
.show_options = hostfs_show_options,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2013-11-15 05:15:13 +08:00
|
|
|
static int hostfs_readdir(struct file *file, struct dir_context *ctx)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
void *dir;
|
|
|
|
char *name;
|
|
|
|
unsigned long long next, ino;
|
|
|
|
int error, len;
|
2012-01-28 02:14:58 +08:00
|
|
|
unsigned int type;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-07 08:42:10 +08:00
|
|
|
name = dentry_name(file->f_path.dentry);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (name == NULL)
|
2007-05-08 15:23:18 +08:00
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
dir = open_dir(name, &error);
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(name);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (dir == NULL)
|
2007-05-08 15:23:18 +08:00
|
|
|
return -error;
|
2013-05-23 04:34:19 +08:00
|
|
|
next = ctx->pos;
|
2015-03-24 22:47:38 +08:00
|
|
|
seek_dir(dir, next);
|
2012-01-28 02:14:58 +08:00
|
|
|
while ((name = read_dir(dir, &next, &ino, &len, &type)) != NULL) {
|
2013-05-23 04:34:19 +08:00
|
|
|
if (!dir_emit(ctx, name, len, ino, type))
|
|
|
|
break;
|
|
|
|
ctx->pos = next;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
close_dir(dir);
|
2007-05-08 15:23:18 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2015-03-02 07:09:33 +08:00
|
|
|
static int hostfs_open(struct inode *ino, struct file *file)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
char *name;
|
2015-03-04 07:06:38 +08:00
|
|
|
fmode_t mode;
|
2010-06-07 11:49:18 +08:00
|
|
|
int err;
|
2015-03-04 07:06:38 +08:00
|
|
|
int r, w, fd;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
mode = file->f_mode & (FMODE_READ | FMODE_WRITE);
|
2007-10-16 16:27:13 +08:00
|
|
|
if ((mode & HOSTFS_I(ino)->mode) == mode)
|
2007-05-08 15:23:18 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-07 11:49:18 +08:00
|
|
|
mode |= HOSTFS_I(ino)->mode;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-07 11:49:18 +08:00
|
|
|
retry:
|
2015-03-05 05:39:48 +08:00
|
|
|
r = w = 0;
|
|
|
|
|
2010-06-07 11:49:18 +08:00
|
|
|
if (mode & FMODE_READ)
|
2005-04-17 06:20:36 +08:00
|
|
|
r = 1;
|
2010-06-07 11:49:18 +08:00
|
|
|
if (mode & FMODE_WRITE)
|
2015-03-04 07:05:11 +08:00
|
|
|
r = w = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2021-04-16 10:03:00 +08:00
|
|
|
name = dentry_name(file_dentry(file));
|
2007-10-16 16:27:13 +08:00
|
|
|
if (name == NULL)
|
2007-05-08 15:23:18 +08:00
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
fd = open_file(name, r, w, append);
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(name);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (fd < 0)
|
2007-05-08 15:23:18 +08:00
|
|
|
return fd;
|
2010-06-07 11:49:18 +08:00
|
|
|
|
2015-02-28 05:55:20 +08:00
|
|
|
mutex_lock(&HOSTFS_I(ino)->open_mutex);
|
2010-06-07 11:49:18 +08:00
|
|
|
/* somebody else had handled it first? */
|
|
|
|
if ((mode & HOSTFS_I(ino)->mode) == mode) {
|
2015-02-28 05:55:20 +08:00
|
|
|
mutex_unlock(&HOSTFS_I(ino)->open_mutex);
|
2015-02-28 05:56:28 +08:00
|
|
|
close_file(&fd);
|
2010-06-07 11:49:18 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if ((mode | HOSTFS_I(ino)->mode) != mode) {
|
|
|
|
mode |= HOSTFS_I(ino)->mode;
|
2015-02-28 05:55:20 +08:00
|
|
|
mutex_unlock(&HOSTFS_I(ino)->open_mutex);
|
2010-06-07 11:49:18 +08:00
|
|
|
close_file(&fd);
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
if (HOSTFS_I(ino)->fd == -1) {
|
|
|
|
HOSTFS_I(ino)->fd = fd;
|
|
|
|
} else {
|
|
|
|
err = replace_file(fd, HOSTFS_I(ino)->fd);
|
|
|
|
close_file(&fd);
|
|
|
|
if (err < 0) {
|
2015-02-28 05:55:20 +08:00
|
|
|
mutex_unlock(&HOSTFS_I(ino)->open_mutex);
|
2010-06-07 11:49:18 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
HOSTFS_I(ino)->mode = mode;
|
2015-02-28 05:55:20 +08:00
|
|
|
mutex_unlock(&HOSTFS_I(ino)->open_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-05-08 15:23:18 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-08-05 01:23:51 +08:00
|
|
|
static int hostfs_file_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
filemap_write_and_wait(inode->i_mapping);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-15 05:15:13 +08:00
|
|
|
static int hostfs_fsync(struct file *file, loff_t start, loff_t end,
|
|
|
|
int datasync)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-07-17 08:44:56 +08:00
|
|
|
struct inode *inode = file->f_mapping->host;
|
|
|
|
int ret;
|
|
|
|
|
2017-07-08 03:20:52 +08:00
|
|
|
ret = file_write_and_wait_range(file, start, end);
|
2011-07-17 08:44:56 +08:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_lock(inode);
|
2011-07-17 08:44:56 +08:00
|
|
|
ret = fsync_file(HOSTFS_I(inode)->fd, datasync);
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_unlock(inode);
|
2011-07-17 08:44:56 +08:00
|
|
|
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-03-28 17:56:42 +08:00
|
|
|
static const struct file_operations hostfs_file_fops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.llseek = generic_file_llseek,
|
2007-06-01 17:49:19 +08:00
|
|
|
.splice_read = generic_file_splice_read,
|
2021-07-07 05:12:42 +08:00
|
|
|
.splice_write = iter_file_splice_write,
|
2014-04-03 02:33:16 +08:00
|
|
|
.read_iter = generic_file_read_iter,
|
2014-04-03 15:17:43 +08:00
|
|
|
.write_iter = generic_file_write_iter,
|
2005-04-17 06:20:36 +08:00
|
|
|
.mmap = generic_file_mmap,
|
2015-03-02 07:09:33 +08:00
|
|
|
.open = hostfs_open,
|
2013-08-05 01:23:51 +08:00
|
|
|
.release = hostfs_file_release,
|
2005-04-17 06:20:36 +08:00
|
|
|
.fsync = hostfs_fsync,
|
|
|
|
};
|
|
|
|
|
2006-03-28 17:56:42 +08:00
|
|
|
static const struct file_operations hostfs_dir_fops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.llseek = generic_file_llseek,
|
2016-05-13 07:49:30 +08:00
|
|
|
.iterate_shared = hostfs_readdir,
|
2005-04-17 06:20:36 +08:00
|
|
|
.read = generic_read_dir,
|
2015-03-02 07:09:33 +08:00
|
|
|
.open = hostfs_open,
|
|
|
|
.fsync = hostfs_fsync,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2013-11-15 05:15:13 +08:00
|
|
|
static int hostfs_writepage(struct page *page, struct writeback_control *wbc)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct address_space *mapping = page->mapping;
|
|
|
|
struct inode *inode = mapping->host;
|
|
|
|
char *buffer;
|
2015-03-05 03:58:39 +08:00
|
|
|
loff_t base = page_offset(page);
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 20:29:47 +08:00
|
|
|
int count = PAGE_SIZE;
|
|
|
|
int end_index = inode->i_size >> PAGE_SHIFT;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if (page->index >= end_index)
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 20:29:47 +08:00
|
|
|
count = inode->i_size & (PAGE_SIZE-1);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
buffer = kmap(page);
|
|
|
|
|
|
|
|
err = write_file(HOSTFS_I(inode)->fd, &base, buffer, count);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (err != count) {
|
2022-05-27 23:20:56 +08:00
|
|
|
if (err >= 0)
|
|
|
|
err = -EIO;
|
|
|
|
mapping_set_error(mapping, err);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (base > inode->i_size)
|
|
|
|
inode->i_size = base;
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
out:
|
|
|
|
kunmap(page);
|
|
|
|
|
|
|
|
unlock_page(page);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2022-04-29 23:12:16 +08:00
|
|
|
static int hostfs_read_folio(struct file *file, struct folio *folio)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2022-04-29 23:12:16 +08:00
|
|
|
struct page *page = &folio->page;
|
2005-04-17 06:20:36 +08:00
|
|
|
char *buffer;
|
2015-03-05 03:58:39 +08:00
|
|
|
loff_t start = page_offset(page);
|
2015-03-04 19:44:03 +08:00
|
|
|
int bytes_read, ret = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
buffer = kmap(page);
|
2015-03-04 04:40:55 +08:00
|
|
|
bytes_read = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer,
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 20:29:47 +08:00
|
|
|
PAGE_SIZE);
|
2015-03-04 04:40:55 +08:00
|
|
|
if (bytes_read < 0) {
|
2015-03-04 19:44:03 +08:00
|
|
|
ClearPageUptodate(page);
|
|
|
|
SetPageError(page);
|
2015-03-04 04:40:55 +08:00
|
|
|
ret = bytes_read;
|
2007-10-16 16:27:13 +08:00
|
|
|
goto out;
|
2015-03-04 04:40:55 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 20:29:47 +08:00
|
|
|
memset(buffer + bytes_read, 0, PAGE_SIZE - bytes_read);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-03-04 19:44:03 +08:00
|
|
|
ClearPageError(page);
|
2005-04-17 06:20:36 +08:00
|
|
|
SetPageUptodate(page);
|
2015-03-04 19:44:03 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
out:
|
2015-03-04 19:44:03 +08:00
|
|
|
flush_dcache_page(page);
|
2005-04-17 06:20:36 +08:00
|
|
|
kunmap(page);
|
|
|
|
unlock_page(page);
|
2015-03-04 04:40:55 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-11-15 05:15:13 +08:00
|
|
|
static int hostfs_write_begin(struct file *file, struct address_space *mapping,
|
2022-02-23 03:31:43 +08:00
|
|
|
loff_t pos, unsigned len,
|
2013-11-15 05:15:13 +08:00
|
|
|
struct page **pagep, void **fsdata)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 20:29:47 +08:00
|
|
|
pgoff_t index = pos >> PAGE_SHIFT;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2022-02-23 00:25:12 +08:00
|
|
|
*pagep = grab_cache_page_write_begin(mapping, index);
|
2007-10-16 16:25:17 +08:00
|
|
|
if (!*pagep)
|
|
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-11-15 05:15:13 +08:00
|
|
|
static int hostfs_write_end(struct file *file, struct address_space *mapping,
|
|
|
|
loff_t pos, unsigned len, unsigned copied,
|
|
|
|
struct page *page, void *fsdata)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct inode *inode = mapping->host;
|
2007-10-16 16:25:17 +08:00
|
|
|
void *buffer;
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 20:29:47 +08:00
|
|
|
unsigned from = pos & (PAGE_SIZE - 1);
|
2007-10-16 16:25:17 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
buffer = kmap(page);
|
2007-10-16 16:25:17 +08:00
|
|
|
err = write_file(FILE_HOSTFS_I(file)->fd, &pos, buffer + from, copied);
|
|
|
|
kunmap(page);
|
2005-12-30 00:39:57 +08:00
|
|
|
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 20:29:47 +08:00
|
|
|
if (!PageUptodate(page) && err == PAGE_SIZE)
|
2007-10-16 16:25:17 +08:00
|
|
|
SetPageUptodate(page);
|
2005-12-30 00:39:57 +08:00
|
|
|
|
2007-10-16 16:27:13 +08:00
|
|
|
/*
|
|
|
|
* If err > 0, write_file has added err to pos, so we are comparing
|
2007-10-16 16:25:17 +08:00
|
|
|
* i_size against the last byte written.
|
|
|
|
*/
|
|
|
|
if (err > 0 && (pos > inode->i_size))
|
|
|
|
inode->i_size = pos;
|
|
|
|
unlock_page(page);
|
mm, fs: get rid of PAGE_CACHE_* and page_cache_{get,release} macros
PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} macros were introduced *long* time
ago with promise that one day it will be possible to implement page
cache with bigger chunks than PAGE_SIZE.
This promise never materialized. And unlikely will.
We have many places where PAGE_CACHE_SIZE assumed to be equal to
PAGE_SIZE. And it's constant source of confusion on whether
PAGE_CACHE_* or PAGE_* constant should be used in a particular case,
especially on the border between fs and mm.
Global switching to PAGE_CACHE_SIZE != PAGE_SIZE would cause to much
breakage to be doable.
Let's stop pretending that pages in page cache are special. They are
not.
The changes are pretty straight-forward:
- <foo> << (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- <foo> >> (PAGE_CACHE_SHIFT - PAGE_SHIFT) -> <foo>;
- PAGE_CACHE_{SIZE,SHIFT,MASK,ALIGN} -> PAGE_{SIZE,SHIFT,MASK,ALIGN};
- page_cache_get() -> get_page();
- page_cache_release() -> put_page();
This patch contains automated changes generated with coccinelle using
script below. For some reason, coccinelle doesn't patch header files.
I've called spatch for them manually.
The only adjustment after coccinelle is revert of changes to
PAGE_CAHCE_ALIGN definition: we are going to drop it later.
There are few places in the code where coccinelle didn't reach. I'll
fix them manually in a separate patch. Comments and documentation also
will be addressed with the separate patch.
virtual patch
@@
expression E;
@@
- E << (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
expression E;
@@
- E >> (PAGE_CACHE_SHIFT - PAGE_SHIFT)
+ E
@@
@@
- PAGE_CACHE_SHIFT
+ PAGE_SHIFT
@@
@@
- PAGE_CACHE_SIZE
+ PAGE_SIZE
@@
@@
- PAGE_CACHE_MASK
+ PAGE_MASK
@@
expression E;
@@
- PAGE_CACHE_ALIGN(E)
+ PAGE_ALIGN(E)
@@
expression E;
@@
- page_cache_get(E)
+ get_page(E)
@@
expression E;
@@
- page_cache_release(E)
+ put_page(E)
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-04-01 20:29:47 +08:00
|
|
|
put_page(page);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-05-08 15:23:18 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-06-28 19:26:44 +08:00
|
|
|
static const struct address_space_operations hostfs_aops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.writepage = hostfs_writepage,
|
2022-04-29 23:12:16 +08:00
|
|
|
.read_folio = hostfs_read_folio,
|
2022-02-10 04:22:03 +08:00
|
|
|
.dirty_folio = filemap_dirty_folio,
|
2007-10-16 16:25:17 +08:00
|
|
|
.write_begin = hostfs_write_begin,
|
|
|
|
.write_end = hostfs_write_end,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2010-06-07 08:33:12 +08:00
|
|
|
static int read_name(struct inode *ino, char *name)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-06-07 08:33:12 +08:00
|
|
|
dev_t rdev;
|
|
|
|
struct hostfs_stat st;
|
|
|
|
int err = stat_file(name, &st, -1);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-07 07:38:18 +08:00
|
|
|
/* Reencode maj and min with the kernel encoding.*/
|
2010-06-07 08:33:12 +08:00
|
|
|
rdev = MKDEV(st.maj, st.min);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-07 08:33:12 +08:00
|
|
|
switch (st.mode & S_IFMT) {
|
|
|
|
case S_IFLNK:
|
2010-06-07 09:51:16 +08:00
|
|
|
ino->i_op = &hostfs_link_iops;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2010-06-07 08:33:12 +08:00
|
|
|
case S_IFDIR:
|
|
|
|
ino->i_op = &hostfs_dir_iops;
|
|
|
|
ino->i_fop = &hostfs_dir_fops;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2010-06-07 08:33:12 +08:00
|
|
|
case S_IFCHR:
|
|
|
|
case S_IFBLK:
|
|
|
|
case S_IFIFO:
|
|
|
|
case S_IFSOCK:
|
|
|
|
init_special_inode(ino, st.mode & S_IFMT, rdev);
|
|
|
|
ino->i_op = &hostfs_iops;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2015-03-02 07:10:25 +08:00
|
|
|
case S_IFREG:
|
2010-06-07 08:33:12 +08:00
|
|
|
ino->i_op = &hostfs_iops;
|
|
|
|
ino->i_fop = &hostfs_file_fops;
|
|
|
|
ino->i_mapping->a_ops = &hostfs_aops;
|
2015-03-02 07:10:25 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EIO;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2010-06-07 08:33:12 +08:00
|
|
|
|
|
|
|
ino->i_ino = st.ino;
|
|
|
|
ino->i_mode = st.mode;
|
2011-10-28 20:13:29 +08:00
|
|
|
set_nlink(ino, st.nlink);
|
2012-02-08 08:28:57 +08:00
|
|
|
i_uid_write(ino, st.uid);
|
|
|
|
i_gid_write(ino, st.gid);
|
2018-06-12 21:31:17 +08:00
|
|
|
ino->i_atime = (struct timespec64){ st.atime.tv_sec, st.atime.tv_nsec };
|
|
|
|
ino->i_mtime = (struct timespec64){ st.mtime.tv_sec, st.mtime.tv_nsec };
|
|
|
|
ino->i_ctime = (struct timespec64){ st.ctime.tv_sec, st.ctime.tv_nsec };
|
2010-06-07 08:33:12 +08:00
|
|
|
ino->i_size = st.size;
|
|
|
|
ino->i_blocks = st.blocks;
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-21 21:19:43 +08:00
|
|
|
static int hostfs_create(struct user_namespace *mnt_userns, struct inode *dir,
|
|
|
|
struct dentry *dentry, umode_t mode, bool excl)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
char *name;
|
|
|
|
int error, fd;
|
|
|
|
|
2008-02-07 16:15:50 +08:00
|
|
|
inode = hostfs_iget(dir->i_sb);
|
|
|
|
if (IS_ERR(inode)) {
|
|
|
|
error = PTR_ERR(inode);
|
2007-10-16 16:27:13 +08:00
|
|
|
goto out;
|
2008-02-07 16:15:50 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
error = -ENOMEM;
|
2010-06-07 08:42:10 +08:00
|
|
|
name = dentry_name(dentry);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (name == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out_put;
|
|
|
|
|
2015-05-04 20:50:29 +08:00
|
|
|
fd = file_create(name, mode & 0777);
|
2010-06-07 08:33:12 +08:00
|
|
|
if (fd < 0)
|
2005-04-17 06:20:36 +08:00
|
|
|
error = fd;
|
2010-06-07 08:33:12 +08:00
|
|
|
else
|
2010-06-07 07:38:18 +08:00
|
|
|
error = read_name(inode, name);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(name);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (error)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out_put;
|
|
|
|
|
|
|
|
HOSTFS_I(inode)->fd = fd;
|
|
|
|
HOSTFS_I(inode)->mode = FMODE_READ | FMODE_WRITE;
|
|
|
|
d_instantiate(dentry, inode);
|
2007-05-08 15:23:18 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
out_put:
|
|
|
|
iput(inode);
|
|
|
|
out:
|
2007-05-08 15:23:18 +08:00
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-11-15 05:15:13 +08:00
|
|
|
static struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry,
|
|
|
|
unsigned int flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
char *name;
|
|
|
|
int err;
|
|
|
|
|
2008-02-07 16:15:50 +08:00
|
|
|
inode = hostfs_iget(ino->i_sb);
|
2018-06-24 08:27:29 +08:00
|
|
|
if (IS_ERR(inode))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = -ENOMEM;
|
2010-06-07 08:42:10 +08:00
|
|
|
name = dentry_name(dentry);
|
2018-06-24 08:27:29 +08:00
|
|
|
if (name) {
|
|
|
|
err = read_name(inode, name);
|
|
|
|
__putname(name);
|
|
|
|
}
|
|
|
|
if (err) {
|
2005-04-17 06:20:36 +08:00
|
|
|
iput(inode);
|
2018-06-24 08:27:29 +08:00
|
|
|
inode = (err == -ENOENT) ? NULL : ERR_PTR(err);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
out:
|
2018-06-24 08:27:29 +08:00
|
|
|
return d_splice_alias(inode, dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-11-15 05:15:13 +08:00
|
|
|
static int hostfs_link(struct dentry *to, struct inode *ino,
|
|
|
|
struct dentry *from)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-05-08 15:23:18 +08:00
|
|
|
char *from_name, *to_name;
|
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-07 08:42:10 +08:00
|
|
|
if ((from_name = dentry_name(from)) == NULL)
|
2007-05-08 15:23:18 +08:00
|
|
|
return -ENOMEM;
|
2010-06-07 08:42:10 +08:00
|
|
|
to_name = dentry_name(to);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (to_name == NULL) {
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(from_name);
|
2007-05-08 15:23:18 +08:00
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-05-08 15:23:18 +08:00
|
|
|
err = link_file(to_name, from_name);
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(from_name);
|
|
|
|
__putname(to_name);
|
2007-05-08 15:23:18 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-11-15 05:15:13 +08:00
|
|
|
static int hostfs_unlink(struct inode *ino, struct dentry *dentry)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
char *file;
|
|
|
|
int err;
|
|
|
|
|
2007-10-16 16:27:13 +08:00
|
|
|
if (append)
|
2007-05-08 15:23:18 +08:00
|
|
|
return -EPERM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-07 11:19:04 +08:00
|
|
|
if ((file = dentry_name(dentry)) == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
err = unlink_file(file);
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(file);
|
2007-05-08 15:23:18 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-21 21:19:43 +08:00
|
|
|
static int hostfs_symlink(struct user_namespace *mnt_userns, struct inode *ino,
|
|
|
|
struct dentry *dentry, const char *to)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
char *file;
|
|
|
|
int err;
|
|
|
|
|
2010-06-07 08:42:10 +08:00
|
|
|
if ((file = dentry_name(dentry)) == NULL)
|
2007-05-08 15:23:18 +08:00
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
err = make_symlink(file, to);
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(file);
|
2007-05-08 15:23:18 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-21 21:19:43 +08:00
|
|
|
static int hostfs_mkdir(struct user_namespace *mnt_userns, struct inode *ino,
|
|
|
|
struct dentry *dentry, umode_t mode)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
char *file;
|
|
|
|
int err;
|
|
|
|
|
2010-06-07 08:42:10 +08:00
|
|
|
if ((file = dentry_name(dentry)) == NULL)
|
2007-05-08 15:23:18 +08:00
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
err = do_mkdir(file, mode);
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(file);
|
2007-05-08 15:23:18 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-11-15 05:15:13 +08:00
|
|
|
static int hostfs_rmdir(struct inode *ino, struct dentry *dentry)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
char *file;
|
|
|
|
int err;
|
|
|
|
|
2010-06-07 08:42:10 +08:00
|
|
|
if ((file = dentry_name(dentry)) == NULL)
|
2007-05-08 15:23:18 +08:00
|
|
|
return -ENOMEM;
|
2018-03-11 18:34:48 +08:00
|
|
|
err = hostfs_do_rmdir(file);
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(file);
|
2007-05-08 15:23:18 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-21 21:19:43 +08:00
|
|
|
static int hostfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
|
|
|
|
struct dentry *dentry, umode_t mode, dev_t dev)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct inode *inode;
|
|
|
|
char *name;
|
2008-02-07 16:15:50 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-02-07 16:15:50 +08:00
|
|
|
inode = hostfs_iget(dir->i_sb);
|
|
|
|
if (IS_ERR(inode)) {
|
|
|
|
err = PTR_ERR(inode);
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
2008-02-07 16:15:50 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
err = -ENOMEM;
|
2010-06-07 08:42:10 +08:00
|
|
|
name = dentry_name(dentry);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (name == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out_put;
|
|
|
|
|
2007-01-30 05:19:44 +08:00
|
|
|
err = do_mknod(name, mode, MAJOR(dev), MINOR(dev));
|
2015-12-17 04:59:56 +08:00
|
|
|
if (err)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out_free;
|
|
|
|
|
|
|
|
err = read_name(inode, name);
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(name);
|
2010-06-07 07:38:18 +08:00
|
|
|
if (err)
|
|
|
|
goto out_put;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
d_instantiate(dentry, inode);
|
2007-05-08 15:23:18 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
out_free:
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(name);
|
2005-04-17 06:20:36 +08:00
|
|
|
out_put:
|
|
|
|
iput(inode);
|
|
|
|
out:
|
2007-05-08 15:23:18 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-21 21:19:43 +08:00
|
|
|
static int hostfs_rename2(struct user_namespace *mnt_userns,
|
|
|
|
struct inode *old_dir, struct dentry *old_dentry,
|
2014-07-23 21:15:35 +08:00
|
|
|
struct inode *new_dir, struct dentry *new_dentry,
|
|
|
|
unsigned int flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2014-07-23 21:15:35 +08:00
|
|
|
char *old_name, *new_name;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
2014-07-23 21:15:35 +08:00
|
|
|
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
old_name = dentry_name(old_dentry);
|
|
|
|
if (old_name == NULL)
|
2007-05-08 15:23:18 +08:00
|
|
|
return -ENOMEM;
|
2014-07-23 21:15:35 +08:00
|
|
|
new_name = dentry_name(new_dentry);
|
|
|
|
if (new_name == NULL) {
|
|
|
|
__putname(old_name);
|
2007-05-08 15:23:18 +08:00
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2014-07-23 21:15:35 +08:00
|
|
|
if (!flags)
|
|
|
|
err = rename_file(old_name, new_name);
|
|
|
|
else
|
|
|
|
err = rename2_file(old_name, new_name, flags);
|
|
|
|
|
|
|
|
__putname(old_name);
|
|
|
|
__putname(new_name);
|
2007-05-08 15:23:18 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2021-01-21 21:19:43 +08:00
|
|
|
static int hostfs_permission(struct user_namespace *mnt_userns,
|
|
|
|
struct inode *ino, int desired)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
char *name;
|
|
|
|
int r = 0, w = 0, x = 0, err;
|
|
|
|
|
2011-06-21 07:28:19 +08:00
|
|
|
if (desired & MAY_NOT_BLOCK)
|
2011-01-07 14:49:58 +08:00
|
|
|
return -ECHILD;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if (desired & MAY_READ) r = 1;
|
|
|
|
if (desired & MAY_WRITE) w = 1;
|
|
|
|
if (desired & MAY_EXEC) x = 1;
|
2010-06-07 08:42:10 +08:00
|
|
|
name = inode_name(ino);
|
2007-05-08 15:23:18 +08:00
|
|
|
if (name == NULL)
|
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (S_ISCHR(ino->i_mode) || S_ISBLK(ino->i_mode) ||
|
2007-10-16 16:27:13 +08:00
|
|
|
S_ISFIFO(ino->i_mode) || S_ISSOCK(ino->i_mode))
|
2005-04-17 06:20:36 +08:00
|
|
|
err = 0;
|
|
|
|
else
|
|
|
|
err = access_file(name, r, w, x);
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(name);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (!err)
|
2021-01-21 21:19:24 +08:00
|
|
|
err = generic_permission(&init_user_ns, ino, desired);
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2021-01-21 21:19:43 +08:00
|
|
|
static int hostfs_setattr(struct user_namespace *mnt_userns,
|
|
|
|
struct dentry *dentry, struct iattr *attr)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-03-18 06:25:59 +08:00
|
|
|
struct inode *inode = d_inode(dentry);
|
2005-04-17 06:20:36 +08:00
|
|
|
struct hostfs_iattr attrs;
|
|
|
|
char *name;
|
|
|
|
int err;
|
|
|
|
|
2010-06-04 17:30:02 +08:00
|
|
|
int fd = HOSTFS_I(inode)->fd;
|
2007-05-08 15:23:16 +08:00
|
|
|
|
2021-01-21 21:19:26 +08:00
|
|
|
err = setattr_prepare(&init_user_ns, dentry, attr);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2007-10-16 16:27:13 +08:00
|
|
|
if (append)
|
2005-04-17 06:20:36 +08:00
|
|
|
attr->ia_valid &= ~ATTR_SIZE;
|
|
|
|
|
|
|
|
attrs.ia_valid = 0;
|
2007-10-16 16:27:13 +08:00
|
|
|
if (attr->ia_valid & ATTR_MODE) {
|
2005-04-17 06:20:36 +08:00
|
|
|
attrs.ia_valid |= HOSTFS_ATTR_MODE;
|
|
|
|
attrs.ia_mode = attr->ia_mode;
|
|
|
|
}
|
2007-10-16 16:27:13 +08:00
|
|
|
if (attr->ia_valid & ATTR_UID) {
|
2005-04-17 06:20:36 +08:00
|
|
|
attrs.ia_valid |= HOSTFS_ATTR_UID;
|
2012-02-08 08:28:57 +08:00
|
|
|
attrs.ia_uid = from_kuid(&init_user_ns, attr->ia_uid);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-10-16 16:27:13 +08:00
|
|
|
if (attr->ia_valid & ATTR_GID) {
|
2005-04-17 06:20:36 +08:00
|
|
|
attrs.ia_valid |= HOSTFS_ATTR_GID;
|
2012-02-08 08:28:57 +08:00
|
|
|
attrs.ia_gid = from_kgid(&init_user_ns, attr->ia_gid);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-10-16 16:27:13 +08:00
|
|
|
if (attr->ia_valid & ATTR_SIZE) {
|
2005-04-17 06:20:36 +08:00
|
|
|
attrs.ia_valid |= HOSTFS_ATTR_SIZE;
|
|
|
|
attrs.ia_size = attr->ia_size;
|
|
|
|
}
|
2007-10-16 16:27:13 +08:00
|
|
|
if (attr->ia_valid & ATTR_ATIME) {
|
2005-04-17 06:20:36 +08:00
|
|
|
attrs.ia_valid |= HOSTFS_ATTR_ATIME;
|
2018-06-12 21:31:17 +08:00
|
|
|
attrs.ia_atime = (struct hostfs_timespec)
|
|
|
|
{ attr->ia_atime.tv_sec, attr->ia_atime.tv_nsec };
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-10-16 16:27:13 +08:00
|
|
|
if (attr->ia_valid & ATTR_MTIME) {
|
2005-04-17 06:20:36 +08:00
|
|
|
attrs.ia_valid |= HOSTFS_ATTR_MTIME;
|
2018-06-12 21:31:17 +08:00
|
|
|
attrs.ia_mtime = (struct hostfs_timespec)
|
|
|
|
{ attr->ia_mtime.tv_sec, attr->ia_mtime.tv_nsec };
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-10-16 16:27:13 +08:00
|
|
|
if (attr->ia_valid & ATTR_CTIME) {
|
2005-04-17 06:20:36 +08:00
|
|
|
attrs.ia_valid |= HOSTFS_ATTR_CTIME;
|
2018-06-12 21:31:17 +08:00
|
|
|
attrs.ia_ctime = (struct hostfs_timespec)
|
|
|
|
{ attr->ia_ctime.tv_sec, attr->ia_ctime.tv_nsec };
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-10-16 16:27:13 +08:00
|
|
|
if (attr->ia_valid & ATTR_ATIME_SET) {
|
2005-04-17 06:20:36 +08:00
|
|
|
attrs.ia_valid |= HOSTFS_ATTR_ATIME_SET;
|
|
|
|
}
|
2007-10-16 16:27:13 +08:00
|
|
|
if (attr->ia_valid & ATTR_MTIME_SET) {
|
2005-04-17 06:20:36 +08:00
|
|
|
attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET;
|
|
|
|
}
|
2010-06-07 08:42:10 +08:00
|
|
|
name = dentry_name(dentry);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (name == NULL)
|
2007-05-08 15:23:18 +08:00
|
|
|
return -ENOMEM;
|
2007-05-08 15:23:16 +08:00
|
|
|
err = set_attr(name, &attrs, fd);
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(name);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (err)
|
2007-05-08 15:23:18 +08:00
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-04 17:30:02 +08:00
|
|
|
if ((attr->ia_valid & ATTR_SIZE) &&
|
2012-10-20 18:02:59 +08:00
|
|
|
attr->ia_size != i_size_read(inode))
|
2012-10-06 16:31:13 +08:00
|
|
|
truncate_setsize(inode, attr->ia_size);
|
2010-06-04 17:30:02 +08:00
|
|
|
|
2021-01-21 21:19:26 +08:00
|
|
|
setattr_copy(&init_user_ns, inode, attr);
|
2010-06-04 17:30:02 +08:00
|
|
|
mark_inode_dirty(inode);
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-02-12 16:55:39 +08:00
|
|
|
static const struct inode_operations hostfs_iops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.permission = hostfs_permission,
|
|
|
|
.setattr = hostfs_setattr,
|
|
|
|
};
|
|
|
|
|
2007-02-12 16:55:39 +08:00
|
|
|
static const struct inode_operations hostfs_dir_iops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.create = hostfs_create,
|
|
|
|
.lookup = hostfs_lookup,
|
|
|
|
.link = hostfs_link,
|
|
|
|
.unlink = hostfs_unlink,
|
|
|
|
.symlink = hostfs_symlink,
|
|
|
|
.mkdir = hostfs_mkdir,
|
|
|
|
.rmdir = hostfs_rmdir,
|
|
|
|
.mknod = hostfs_mknod,
|
2016-09-27 17:03:58 +08:00
|
|
|
.rename = hostfs_rename2,
|
2005-04-17 06:20:36 +08:00
|
|
|
.permission = hostfs_permission,
|
|
|
|
.setattr = hostfs_setattr,
|
|
|
|
};
|
|
|
|
|
2015-11-17 23:20:54 +08:00
|
|
|
static const char *hostfs_get_link(struct dentry *dentry,
|
2015-12-30 04:58:39 +08:00
|
|
|
struct inode *inode,
|
|
|
|
struct delayed_call *done)
|
2010-06-07 09:51:16 +08:00
|
|
|
{
|
2015-11-17 23:20:54 +08:00
|
|
|
char *link;
|
|
|
|
if (!dentry)
|
|
|
|
return ERR_PTR(-ECHILD);
|
2015-12-30 04:58:39 +08:00
|
|
|
link = kmalloc(PATH_MAX, GFP_KERNEL);
|
2010-06-07 09:51:16 +08:00
|
|
|
if (link) {
|
|
|
|
char *path = dentry_name(dentry);
|
|
|
|
int err = -ENOMEM;
|
|
|
|
if (path) {
|
2010-08-18 18:21:10 +08:00
|
|
|
err = hostfs_do_readlink(path, link, PATH_MAX);
|
2010-06-07 09:51:16 +08:00
|
|
|
if (err == PATH_MAX)
|
|
|
|
err = -E2BIG;
|
2010-06-07 11:16:34 +08:00
|
|
|
__putname(path);
|
2010-06-07 09:51:16 +08:00
|
|
|
}
|
|
|
|
if (err < 0) {
|
2015-12-30 04:58:39 +08:00
|
|
|
kfree(link);
|
2015-05-03 01:32:22 +08:00
|
|
|
return ERR_PTR(err);
|
2010-06-07 09:51:16 +08:00
|
|
|
}
|
|
|
|
} else {
|
2015-05-03 01:32:22 +08:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2010-06-07 09:51:16 +08:00
|
|
|
|
2015-12-30 04:58:39 +08:00
|
|
|
set_delayed_call(done, kfree_link, link);
|
|
|
|
return link;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2010-06-07 09:51:16 +08:00
|
|
|
static const struct inode_operations hostfs_link_iops = {
|
2015-11-17 23:20:54 +08:00
|
|
|
.get_link = hostfs_get_link,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent)
|
|
|
|
{
|
|
|
|
struct inode *root_inode;
|
2007-03-29 16:20:33 +08:00
|
|
|
char *host_root_path, *req_root = d;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
sb->s_blocksize = 1024;
|
|
|
|
sb->s_blocksize_bits = 10;
|
|
|
|
sb->s_magic = HOSTFS_SUPER_MAGIC;
|
|
|
|
sb->s_op = &hostfs_sbops;
|
2013-10-26 06:47:37 +08:00
|
|
|
sb->s_d_op = &simple_dentry_operations;
|
2009-07-01 02:41:44 +08:00
|
|
|
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
2021-11-05 16:10:51 +08:00
|
|
|
err = super_setup_bdi(sb);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2020-03-20 21:07:35 +08:00
|
|
|
/* NULL is printed as '(null)' by printf(): avoid that. */
|
2007-03-29 16:20:33 +08:00
|
|
|
if (req_root == NULL)
|
|
|
|
req_root = "";
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
err = -ENOMEM;
|
2010-06-07 05:53:01 +08:00
|
|
|
sb->s_fs_info = host_root_path =
|
2020-03-20 21:07:35 +08:00
|
|
|
kasprintf(GFP_KERNEL, "%s/%s", root_ino, req_root);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (host_root_path == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out;
|
|
|
|
|
2010-06-07 06:43:19 +08:00
|
|
|
root_inode = new_inode(sb);
|
|
|
|
if (!root_inode)
|
2010-06-07 05:53:01 +08:00
|
|
|
goto out;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-06-07 08:33:12 +08:00
|
|
|
err = read_name(root_inode, host_root_path);
|
|
|
|
if (err)
|
|
|
|
goto out_put;
|
2010-06-07 06:43:19 +08:00
|
|
|
|
2010-06-07 08:33:12 +08:00
|
|
|
if (S_ISLNK(root_inode->i_mode)) {
|
2010-06-07 06:43:19 +08:00
|
|
|
char *name = follow_link(host_root_path);
|
2016-07-13 18:12:34 +08:00
|
|
|
if (IS_ERR(name)) {
|
2010-06-07 06:43:19 +08:00
|
|
|
err = PTR_ERR(name);
|
2016-07-13 18:12:34 +08:00
|
|
|
goto out_put;
|
|
|
|
}
|
|
|
|
err = read_name(root_inode, name);
|
2010-06-07 06:43:19 +08:00
|
|
|
kfree(name);
|
2010-06-07 08:33:12 +08:00
|
|
|
if (err)
|
|
|
|
goto out_put;
|
2010-06-07 06:43:19 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
err = -ENOMEM;
|
2012-01-09 11:15:13 +08:00
|
|
|
sb->s_root = d_make_root(root_inode);
|
2007-10-16 16:27:13 +08:00
|
|
|
if (sb->s_root == NULL)
|
2012-01-09 11:15:13 +08:00
|
|
|
goto out;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-05-08 15:23:18 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-05-08 15:23:18 +08:00
|
|
|
out_put:
|
|
|
|
iput(root_inode);
|
|
|
|
out:
|
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2010-07-25 15:46:36 +08:00
|
|
|
static struct dentry *hostfs_read_sb(struct file_system_type *type,
|
[PATCH] VFS: Permit filesystem to override root dentry on mount
Extend the get_sb() filesystem operation to take an extra argument that
permits the VFS to pass in the target vfsmount that defines the mountpoint.
The filesystem is then required to manually set the superblock and root dentry
pointers. For most filesystems, this should be done with simple_set_mnt()
which will set the superblock pointer and then set the root dentry to the
superblock's s_root (as per the old default behaviour).
The get_sb() op now returns an integer as there's now no need to return the
superblock pointer.
This patch permits a superblock to be implicitly shared amongst several mount
points, such as can be done with NFS to avoid potential inode aliasing. In
such a case, simple_set_mnt() would not be called, and instead the mnt_root
and mnt_sb would be set directly.
The patch also makes the following changes:
(*) the get_sb_*() convenience functions in the core kernel now take a vfsmount
pointer argument and return an integer, so most filesystems have to change
very little.
(*) If one of the convenience function is not used, then get_sb() should
normally call simple_set_mnt() to instantiate the vfsmount. This will
always return 0, and so can be tail-called from get_sb().
(*) generic_shutdown_super() now calls shrink_dcache_sb() to clean up the
dcache upon superblock destruction rather than shrink_dcache_anon().
This is required because the superblock may now have multiple trees that
aren't actually bound to s_root, but that still need to be cleaned up. The
currently called functions assume that the whole tree is rooted at s_root,
and that anonymous dentries are not the roots of trees which results in
dentries being left unculled.
However, with the way NFS superblock sharing are currently set to be
implemented, these assumptions are violated: the root of the filesystem is
simply a dummy dentry and inode (the real inode for '/' may well be
inaccessible), and all the vfsmounts are rooted on anonymous[*] dentries
with child trees.
[*] Anonymous until discovered from another tree.
(*) The documentation has been adjusted, including the additional bit of
changing ext2_* into foo_* in the documentation.
[akpm@osdl.org: convert ipath_fs, do other stuff]
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: Al Viro <viro@zeniv.linux.org.uk>
Cc: Nathan Scott <nathans@sgi.com>
Cc: Roland Dreier <rolandd@cisco.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-23 17:02:57 +08:00
|
|
|
int flags, const char *dev_name,
|
2010-07-25 15:46:36 +08:00
|
|
|
void *data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-07-25 15:46:36 +08:00
|
|
|
return mount_nodev(type, flags, data, hostfs_fill_sb_common);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2010-06-07 05:53:01 +08:00
|
|
|
static void hostfs_kill_sb(struct super_block *s)
|
|
|
|
{
|
|
|
|
kill_anon_super(s);
|
|
|
|
kfree(s->s_fs_info);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct file_system_type hostfs_type = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.name = "hostfs",
|
2010-07-25 15:46:36 +08:00
|
|
|
.mount = hostfs_read_sb,
|
2010-06-07 05:53:01 +08:00
|
|
|
.kill_sb = hostfs_kill_sb,
|
2005-04-17 06:20:36 +08:00
|
|
|
.fs_flags = 0,
|
|
|
|
};
|
2013-03-11 22:05:42 +08:00
|
|
|
MODULE_ALIAS_FS("hostfs");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static int __init init_hostfs(void)
|
|
|
|
{
|
2021-01-14 04:31:55 +08:00
|
|
|
hostfs_inode_cache = KMEM_CACHE(hostfs_inode_info, 0);
|
|
|
|
if (!hostfs_inode_cache)
|
|
|
|
return -ENOMEM;
|
2007-05-08 15:23:18 +08:00
|
|
|
return register_filesystem(&hostfs_type);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit exit_hostfs(void)
|
|
|
|
{
|
|
|
|
unregister_filesystem(&hostfs_type);
|
2021-01-14 04:31:55 +08:00
|
|
|
kmem_cache_destroy(hostfs_inode_cache);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(init_hostfs)
|
|
|
|
module_exit(exit_hostfs)
|
|
|
|
MODULE_LICENSE("GPL");
|