From: Giuseppe Scrivano <gscrivan@redhat.com>
To: linux-unionfs@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org, linux-api@vger.kernel.org
Subject: [PATCH] ovl: add ioctls to retrieve layer file descriptors
Date: Wed, 8 Jul 2026 11:58:31 +0200 [thread overview]
Message-ID: <20260708095831.3381978-1-gscrivan@redhat.com> (raw)
Add two ioctls to overlay filesystem to allow userspace to retrieve
information about the overlay layers:
OVL_IOC_OPEN_LAYER: return an O_PATH fd to the root of a layer.
arg == 0 returns the upper layer (-ENOENT if
no upper is configured), arg >= 1 returns
lower layers (-ENOENT if index is out of
range).
OVL_IOC_GET_LAYERS_INFO: copy a struct ovl_layers_info to userspace
with numlower, numlowerdata, and has_upper.
The ioctls work on any overlayfs file or directory and require
CAP_SYS_ADMIN in the mounter's user namespace.
The UAPI constants and struct are defined in include/uapi/linux/overlay.h.
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
---
MAINTAINERS | 1 +
fs/overlayfs/file.c | 2 ++
fs/overlayfs/overlayfs.h | 4 +++
fs/overlayfs/ovl_entry.h | 2 ++
fs/overlayfs/params.c | 10 ++++++
fs/overlayfs/params.h | 1 +
fs/overlayfs/readdir.c | 2 ++
fs/overlayfs/super.c | 70 ++++++++++++++++++++++++++++++++++++
include/uapi/linux/overlay.h | 30 ++++++++++++++++
9 files changed, 122 insertions(+)
create mode 100644 include/uapi/linux/overlay.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 25453040dffb..b64c696686e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20368,6 +20368,7 @@ S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs.git
F: Documentation/filesystems/overlayfs.rst
F: fs/overlayfs/
+F: include/uapi/linux/overlay.h
P54 WIRELESS DRIVER
M: Christian Lamparter <chunkeey@googlemail.com>
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index 27cc07738f33..fc9c448c5959 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -649,4 +649,6 @@ const struct file_operations ovl_file_operations = {
.copy_file_range = ovl_copy_file_range,
.remap_file_range = ovl_remap_file_range,
.setlease = generic_setlease,
+ .unlocked_ioctl = ovl_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
};
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index b75df37f70ac..10f7ef8cb78c 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -8,6 +8,7 @@
#include <linux/uuid.h>
#include <linux/fs.h>
#include <linux/fsverity.h>
+#include <uapi/linux/overlay.h>
#include <linux/namei.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
@@ -908,6 +909,9 @@ void ovl_tempname(char name[OVL_TEMPNAME_SIZE]);
struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
struct ovl_cattr *attr);
+/* super.c */
+long ovl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+
/* file.c */
extern const struct file_operations ovl_file_operations;
int ovl_real_fileattr_get(const struct path *realpath, struct file_kattr *fa);
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h
index 80cad4ea96a3..b8f4bca89a27 100644
--- a/fs/overlayfs/ovl_entry.h
+++ b/fs/overlayfs/ovl_entry.h
@@ -35,6 +35,8 @@ struct ovl_layer {
struct vfsmount *mnt;
/* Trap in ovl inode cache */
struct inode *trap;
+ /* Keeps the original fsmount file alive for OVL_IOC_OPEN_LAYER */
+ struct file *origin;
struct ovl_sb *fs;
/* Index of this layer in fs root (upper idx == 0) */
int idx;
diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c
index c93fcaa45d4a..92d1a56178f2 100644
--- a/fs/overlayfs/params.c
+++ b/fs/overlayfs/params.c
@@ -482,6 +482,11 @@ static int ovl_parse_layer(struct fs_context *fc, struct fs_parameter *param,
return PTR_ERR(layer_name);
err = ovl_do_parse_layer(fc, layer_name, &layer_path, layer);
+ if (!err && !is_upper_layer(layer)) {
+ struct ovl_fs_context *ctx = fc->fs_private;
+
+ ctx->lower[ctx->nr - 1].origin = get_file(param->file);
+ }
break;
}
default:
@@ -504,6 +509,9 @@ static void ovl_reset_lowerdirs(struct ovl_fs_context *ctx)
path_put(&l->path);
kfree(l->name);
l->name = NULL;
+ if (l->origin)
+ fput(l->origin);
+ l->origin = NULL;
}
ctx->nr = 0;
ctx->nr_data = 0;
@@ -856,6 +864,8 @@ void ovl_free_fs(struct ovl_fs *ofs)
mounts = (struct vfsmount **) ofs->config.lowerdirs;
for (i = 0; i < ofs->numlayer; i++) {
iput(ofs->layers[i].trap);
+ if (ofs->layers[i].origin)
+ fput(ofs->layers[i].origin);
kfree(ofs->config.lowerdirs[i]);
mounts[i] = ofs->layers[i].mnt;
}
diff --git a/fs/overlayfs/params.h b/fs/overlayfs/params.h
index ffd53cdd8482..1d8fe8fbaca2 100644
--- a/fs/overlayfs/params.h
+++ b/fs/overlayfs/params.h
@@ -22,6 +22,7 @@ struct ovl_opt_set {
struct ovl_fs_context_layer {
char *name;
struct path path;
+ struct file *origin;
};
struct ovl_fs_context {
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index e7fe29cb6028..7bab71c8bcc2 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -1069,6 +1069,8 @@ const struct file_operations ovl_dir_operations = {
.iterate_shared = shared_ovl_iterate,
.llseek = ovl_dir_llseek,
.fsync = ovl_dir_fsync,
+ .unlocked_ioctl = ovl_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
.release = ovl_dir_release,
.setlease = generic_setlease,
};
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 60f0b7ceef0a..d143002e74b2 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -1107,6 +1107,8 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
*/
mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME;
+ layers[ofs->numlayer].origin = l->origin;
+ l->origin = NULL;
layers[ofs->numlayer].trap = trap;
layers[ofs->numlayer].mnt = mnt;
layers[ofs->numlayer].idx = ofs->numlayer;
@@ -1568,6 +1570,74 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
return err;
}
+static long ovl_ioctl_open_layer(struct file *filp, unsigned long arg)
+{
+ struct super_block *sb = file_inode(filp)->i_sb;
+ struct ovl_fs *ofs = OVL_FS(sb);
+ struct path root;
+ struct file *f;
+ int fd;
+
+ if (arg >= ofs->numlayer)
+ return -ENOENT;
+ if (arg == 0 && !ovl_upper_mnt(ofs))
+ return -ENOENT;
+ if (!ofs->layers[arg].origin)
+ return -EOPNOTSUPP;
+
+ root.mnt = mntget(ofs->layers[arg].origin->f_path.mnt);
+ root.dentry = dget(root.mnt->mnt_root);
+
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0) {
+ path_put(&root);
+ return fd;
+ }
+
+ f = dentry_open(&root, O_PATH | O_NOFOLLOW, current_cred());
+ path_put(&root);
+ if (IS_ERR(f)) {
+ put_unused_fd(fd);
+ return PTR_ERR(f);
+ }
+
+ fd_install(fd, f);
+ return fd;
+}
+
+static long ovl_ioctl_get_layers_info(struct file *filp, unsigned long arg)
+{
+ struct super_block *sb = file_inode(filp)->i_sb;
+ struct ovl_fs *ofs = OVL_FS(sb);
+ struct ovl_layers_info info = {
+ .numlower = ofs->numlayer - 1,
+ .numlowerdata = ofs->numdatalayer,
+ .has_upper = !!ovl_upper_mnt(ofs),
+ };
+
+ if (copy_to_user((void __user *)arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+long ovl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct ovl_fs *ofs = OVL_FS(file_inode(filp)->i_sb);
+
+ if (!ns_capable(ofs->creator_cred->user_ns, CAP_SYS_ADMIN))
+ return -EPERM;
+
+ switch (cmd) {
+ case OVL_IOC_OPEN_LAYER:
+ return ovl_ioctl_open_layer(filp, arg);
+ case OVL_IOC_GET_LAYERS_INFO:
+ return ovl_ioctl_get_layers_info(filp, arg);
+ default:
+ return -ENOTTY;
+ }
+}
+
struct file_system_type ovl_fs_type = {
.owner = THIS_MODULE,
.name = "overlay",
diff --git a/include/uapi/linux/overlay.h b/include/uapi/linux/overlay.h
new file mode 100644
index 000000000000..c92ccecd9e21
--- /dev/null
+++ b/include/uapi/linux/overlay.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_OVERLAY_H
+#define _UAPI_LINUX_OVERLAY_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/**
+ * struct ovl_layers_info - overlay layer configuration summary
+ * @numlower: number of lower (metadata) layers
+ * @numlowerdata: number of data-only lower layers
+ * @has_upper: 1 if an upper layer is configured, 0 otherwise
+ */
+struct ovl_layers_info {
+ __u32 numlower;
+ __u32 numlowerdata;
+ __u32 has_upper;
+};
+
+/*
+ * Return an O_PATH fd to the root of the specified overlay layer.
+ * arg == 0: upper layer (returns -ENOENT if no upper is configured)
+ * arg >= 1: lower layers (returns -ENOENT if index is out of range)
+ */
+#define OVL_IOC_OPEN_LAYER _IO('O', 1)
+
+/* Retrieve overlay layer configuration into struct ovl_layers_info. */
+#define OVL_IOC_GET_LAYERS_INFO _IOR('O', 2, struct ovl_layers_info)
+
+#endif /* _UAPI_LINUX_OVERLAY_H */
--
2.55.0
next reply other threads:[~2026-07-08 9:58 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-08 9:58 Giuseppe Scrivano [this message]
2026-07-08 10:24 ` [PATCH] ovl: add ioctls to retrieve layer file descriptors Miklos Szeredi
2026-07-08 12:27 ` Giuseppe Scrivano
2026-07-08 13:23 ` Amir Goldstein
2026-07-08 13:44 ` Giuseppe Scrivano
2026-07-08 14:01 ` Miklos Szeredi
2026-07-08 20:45 ` Giuseppe Scrivano
2026-07-08 14:08 ` Miklos Szeredi
2026-07-08 14:31 ` Giuseppe Scrivano
2026-07-08 15:37 ` Miklos Szeredi
2026-07-08 15:55 ` Giuseppe Scrivano
2026-07-08 19:01 ` Amir Goldstein
2026-07-08 20:16 ` Colin Walters
2026-07-08 20:18 ` Giuseppe Scrivano
2026-07-08 19:45 ` Miklos Szeredi
2026-07-08 20:35 ` Giuseppe Scrivano
2026-07-09 6:11 ` Miklos Szeredi
2026-07-09 14:32 ` Amir Goldstein
2026-07-10 18:23 ` Miklos Szeredi
2026-07-08 10:40 ` Amir Goldstein
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260708095831.3381978-1-gscrivan@redhat.com \
--to=gscrivan@redhat.com \
--cc=linux-api@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-unionfs@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox