summaryrefslogtreecommitdiffstats
path: root/fs/ramfs
diff options
context:
space:
mode:
authorJinke Han <jinkehan@didiglobal.com>2026-09-08 15:37:42 +0800
committerIngo Molnar <mingo@kernel.org>2026-09-17 09:48:42 +0200
commita5f7a5bb3b7f28ba7e4fa246775b29a0e5537255 (patch)
tree73fef824f9a846cf935869d0dbc5070ef6e2f741 /fs/ramfs
downloadlinux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.tar.gz
linux-stable-a5f7a5bb3b7f28ba7e4fa246775b29a0e5537255.zip
x86/kprobes: Fix crash when probing CS CALL instructionsgrafted
When using eBPF to probe CS CALL instructions within a function, a crash can be triggered. The eBPF tool probes offset 257 of the __hrtimer_run_queues() function: <__hrtimer_run_queues+249>: nopl 0x0(%rax,%rax,1) <__hrtimer_run_queues+254>: mov %r14,%rdi <__hrtimer_run_queues+257>: cs call <__x86_indirect_thunk_r12> <__hrtimer_run_queues+263>: mov %eax,%r12d <__hrtimer_run_queues+266>: xchg %ax,%ax <__hrtimer_run_queues+268>: mov %r13,%rdi Which triggers this crash: BUG: unable to handle page fault for address: 00000000000f41c9 #PF: supervisor write access in kernel mode #PF: error_code(0x0002) - not-present page PGD 0 P4D 0 Oops: 0002 [#1] SMP NOPTI CPU: 1 PID: 0 Comm: swapper/1 Kdump: loaded Tainted: P RIP: 0010:__hrtimer_run_queues+0x106/0x230 Note that __hrtimer_run_queues+0x106 is __hrtimer_run_queues+262, which is at the 6th byte of the above CS CALL instruction. Since the CS CALL instruction occupies 6 bytes, the exception occurred in the middle of that call instruction. The root cause is that when using eBPF tools to probe in the middle of a function, a kprobe with INT3 is used as the underlying implementation. During single-step emulation of the original CALL instruction, int3_emulate_call() assumes that the probed CALL instruction is 5 bytes long. However, the actual CS-prefixed CALL instruction occupies 6 bytes, so it constructs an incorrect exception return address. When the CPU returns from the kprobe handler, the next instruction to be executed is at the address of the last byte of that CS CALL instruction. Coincidentally, starting from that address, the CPU fetches and decodes a completely different instruction, which ultimately triggers a kernel crash. Fix the issue by using the actual instruction length obtained from the instruction decoder when constructing the exception return address, rather than relying on the hardcoded CALL_INSN_SIZE macro. [ mingo: Refined the changelog ] Fixes: 6256e668b7af ("x86/kprobes: Use int3 instead of debug trap for single-step") Suggested-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Signed-off-by: Jinke Han <jinkehan@didiglobal.com> Signed-off-by: Ingo Molnar <mingo@kernel.org> Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org> Acked-by: Yafang Shao <laoar.shao@gmail.com> Acked-by: Borislav Petkov <bp@alien8.de> Cc: Peter Zijlstra <peterz@infradead.org> Link: https://patch.msgid.link/20260908073742.GA10517@didi-ThinkCentre-M920t-N000
Diffstat (limited to 'fs/ramfs')
-rw-r--r--fs/ramfs/Makefile10
-rw-r--r--fs/ramfs/file-mmu.c55
-rw-r--r--fs/ramfs/file-nommu.c276
-rw-r--r--fs/ramfs/inode.c329
-rw-r--r--fs/ramfs/internal.h9
5 files changed, 679 insertions, 0 deletions
diff --git a/fs/ramfs/Makefile b/fs/ramfs/Makefile
new file mode 100644
index 000000000..d1198adb5
--- /dev/null
+++ b/fs/ramfs/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the linux ramfs routines.
+#
+
+obj-y += ramfs.o
+
+file-mmu-y := file-nommu.o
+file-mmu-$(CONFIG_MMU) := file-mmu.o
+ramfs-objs += inode.o $(file-mmu-y)
diff --git a/fs/ramfs/file-mmu.c b/fs/ramfs/file-mmu.c
new file mode 100644
index 000000000..c3ed1c511
--- /dev/null
+++ b/fs/ramfs/file-mmu.c
@@ -0,0 +1,55 @@
+/* file-mmu.c: ramfs MMU-based file operations
+ *
+ * Resizable simple ram filesystem for Linux.
+ *
+ * Copyright (C) 2000 Linus Torvalds.
+ * 2000 Transmeta Corp.
+ *
+ * Usage limits added by David Gibson, Linuxcare Australia.
+ * This file is released under the GPL.
+ */
+
+/*
+ * NOTE! This filesystem is probably most useful
+ * not as a real filesystem, but as an example of
+ * how virtual filesystems can be written.
+ *
+ * It doesn't get much simpler than this. Consider
+ * that this file implements the full semantics of
+ * a POSIX-compliant read-write filesystem.
+ *
+ * Note in particular how the filesystem does not
+ * need to implement any data structures of its own
+ * to keep track of the virtual data: using the VFS
+ * caches is sufficient.
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/ramfs.h>
+#include <linux/sched.h>
+
+#include "internal.h"
+
+static unsigned long ramfs_mmu_get_unmapped_area(struct file *file,
+ unsigned long addr, unsigned long len, unsigned long pgoff,
+ unsigned long flags)
+{
+ return mm_get_unmapped_area(file, addr, len, pgoff, flags);
+}
+
+const struct file_operations ramfs_file_operations = {
+ .read_iter = generic_file_read_iter,
+ .write_iter = generic_file_write_iter,
+ .mmap_prepare = generic_file_mmap_prepare,
+ .fsync = noop_fsync,
+ .splice_read = filemap_splice_read,
+ .splice_write = iter_file_splice_write,
+ .llseek = generic_file_llseek,
+ .get_unmapped_area = ramfs_mmu_get_unmapped_area,
+};
+
+const struct inode_operations ramfs_file_inode_operations = {
+ .setattr = simple_setattr,
+ .getattr = simple_getattr,
+};
diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c
new file mode 100644
index 000000000..fb471bf88
--- /dev/null
+++ b/fs/ramfs/file-nommu.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* file-nommu.c: no-MMU version of ramfs
+ *
+ * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/highmem.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/backing-dev.h>
+#include <linux/ramfs.h>
+#include <linux/folio_batch.h>
+#include <linux/mman.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <linux/uaccess.h>
+#include "internal.h"
+
+static int ramfs_nommu_setattr(struct mnt_idmap *, struct dentry *, struct iattr *);
+static unsigned long ramfs_nommu_get_unmapped_area(struct file *file,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags);
+static int ramfs_nommu_mmap_prepare(struct vm_area_desc *desc);
+
+static unsigned ramfs_mmap_capabilities(struct file *file)
+{
+ return NOMMU_MAP_DIRECT | NOMMU_MAP_COPY | NOMMU_MAP_READ |
+ NOMMU_MAP_WRITE | NOMMU_MAP_EXEC;
+}
+
+const struct file_operations ramfs_file_operations = {
+ .mmap_capabilities = ramfs_mmap_capabilities,
+ .mmap_prepare = ramfs_nommu_mmap_prepare,
+ .get_unmapped_area = ramfs_nommu_get_unmapped_area,
+ .read_iter = generic_file_read_iter,
+ .write_iter = generic_file_write_iter,
+ .fsync = noop_fsync,
+ .splice_read = filemap_splice_read,
+ .splice_write = iter_file_splice_write,
+ .llseek = generic_file_llseek,
+};
+
+const struct inode_operations ramfs_file_inode_operations = {
+ .setattr = ramfs_nommu_setattr,
+ .getattr = simple_getattr,
+};
+
+/*****************************************************************************/
+/*
+ * add a contiguous set of pages into a ramfs inode when it's truncated from
+ * size 0 on the assumption that it's going to be used for an mmap of shared
+ * memory
+ */
+int ramfs_nommu_expand_for_mapping(struct inode *inode, size_t newsize)
+{
+ unsigned long npages, xpages, loop;
+ struct page *pages;
+ unsigned order;
+ void *data;
+ int ret;
+ gfp_t gfp = mapping_gfp_mask(inode->i_mapping);
+
+ /* make various checks */
+ if (!newsize)
+ return 0;
+
+ order = get_order(newsize);
+ if (unlikely(order > MAX_PAGE_ORDER))
+ return -EFBIG;
+
+ ret = inode_newsize_ok(inode, newsize);
+ if (ret)
+ return ret;
+
+ i_size_write(inode, newsize);
+
+ /* allocate enough contiguous pages to be able to satisfy the
+ * request */
+ pages = alloc_pages(gfp, order);
+ if (!pages)
+ return -ENOMEM;
+
+ /* split the high-order page into an array of single pages */
+ xpages = 1UL << order;
+ npages = (newsize + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ split_page(pages, order);
+
+ /* trim off any pages we don't actually require */
+ for (loop = npages; loop < xpages; loop++)
+ __free_page(pages + loop);
+
+ /* clear the memory we allocated */
+ newsize = PAGE_SIZE * npages;
+ data = page_address(pages);
+ memset(data, 0, newsize);
+
+ /* attach all the pages to the inode's address space */
+ for (loop = 0; loop < npages; loop++) {
+ struct page *page = pages + loop;
+
+ ret = add_to_page_cache_lru(page, inode->i_mapping, loop,
+ gfp);
+ if (ret < 0)
+ goto add_error;
+
+ /* prevent the page from being discarded on memory pressure */
+ SetPageDirty(page);
+ SetPageUptodate(page);
+
+ unlock_page(page);
+ put_page(page);
+ }
+
+ return 0;
+
+add_error:
+ while (loop < npages)
+ __free_page(pages + loop++);
+ return ret;
+}
+
+/*****************************************************************************/
+/*
+ *
+ */
+static int ramfs_nommu_resize(struct inode *inode, loff_t newsize, loff_t size)
+{
+ int ret;
+
+ /* assume a truncate from zero size is going to be for the purposes of
+ * shared mmap */
+ if (size == 0) {
+ if (unlikely(newsize >> 32))
+ return -EFBIG;
+
+ return ramfs_nommu_expand_for_mapping(inode, newsize);
+ }
+
+ /* check that a decrease in size doesn't cut off any shared mappings */
+ if (newsize < size) {
+ ret = nommu_shrink_inode_mappings(inode, size, newsize);
+ if (ret < 0)
+ return ret;
+ }
+
+ truncate_setsize(inode, newsize);
+ return 0;
+}
+
+/*****************************************************************************/
+/*
+ * handle a change of attributes
+ * - we're specifically interested in a change of size
+ */
+static int ramfs_nommu_setattr(struct mnt_idmap *idmap,
+ struct dentry *dentry, struct iattr *ia)
+{
+ struct inode *inode = d_inode(dentry);
+ unsigned int old_ia_valid = ia->ia_valid;
+ int ret = 0;
+
+ /* POSIX UID/GID verification for setting inode attributes */
+ ret = setattr_prepare(&nop_mnt_idmap, dentry, ia);
+ if (ret)
+ return ret;
+
+ /* pick out size-changing events */
+ if (ia->ia_valid & ATTR_SIZE) {
+ loff_t size = inode->i_size;
+
+ if (ia->ia_size != size) {
+ ret = ramfs_nommu_resize(inode, ia->ia_size, size);
+ if (ret < 0 || ia->ia_valid == ATTR_SIZE)
+ goto out;
+ } else {
+ /* we skipped the truncate but must still update
+ * timestamps
+ */
+ ia->ia_valid |= ATTR_MTIME|ATTR_CTIME;
+ }
+ }
+
+ setattr_copy(&nop_mnt_idmap, inode, ia);
+ out:
+ ia->ia_valid = old_ia_valid;
+ return ret;
+}
+
+/*****************************************************************************/
+/*
+ * try to determine where a shared mapping can be made
+ * - we require that:
+ * - the pages to be mapped must exist
+ * - the pages be physically contiguous in sequence
+ */
+static unsigned long ramfs_nommu_get_unmapped_area(struct file *file,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags)
+{
+ unsigned long maxpages, lpages, nr_folios, loop, ret, nr_pages, pfn;
+ struct inode *inode = file_inode(file);
+ struct folio_batch fbatch;
+ loff_t isize;
+
+ /* the mapping mustn't extend beyond the EOF */
+ lpages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ isize = i_size_read(inode);
+
+ ret = -ENOSYS;
+ maxpages = (isize + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ if (pgoff >= maxpages)
+ goto out;
+
+ if (maxpages - pgoff < lpages)
+ goto out;
+
+ /* gang-find the pages */
+ folio_batch_init(&fbatch);
+ nr_pages = 0;
+repeat:
+ nr_folios = filemap_get_folios_contig(inode->i_mapping, &pgoff,
+ ULONG_MAX, &fbatch);
+ if (!nr_folios) {
+ ret = -ENOSYS;
+ return ret;
+ }
+
+ if (ret == -ENOSYS) {
+ ret = (unsigned long) folio_address(fbatch.folios[0]);
+ pfn = folio_pfn(fbatch.folios[0]);
+ }
+ /* check the pages for physical adjacency */
+ for (loop = 0; loop < nr_folios; loop++) {
+ if (pfn + nr_pages != folio_pfn(fbatch.folios[loop])) {
+ ret = -ENOSYS;
+ goto out_free; /* leave if not physical adjacent */
+ }
+ nr_pages += folio_nr_pages(fbatch.folios[loop]);
+ if (nr_pages >= lpages)
+ goto out_free; /* successfully found desired pages*/
+ }
+
+ if (nr_pages < lpages) {
+ folio_batch_release(&fbatch);
+ goto repeat; /* loop if pages are missing */
+ }
+ /* okay - all conditions fulfilled */
+
+out_free:
+ folio_batch_release(&fbatch);
+out:
+ return ret;
+}
+
+/*****************************************************************************/
+/*
+ * set up a mapping for shared memory segments
+ */
+static int ramfs_nommu_mmap_prepare(struct vm_area_desc *desc)
+{
+ if (!is_nommu_shared_vma_flags(&desc->vma_flags))
+ return -ENOSYS;
+
+ file_accessed(desc->file);
+ desc->vm_ops = &generic_file_vm_ops;
+ return 0;
+}
diff --git a/fs/ramfs/inode.c b/fs/ramfs/inode.c
new file mode 100644
index 000000000..0a88ede48
--- /dev/null
+++ b/fs/ramfs/inode.c
@@ -0,0 +1,329 @@
+/*
+ * Resizable simple ram filesystem for Linux.
+ *
+ * Copyright (C) 2000 Linus Torvalds.
+ * 2000 Transmeta Corp.
+ *
+ * Usage limits added by David Gibson, Linuxcare Australia.
+ * This file is released under the GPL.
+ */
+
+/*
+ * NOTE! This filesystem is probably most useful
+ * not as a real filesystem, but as an example of
+ * how virtual filesystems can be written.
+ *
+ * It doesn't get much simpler than this. Consider
+ * that this file implements the full semantics of
+ * a POSIX-compliant read-write filesystem.
+ *
+ * Note in particular how the filesystem does not
+ * need to implement any data structures of its own
+ * to keep track of the virtual data: using the VFS
+ * caches is sufficient.
+ */
+
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/highmem.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/backing-dev.h>
+#include <linux/ramfs.h>
+#include <linux/sched.h>
+#include <linux/parser.h>
+#include <linux/magic.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
+#include <linux/seq_file.h>
+#include "internal.h"
+
+struct ramfs_mount_opts {
+ umode_t mode;
+};
+
+struct ramfs_fs_info {
+ struct ramfs_mount_opts mount_opts;
+};
+
+#define RAMFS_DEFAULT_MODE 0755
+
+static const struct super_operations ramfs_ops;
+static const struct inode_operations ramfs_dir_inode_operations;
+
+struct inode *ramfs_get_inode(struct super_block *sb,
+ const struct inode *dir, umode_t mode, dev_t dev)
+{
+ struct inode * inode = new_inode(sb);
+
+ if (inode) {
+ inode->i_ino = get_next_ino();
+ inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
+ inode->i_mapping->a_ops = &ram_aops;
+ mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER);
+ mapping_set_unevictable(inode->i_mapping);
+ simple_inode_init_ts(inode);
+ switch (mode & S_IFMT) {
+ default:
+ init_special_inode(inode, mode, dev);
+ break;
+ case S_IFREG:
+ inode->i_op = &ramfs_file_inode_operations;
+ inode->i_fop = &ramfs_file_operations;
+ break;
+ case S_IFDIR:
+ inode->i_op = &ramfs_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+
+ /* directory inodes start off with i_nlink == 2 (for "." entry) */
+ inc_nlink(inode);
+ break;
+ case S_IFLNK:
+ inode->i_op = &page_symlink_inode_operations;
+ inode_nohighmem(inode);
+ break;
+ }
+ }
+ return inode;
+}
+
+/*
+ * File creation. Allocate an inode, and we're done..
+ */
+/* SMP-safe */
+static int
+ramfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode, dev_t dev)
+{
+ struct inode * inode = ramfs_get_inode(dir->i_sb, dir, mode, dev);
+ int error = -ENOSPC;
+
+ if (inode) {
+ error = security_inode_init_security(inode, dir,
+ &dentry->d_name, NULL,
+ NULL);
+ if (error) {
+ iput(inode);
+ goto out;
+ }
+
+ d_make_persistent(dentry, inode);
+ error = 0;
+ inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
+ }
+out:
+ return error;
+}
+
+static struct dentry *ramfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode)
+{
+ int retval = ramfs_mknod(&nop_mnt_idmap, dir, dentry, mode, 0);
+ if (!retval)
+ inc_nlink(dir);
+ return ERR_PTR(retval);
+}
+
+static int ramfs_create(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, umode_t mode)
+{
+ return ramfs_mknod(&nop_mnt_idmap, dir, dentry, mode | S_IFREG, 0);
+}
+
+static int ramfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
+ struct dentry *dentry, const char *symname)
+{
+ struct inode *inode;
+ int error = -ENOSPC;
+
+ inode = ramfs_get_inode(dir->i_sb, dir, S_IFLNK|S_IRWXUGO, 0);
+ if (inode) {
+ int l = strlen(symname)+1;
+
+ error = security_inode_init_security(inode, dir,
+ &dentry->d_name, NULL,
+ NULL);
+ if (error) {
+ iput(inode);
+ goto out;
+ }
+
+ error = page_symlink(inode, symname, l);
+ if (!error) {
+ d_make_persistent(dentry, inode);
+ inode_set_mtime_to_ts(dir,
+ inode_set_ctime_current(dir));
+ } else
+ iput(inode);
+ }
+out:
+ return error;
+}
+
+static int ramfs_tmpfile(struct mnt_idmap *idmap,
+ struct inode *dir, struct file *file, umode_t mode)
+{
+ struct inode *inode;
+ int error;
+
+ inode = ramfs_get_inode(dir->i_sb, dir, mode, 0);
+ if (!inode)
+ return -ENOSPC;
+
+ error = security_inode_init_security(inode, dir,
+ &file_dentry(file)->d_name, NULL,
+ NULL);
+ if (error) {
+ iput(inode);
+ goto out;
+ }
+
+ d_tmpfile(file, inode);
+out:
+ return finish_open_simple(file, error);
+}
+
+static const struct inode_operations ramfs_dir_inode_operations = {
+ .create = ramfs_create,
+ .lookup = simple_lookup,
+ .link = simple_link,
+ .unlink = simple_unlink,
+ .symlink = ramfs_symlink,
+ .mkdir = ramfs_mkdir,
+ .rmdir = simple_rmdir,
+ .mknod = ramfs_mknod,
+ .rename = simple_rename,
+ .tmpfile = ramfs_tmpfile,
+};
+
+/*
+ * Display the mount options in /proc/mounts.
+ */
+static int ramfs_show_options(struct seq_file *m, struct dentry *root)
+{
+ struct ramfs_fs_info *fsi = root->d_sb->s_fs_info;
+
+ if (fsi->mount_opts.mode != RAMFS_DEFAULT_MODE)
+ seq_printf(m, ",mode=%o", fsi->mount_opts.mode);
+ return 0;
+}
+
+static const struct super_operations ramfs_ops = {
+ .statfs = simple_statfs,
+ .drop_inode = inode_just_drop,
+ .show_options = ramfs_show_options,
+};
+
+enum ramfs_param {
+ Opt_mode,
+};
+
+const struct fs_parameter_spec ramfs_fs_parameters[] = {
+ fsparam_u32oct("mode", Opt_mode),
+ {}
+};
+
+static int ramfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
+{
+ struct fs_parse_result result;
+ struct ramfs_fs_info *fsi = fc->s_fs_info;
+ int opt;
+
+ opt = fs_parse(fc, ramfs_fs_parameters, param, &result);
+ if (opt == -ENOPARAM) {
+ opt = vfs_parse_fs_param_source(fc, param);
+ if (opt != -ENOPARAM)
+ return opt;
+ /*
+ * We might like to report bad mount options here;
+ * but traditionally ramfs has ignored all mount options,
+ * and as it is used as a !CONFIG_SHMEM simple substitute
+ * for tmpfs, better continue to ignore other mount options.
+ */
+ return 0;
+ }
+ if (opt < 0)
+ return opt;
+
+ switch (opt) {
+ case Opt_mode:
+ fsi->mount_opts.mode = result.uint_32 & S_IALLUGO;
+ break;
+ }
+
+ return 0;
+}
+
+static int ramfs_fill_super(struct super_block *sb, struct fs_context *fc)
+{
+ struct ramfs_fs_info *fsi = sb->s_fs_info;
+ struct inode *inode;
+
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
+ sb->s_blocksize = PAGE_SIZE;
+ sb->s_blocksize_bits = PAGE_SHIFT;
+ sb->s_magic = RAMFS_MAGIC;
+ sb->s_op = &ramfs_ops;
+ sb->s_d_flags = DCACHE_DONTCACHE;
+ sb->s_time_gran = 1;
+
+ inode = ramfs_get_inode(sb, NULL, S_IFDIR | fsi->mount_opts.mode, 0);
+ sb->s_root = d_make_root(inode);
+ if (!sb->s_root)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int ramfs_get_tree(struct fs_context *fc)
+{
+ return get_tree_nodev(fc, ramfs_fill_super);
+}
+
+static void ramfs_free_fc(struct fs_context *fc)
+{
+ kfree(fc->s_fs_info);
+}
+
+static const struct fs_context_operations ramfs_context_ops = {
+ .free = ramfs_free_fc,
+ .parse_param = ramfs_parse_param,
+ .get_tree = ramfs_get_tree,
+};
+
+int ramfs_init_fs_context(struct fs_context *fc)
+{
+ struct ramfs_fs_info *fsi;
+
+ fsi = kzalloc_obj(*fsi);
+ if (!fsi)
+ return -ENOMEM;
+
+ fsi->mount_opts.mode = RAMFS_DEFAULT_MODE;
+ fc->s_fs_info = fsi;
+ fc->ops = &ramfs_context_ops;
+ return 0;
+}
+
+void ramfs_kill_sb(struct super_block *sb)
+{
+ kfree(sb->s_fs_info);
+ kill_anon_super(sb);
+}
+
+static struct file_system_type ramfs_fs_type = {
+ .name = "ramfs",
+ .init_fs_context = ramfs_init_fs_context,
+ .parameters = ramfs_fs_parameters,
+ .kill_sb = ramfs_kill_sb,
+ .fs_flags = FS_USERNS_MOUNT,
+};
+
+static int __init init_ramfs_fs(void)
+{
+ return register_filesystem(&ramfs_fs_type);
+}
+fs_initcall(init_ramfs_fs);
diff --git a/fs/ramfs/internal.h b/fs/ramfs/internal.h
new file mode 100644
index 000000000..3257fc180
--- /dev/null
+++ b/fs/ramfs/internal.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* internal.h: ramfs internal definitions
+ *
+ * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+
+extern const struct inode_operations ramfs_file_inode_operations;