mirror of
https://github.com/torvalds/linux.git
synced 2026-03-07 23:04:33 +01:00
vfs-7.0-rc1.misc.2
Please consider pulling these changes from the signed vfs-7.0-rc1.misc.2 tag.
Thanks!
Christian
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaZMOCwAKCRCRxhvAZXjc
oswrAP9r1zjzMimjX2J0hBoMnYjNzQfLLew8+IRygImQ+yaqWgD9Fiw/cQ9eE1Hm
TMLqck/ky588ywSDaBzfztrXAY3ISgg=
=4yr2
-----END PGP SIGNATURE-----
Merge tag 'vfs-7.0-rc1.misc.2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull more misc vfs updates from Christian Brauner:
"Features:
- Optimize close_range() from O(range size) to O(active FDs) by using
find_next_bit() on the open_fds bitmap instead of linearly scanning
the entire requested range. This is a significant improvement for
large-range close operations on sparse file descriptor tables.
- Add FS_XFLAG_VERITY file attribute for fs-verity files, retrievable
via FS_IOC_FSGETXATTR and file_getattr(). The flag is read-only.
Add tracepoints for fs-verity enable and verify operations,
replacing the previously removed debug printk's.
- Prevent nfsd from exporting special kernel filesystems like pidfs
and nsfs. These filesystems have custom ->open() and ->permission()
export methods that are designed for open_by_handle_at(2) only and
are incompatible with nfsd. Update the exportfs documentation
accordingly.
Fixes:
- Fix KMSAN uninit-value in ovl_fill_real() where strcmp() was used
on a non-null-terminated decrypted directory entry name from
fscrypt. This triggered on encrypted lower layers when the
decrypted name buffer contained uninitialized tail data.
The fix also adds VFS-level name_is_dot(), name_is_dotdot(), and
name_is_dot_dotdot() helpers, replacing various open-coded "." and
".." checks across the tree.
- Fix read-only fsflags not being reset together with xflags in
vfs_fileattr_set(). Currently harmless since no read-only xflags
overlap with flags, but this would cause inconsistencies for any
future shared read-only flag
- Return -EREMOTE instead of -ESRCH from PIDFD_GET_INFO when the
target process is in a different pid namespace. This lets userspace
distinguish "process exited" from "process in another namespace",
matching glibc's pidfd_getpid() behavior
Cleanups:
- Use C-string literals in the Rust seq_file bindings, replacing the
kernel::c_str!() macro (available since Rust 1.77)
- Fix typo in d_walk_ret enum comment, add porting notes for the
readlink_copy() calling convention change"
* tag 'vfs-7.0-rc1.misc.2' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
fs: add porting notes about readlink_copy()
pidfs: return -EREMOTE when PIDFD_GET_INFO is called on another ns
nfsd: do not allow exporting of special kernel filesystems
exportfs: clarify the documentation of open()/permission() expotrfs ops
fsverity: add tracepoints
fs: add FS_XFLAG_VERITY for fs-verity files
rust: seq_file: replace `kernel::c_str!` with C-Strings
fs: dcache: fix typo in enum d_walk_ret comment
ovl: use name_is_dot* helpers in readdir code
fs: add helpers name_is_dot{,dot,_dotdot}
ovl: Fix uninit-value in ovl_fill_real
fs: reset read-only fsflags together with xflags
fs/file: optimize close_range() complexity from O(N) to O(Sparse)
This commit is contained in:
commit
45a43ac5ac
26 changed files with 274 additions and 57 deletions
|
|
@ -341,6 +341,22 @@ the file has fs-verity enabled. This can perform better than
|
|||
FS_IOC_GETFLAGS and FS_IOC_MEASURE_VERITY because it doesn't require
|
||||
opening the file, and opening verity files can be expensive.
|
||||
|
||||
FS_IOC_FSGETXATTR
|
||||
-----------------
|
||||
|
||||
Since Linux v7.0, the FS_IOC_FSGETXATTR ioctl sets FS_XFLAG_VERITY (0x00020000)
|
||||
in the returned flags when the file has verity enabled. Note that this attribute
|
||||
cannot be set with FS_IOC_FSSETXATTR as enabling verity requires input
|
||||
parameters. See FS_IOC_ENABLE_VERITY.
|
||||
|
||||
file_getattr
|
||||
------------
|
||||
|
||||
Since Linux v7.0, the file_getattr() syscall sets FS_XFLAG_VERITY (0x00020000)
|
||||
in the returned flags when the file has verity enabled. Note that this attribute
|
||||
cannot be set with file_setattr() as enabling verity requires input parameters.
|
||||
See FS_IOC_ENABLE_VERITY.
|
||||
|
||||
.. _accessing_verity_files:
|
||||
|
||||
Accessing verity files
|
||||
|
|
|
|||
|
|
@ -1351,3 +1351,13 @@ and do_rmdir()) are gone; they are replaced with non-consuming analogues
|
|||
(filename_renameat2(), etc.)
|
||||
Callers are adjusted - responsibility for dropping the filenames belongs
|
||||
to them now.
|
||||
|
||||
---
|
||||
|
||||
**mandatory**
|
||||
|
||||
readlink_copy() now requires link length as the 4th argument. Said length needs
|
||||
to match what strlen() would return if it was ran on the string.
|
||||
|
||||
However, if the string is freely accessible for the duration of inode's
|
||||
lifetime, consider using inode_set_cached_link() instead.
|
||||
|
|
|
|||
|
|
@ -10415,6 +10415,7 @@ T: git https://git.kernel.org/pub/scm/fs/fsverity/linux.git
|
|||
F: Documentation/filesystems/fsverity.rst
|
||||
F: fs/verity/
|
||||
F: include/linux/fsverity.h
|
||||
F: include/trace/events/fsverity.h
|
||||
F: include/uapi/linux/fsverity.h
|
||||
|
||||
FT260 FTDI USB-HID TO I2C BRIDGE DRIVER
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ struct fscrypt_nokey_name {
|
|||
|
||||
static inline bool fscrypt_is_dot_dotdot(const struct qstr *str)
|
||||
{
|
||||
return is_dot_dotdot(str->name, str->len);
|
||||
return name_is_dot_dotdot(str->name, str->len);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
10
fs/dcache.c
10
fs/dcache.c
|
|
@ -1298,8 +1298,8 @@ void shrink_dcache_sb(struct super_block *sb)
|
|||
EXPORT_SYMBOL(shrink_dcache_sb);
|
||||
|
||||
/**
|
||||
* enum d_walk_ret - action to talke during tree walk
|
||||
* @D_WALK_CONTINUE: contrinue walk
|
||||
* enum d_walk_ret - action to take during tree walk
|
||||
* @D_WALK_CONTINUE: continue walk
|
||||
* @D_WALK_QUIT: quit walk
|
||||
* @D_WALK_NORETRY: quit when retry is needed
|
||||
* @D_WALK_SKIP: skip this dentry and its children
|
||||
|
|
@ -1722,7 +1722,7 @@ void d_invalidate(struct dentry *dentry)
|
|||
EXPORT_SYMBOL(d_invalidate);
|
||||
|
||||
/**
|
||||
* __d_alloc - allocate a dcache entry
|
||||
* __d_alloc - allocate a dcache entry
|
||||
* @sb: filesystem it will belong to
|
||||
* @name: qstr of the name
|
||||
*
|
||||
|
|
@ -1806,7 +1806,7 @@ static struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
|
|||
}
|
||||
|
||||
/**
|
||||
* d_alloc - allocate a dcache entry
|
||||
* d_alloc - allocate a dcache entry
|
||||
* @parent: parent of entry to allocate
|
||||
* @name: qstr of the name
|
||||
*
|
||||
|
|
@ -2546,7 +2546,7 @@ static void __d_rehash(struct dentry *entry)
|
|||
}
|
||||
|
||||
/**
|
||||
* d_rehash - add an entry back to the hash
|
||||
* d_rehash - add an entry back to the hash
|
||||
* @entry: dentry to add to the hash
|
||||
*
|
||||
* Adds a dentry to the hash according to its name.
|
||||
|
|
|
|||
|
|
@ -1904,7 +1904,7 @@ int ecryptfs_decode_and_decrypt_filename(char **plaintext_name,
|
|||
|
||||
if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) &&
|
||||
!(mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED)) {
|
||||
if (is_dot_dotdot(name, name_size)) {
|
||||
if (name_is_dot_dotdot(name, name_size)) {
|
||||
rc = ecryptfs_copy_filename(plaintext_name,
|
||||
plaintext_name_size,
|
||||
name, name_size);
|
||||
|
|
|
|||
|
|
@ -253,7 +253,8 @@ static bool filldir_one(struct dir_context *ctx, const char *name, int len,
|
|||
container_of(ctx, struct getdents_callback, ctx);
|
||||
|
||||
buf->sequence++;
|
||||
if (buf->ino == ino && len <= NAME_MAX && !is_dot_dotdot(name, len)) {
|
||||
if (buf->ino == ino && len <= NAME_MAX &&
|
||||
!name_is_dot_dotdot(name, len)) {
|
||||
memcpy(buf->name, name, len);
|
||||
buf->name[len] = '\0';
|
||||
buf->found = 1;
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ int f2fs_init_casefolded_name(const struct inode *dir,
|
|||
int len;
|
||||
|
||||
if (IS_CASEFOLDED(dir) &&
|
||||
!is_dot_dotdot(fname->usr_fname->name, fname->usr_fname->len)) {
|
||||
!name_is_dot_dotdot(fname->usr_fname->name, fname->usr_fname->len)) {
|
||||
buf = f2fs_kmem_cache_alloc(f2fs_cf_name_slab,
|
||||
GFP_NOFS, false, F2FS_SB(sb));
|
||||
if (!buf)
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ void f2fs_hash_filename(const struct inode *dir, struct f2fs_filename *fname)
|
|||
|
||||
WARN_ON_ONCE(!name);
|
||||
|
||||
if (is_dot_dotdot(name, len)) {
|
||||
if (name_is_dot_dotdot(name, len)) {
|
||||
fname->hash = 0;
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
10
fs/file.c
10
fs/file.c
|
|
@ -777,23 +777,29 @@ static inline void __range_close(struct files_struct *files, unsigned int fd,
|
|||
unsigned int max_fd)
|
||||
{
|
||||
struct file *file;
|
||||
struct fdtable *fdt;
|
||||
unsigned n;
|
||||
|
||||
spin_lock(&files->file_lock);
|
||||
n = last_fd(files_fdtable(files));
|
||||
fdt = files_fdtable(files);
|
||||
n = last_fd(fdt);
|
||||
max_fd = min(max_fd, n);
|
||||
|
||||
for (; fd <= max_fd; fd++) {
|
||||
for (fd = find_next_bit(fdt->open_fds, max_fd + 1, fd);
|
||||
fd <= max_fd;
|
||||
fd = find_next_bit(fdt->open_fds, max_fd + 1, fd + 1)) {
|
||||
file = file_close_fd_locked(files, fd);
|
||||
if (file) {
|
||||
spin_unlock(&files->file_lock);
|
||||
filp_close(file, files);
|
||||
cond_resched();
|
||||
spin_lock(&files->file_lock);
|
||||
fdt = files_fdtable(files);
|
||||
} else if (need_resched()) {
|
||||
spin_unlock(&files->file_lock);
|
||||
cond_resched();
|
||||
spin_lock(&files->file_lock);
|
||||
fdt = files_fdtable(files);
|
||||
}
|
||||
}
|
||||
spin_unlock(&files->file_lock);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ void fileattr_fill_xflags(struct file_kattr *fa, u32 xflags)
|
|||
fa->flags |= FS_DAX_FL;
|
||||
if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
|
||||
fa->flags |= FS_PROJINHERIT_FL;
|
||||
if (fa->fsx_xflags & FS_XFLAG_VERITY)
|
||||
fa->flags |= FS_VERITY_FL;
|
||||
}
|
||||
EXPORT_SYMBOL(fileattr_fill_xflags);
|
||||
|
||||
|
|
@ -67,6 +69,8 @@ void fileattr_fill_flags(struct file_kattr *fa, u32 flags)
|
|||
fa->fsx_xflags |= FS_XFLAG_DAX;
|
||||
if (fa->flags & FS_PROJINHERIT_FL)
|
||||
fa->fsx_xflags |= FS_XFLAG_PROJINHERIT;
|
||||
if (fa->flags & FS_VERITY_FL)
|
||||
fa->fsx_xflags |= FS_XFLAG_VERITY;
|
||||
}
|
||||
EXPORT_SYMBOL(fileattr_fill_flags);
|
||||
|
||||
|
|
@ -142,8 +146,7 @@ static int file_attr_to_fileattr(const struct file_attr *fattr,
|
|||
if (fattr->fa_xflags & ~mask)
|
||||
return -EINVAL;
|
||||
|
||||
fileattr_fill_xflags(fa, fattr->fa_xflags);
|
||||
fa->fsx_xflags &= ~FS_XFLAG_RDONLY_MASK;
|
||||
fileattr_fill_xflags(fa, fattr->fa_xflags & ~FS_XFLAG_RDONLY_MASK);
|
||||
fa->fsx_extsize = fattr->fa_extsize;
|
||||
fa->fsx_projid = fattr->fa_projid;
|
||||
fa->fsx_cowextsize = fattr->fa_cowextsize;
|
||||
|
|
@ -163,8 +166,7 @@ static int copy_fsxattr_from_user(struct file_kattr *fa,
|
|||
if (xfa.fsx_xflags & ~mask)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
fileattr_fill_xflags(fa, xfa.fsx_xflags);
|
||||
fa->fsx_xflags &= ~FS_XFLAG_RDONLY_MASK;
|
||||
fileattr_fill_xflags(fa, xfa.fsx_xflags & ~FS_XFLAG_RDONLY_MASK);
|
||||
fa->fsx_extsize = xfa.fsx_extsize;
|
||||
fa->fsx_nextents = xfa.fsx_nextents;
|
||||
fa->fsx_projid = xfa.fsx_projid;
|
||||
|
|
|
|||
|
|
@ -3088,7 +3088,7 @@ int lookup_noperm_common(struct qstr *qname, struct dentry *base)
|
|||
if (!len)
|
||||
return -EACCES;
|
||||
|
||||
if (is_dot_dotdot(name, len))
|
||||
if (name_is_dot_dotdot(name, len))
|
||||
return -EACCES;
|
||||
|
||||
while (len--) {
|
||||
|
|
|
|||
|
|
@ -427,7 +427,8 @@ static int check_export(const struct path *path, int *flags, unsigned char *uuid
|
|||
* either a device number (so FS_REQUIRES_DEV needed)
|
||||
* or an FSID number (so NFSEXP_FSID or ->uuid is needed).
|
||||
* 2: We must be able to find an inode from a filehandle.
|
||||
* This means that s_export_op must be set.
|
||||
* This means that s_export_op must be set and comply with
|
||||
* the requirements for remote filesystem export.
|
||||
* 3: We must not currently be on an idmapped mount.
|
||||
*/
|
||||
if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) &&
|
||||
|
|
@ -437,8 +438,9 @@ static int check_export(const struct path *path, int *flags, unsigned char *uuid
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!exportfs_can_decode_fh(inode->i_sb->s_export_op)) {
|
||||
dprintk("exp_export: export of invalid fs type.\n");
|
||||
if (!exportfs_may_export(inode->i_sb->s_export_op)) {
|
||||
dprintk("exp_export: export of invalid fs type (%s).\n",
|
||||
inode->i_sb->s_type->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,8 @@ static int ovl_casefold(struct ovl_readdir_data *rdd, const char *str, int len,
|
|||
char *cf_name;
|
||||
int cf_len;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_UNICODE) || !rdd->map || is_dot_dotdot(str, len))
|
||||
if (!IS_ENABLED(CONFIG_UNICODE) || !rdd->map ||
|
||||
name_is_dot_dotdot(str, len))
|
||||
return 0;
|
||||
|
||||
cf_name = kmalloc(NAME_MAX, GFP_KERNEL);
|
||||
|
|
@ -154,7 +155,7 @@ static bool ovl_calc_d_ino(struct ovl_readdir_data *rdd,
|
|||
return true;
|
||||
|
||||
/* Always recalc d_ino for parent */
|
||||
if (strcmp(p->name, "..") == 0)
|
||||
if (name_is_dotdot(p->name, p->len))
|
||||
return true;
|
||||
|
||||
/* If this is lower, then native d_ino will do */
|
||||
|
|
@ -165,7 +166,7 @@ static bool ovl_calc_d_ino(struct ovl_readdir_data *rdd,
|
|||
* Recalc d_ino for '.' and for all entries if dir is impure (contains
|
||||
* copied up entries)
|
||||
*/
|
||||
if ((p->name[0] == '.' && p->len == 1) ||
|
||||
if (name_is_dot(p->name, p->len) ||
|
||||
ovl_test_flag(OVL_IMPURE, d_inode(rdd->dentry)))
|
||||
return true;
|
||||
|
||||
|
|
@ -561,12 +562,12 @@ static int ovl_cache_update(const struct path *path, struct ovl_cache_entry *p,
|
|||
if (!ovl_same_dev(ofs) && !p->check_xwhiteout)
|
||||
goto out;
|
||||
|
||||
if (p->name[0] == '.') {
|
||||
if (name_is_dot_dotdot(p->name, p->len)) {
|
||||
if (p->len == 1) {
|
||||
this = dget(dir);
|
||||
goto get;
|
||||
}
|
||||
if (p->len == 2 && p->name[1] == '.') {
|
||||
if (p->len == 2) {
|
||||
/* we shall not be moved */
|
||||
this = dget(dir->d_parent);
|
||||
goto get;
|
||||
|
|
@ -666,8 +667,7 @@ static int ovl_dir_read_impure(const struct path *path, struct list_head *list,
|
|||
return err;
|
||||
|
||||
list_for_each_entry_safe(p, n, list, l_node) {
|
||||
if (strcmp(p->name, ".") != 0 &&
|
||||
strcmp(p->name, "..") != 0) {
|
||||
if (!name_is_dot_dotdot(p->name, p->len)) {
|
||||
err = ovl_cache_update(path, p, true);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
@ -756,7 +756,7 @@ static bool ovl_fill_real(struct dir_context *ctx, const char *name,
|
|||
struct dir_context *orig_ctx = rdt->orig_ctx;
|
||||
bool res;
|
||||
|
||||
if (rdt->parent_ino && strcmp(name, "..") == 0) {
|
||||
if (rdt->parent_ino && name_is_dotdot(name, namelen)) {
|
||||
ino = rdt->parent_ino;
|
||||
} else if (rdt->cache) {
|
||||
struct ovl_cache_entry *p;
|
||||
|
|
@ -1098,12 +1098,8 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
|
|||
goto del_entry;
|
||||
}
|
||||
|
||||
if (p->name[0] == '.') {
|
||||
if (p->len == 1)
|
||||
goto del_entry;
|
||||
if (p->len == 2 && p->name[1] == '.')
|
||||
goto del_entry;
|
||||
}
|
||||
if (name_is_dot_dotdot(p->name, p->len))
|
||||
goto del_entry;
|
||||
err = -ENOTEMPTY;
|
||||
break;
|
||||
|
||||
|
|
@ -1147,7 +1143,7 @@ static bool ovl_check_d_type(struct dir_context *ctx, const char *name,
|
|||
container_of(ctx, struct ovl_readdir_data, ctx);
|
||||
|
||||
/* Even if d_type is not supported, DT_DIR is returned for . and .. */
|
||||
if (!strncmp(name, ".", namelen) || !strncmp(name, "..", namelen))
|
||||
if (name_is_dot_dotdot(name, namelen))
|
||||
return true;
|
||||
|
||||
if (d_type != DT_UNKNOWN)
|
||||
|
|
@ -1210,11 +1206,8 @@ static int ovl_workdir_cleanup_recurse(struct ovl_fs *ofs, const struct path *pa
|
|||
list_for_each_entry(p, &list, l_node) {
|
||||
struct dentry *dentry;
|
||||
|
||||
if (p->name[0] == '.') {
|
||||
if (p->len == 1)
|
||||
continue;
|
||||
if (p->len == 2 && p->name[1] == '.')
|
||||
continue;
|
||||
if (name_is_dot_dotdot(p->name, p->len)) {
|
||||
continue;
|
||||
} else if (incompat) {
|
||||
pr_err("overlay with incompat feature '%s' cannot be mounted\n",
|
||||
p->name);
|
||||
|
|
@ -1279,12 +1272,8 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
|
|||
goto out;
|
||||
|
||||
list_for_each_entry(p, &list, l_node) {
|
||||
if (p->name[0] == '.') {
|
||||
if (p->len == 1)
|
||||
continue;
|
||||
if (p->len == 2 && p->name[1] == '.')
|
||||
continue;
|
||||
}
|
||||
if (name_is_dot_dotdot(p->name, p->len))
|
||||
continue;
|
||||
index = ovl_lookup_upper_unlocked(ofs, p->name, indexdir, p->len);
|
||||
if (IS_ERR(index)) {
|
||||
err = PTR_ERR(index);
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
* namespace hierarchy.
|
||||
*/
|
||||
if (!pid_in_current_pidns(pid))
|
||||
return -ESRCH;
|
||||
return -EREMOTE;
|
||||
|
||||
attr = READ_ONCE(pid->attr);
|
||||
if (mask & PIDFD_INFO_EXIT) {
|
||||
|
|
|
|||
|
|
@ -1044,7 +1044,7 @@ static bool __dir_empty(struct dir_context *ctx, const char *name, int namlen,
|
|||
struct ksmbd_readdir_data *buf;
|
||||
|
||||
buf = container_of(ctx, struct ksmbd_readdir_data, ctx);
|
||||
if (!is_dot_dotdot(name, namlen))
|
||||
if (!name_is_dot_dotdot(name, namlen))
|
||||
buf->dirent_count++;
|
||||
|
||||
return !buf->dirent_count;
|
||||
|
|
|
|||
|
|
@ -223,6 +223,8 @@ static int enable_verity(struct file *filp,
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
trace_fsverity_enable(inode, ¶ms);
|
||||
|
||||
/*
|
||||
* Start enabling verity on this file, serialized by the inode lock.
|
||||
* Fail if verity is already enabled or is already being enabled.
|
||||
|
|
@ -265,6 +267,8 @@ static int enable_verity(struct file *filp,
|
|||
goto rollback;
|
||||
}
|
||||
|
||||
trace_fsverity_tree_done(inode, vi, ¶ms);
|
||||
|
||||
/*
|
||||
* Add the fsverity_info into the hash table before finishing the
|
||||
* initialization so that we don't have to undo the enabling when memory
|
||||
|
|
|
|||
|
|
@ -163,4 +163,6 @@ static inline void fsverity_init_signature(void)
|
|||
|
||||
void __init fsverity_init_workqueue(void);
|
||||
|
||||
#include <trace/events/fsverity.h>
|
||||
|
||||
#endif /* _FSVERITY_PRIVATE_H */
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
* Copyright 2019 Google LLC
|
||||
*/
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "fsverity_private.h"
|
||||
|
||||
#include <linux/ratelimit.h>
|
||||
|
|
|
|||
|
|
@ -177,6 +177,9 @@ static bool verify_data_block(struct fsverity_info *vi,
|
|||
/* Byte offset of the wanted hash relative to @addr */
|
||||
unsigned int hoffset;
|
||||
} hblocks[FS_VERITY_MAX_LEVELS];
|
||||
|
||||
trace_fsverity_verify_data_block(inode, params, data_pos);
|
||||
|
||||
/*
|
||||
* The index of the previous level's block within that level; also the
|
||||
* index of that block's hash within the current level.
|
||||
|
|
@ -255,6 +258,9 @@ static bool verify_data_block(struct fsverity_info *vi,
|
|||
want_hash = _want_hash;
|
||||
kunmap_local(haddr);
|
||||
put_page(hpage);
|
||||
trace_fsverity_merkle_hit(inode, data_pos, hblock_idx,
|
||||
level,
|
||||
hoffset >> params->log_digestsize);
|
||||
goto descend;
|
||||
}
|
||||
hblocks[level].page = hpage;
|
||||
|
|
@ -273,6 +279,9 @@ descend:
|
|||
unsigned long hblock_idx = hblocks[level - 1].index;
|
||||
unsigned int hoffset = hblocks[level - 1].hoffset;
|
||||
|
||||
trace_fsverity_verify_merkle_block(inode, hblock_idx,
|
||||
level, hoffset >> params->log_digestsize);
|
||||
|
||||
fsverity_hash_block(params, haddr, real_hash);
|
||||
if (memcmp(want_hash, real_hash, hsize) != 0)
|
||||
goto corrupted;
|
||||
|
|
|
|||
|
|
@ -200,6 +200,10 @@ struct handle_to_path_ctx {
|
|||
* @get_parent: find the parent of a given directory
|
||||
* @commit_metadata: commit metadata changes to stable storage
|
||||
*
|
||||
* Methods for open_by_handle(2) syscall with special kernel file systems:
|
||||
* @permission: custom permission for opening a file by handle
|
||||
* @open: custom open routine for opening file by handle
|
||||
*
|
||||
* See Documentation/filesystems/nfs/exporting.rst for details on how to use
|
||||
* this interface correctly and the definition of the flags.
|
||||
*
|
||||
|
|
@ -244,10 +248,14 @@ struct handle_to_path_ctx {
|
|||
* space cannot be allocated, a %ERR_PTR should be returned.
|
||||
*
|
||||
* @permission:
|
||||
* Allow filesystems to specify a custom permission function.
|
||||
* Allow filesystems to specify a custom permission function for the
|
||||
* open_by_handle_at(2) syscall instead of the default permission check.
|
||||
* This custom permission function is not respected by nfsd.
|
||||
*
|
||||
* @open:
|
||||
* Allow filesystems to specify a custom open function.
|
||||
* Allow filesystems to specify a custom open function for the
|
||||
* open_by_handle_at(2) syscall instead of the default file_open_root().
|
||||
* This custom open function is not respected by nfsd.
|
||||
*
|
||||
* @commit_metadata:
|
||||
* @commit_metadata should commit metadata changes to stable storage.
|
||||
|
|
@ -330,6 +338,15 @@ static inline bool exportfs_can_decode_fh(const struct export_operations *nop)
|
|||
return nop && nop->fh_to_dentry;
|
||||
}
|
||||
|
||||
static inline bool exportfs_may_export(const struct export_operations *nop)
|
||||
{
|
||||
/*
|
||||
* Do not allow nfs export for filesystems with custom ->open() or
|
||||
* ->permission() ops, which nfsd does not respect (e.g. pidfs, nsfs).
|
||||
*/
|
||||
return exportfs_can_decode_fh(nop) && !nop->open && !nop->permission;
|
||||
}
|
||||
|
||||
static inline bool exportfs_can_encode_fh(const struct export_operations *nop,
|
||||
int fh_flags)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,16 +7,16 @@
|
|||
#define FS_COMMON_FL \
|
||||
(FS_SYNC_FL | FS_IMMUTABLE_FL | FS_APPEND_FL | \
|
||||
FS_NODUMP_FL | FS_NOATIME_FL | FS_DAX_FL | \
|
||||
FS_PROJINHERIT_FL)
|
||||
FS_PROJINHERIT_FL | FS_VERITY_FL)
|
||||
|
||||
#define FS_XFLAG_COMMON \
|
||||
(FS_XFLAG_SYNC | FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND | \
|
||||
FS_XFLAG_NODUMP | FS_XFLAG_NOATIME | FS_XFLAG_DAX | \
|
||||
FS_XFLAG_PROJINHERIT)
|
||||
FS_XFLAG_PROJINHERIT | FS_XFLAG_VERITY)
|
||||
|
||||
/* Read-only inode flags */
|
||||
#define FS_XFLAG_RDONLY_MASK \
|
||||
(FS_XFLAG_PREALLOC | FS_XFLAG_HASATTR)
|
||||
(FS_XFLAG_PREALLOC | FS_XFLAG_HASATTR | FS_XFLAG_VERITY)
|
||||
|
||||
/* Flags to indicate valid value of fsx_ fields */
|
||||
#define FS_XFLAG_VALUES_MASK \
|
||||
|
|
|
|||
|
|
@ -2872,12 +2872,22 @@ u64 vfsmount_to_propagation_flags(struct vfsmount *mnt);
|
|||
|
||||
extern char *file_path(struct file *, char *, int);
|
||||
|
||||
static inline bool name_is_dot(const char *name, size_t len)
|
||||
{
|
||||
return unlikely(len == 1 && name[0] == '.');
|
||||
}
|
||||
|
||||
static inline bool name_is_dotdot(const char *name, size_t len)
|
||||
{
|
||||
return unlikely(len == 2 && name[0] == '.' && name[1] == '.');
|
||||
}
|
||||
|
||||
/**
|
||||
* is_dot_dotdot - returns true only if @name is "." or ".."
|
||||
* name_is_dot_dotdot - returns true only if @name is "." or ".."
|
||||
* @name: file name to check
|
||||
* @len: length of file name, in bytes
|
||||
*/
|
||||
static inline bool is_dot_dotdot(const char *name, size_t len)
|
||||
static inline bool name_is_dot_dotdot(const char *name, size_t len)
|
||||
{
|
||||
return len && unlikely(name[0] == '.') &&
|
||||
(len == 1 || (len == 2 && name[1] == '.'));
|
||||
|
|
|
|||
146
include/trace/events/fsverity.h
Normal file
146
include/trace/events/fsverity.h
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM fsverity
|
||||
|
||||
#if !defined(_TRACE_FSVERITY_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_FSVERITY_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
struct fsverity_descriptor;
|
||||
struct merkle_tree_params;
|
||||
struct fsverity_info;
|
||||
|
||||
TRACE_EVENT(fsverity_enable,
|
||||
TP_PROTO(const struct inode *inode,
|
||||
const struct merkle_tree_params *params),
|
||||
TP_ARGS(inode, params),
|
||||
TP_STRUCT__entry(
|
||||
__field(ino_t, ino)
|
||||
__field(u64, data_size)
|
||||
__field(u64, tree_size)
|
||||
__field(unsigned int, merkle_block)
|
||||
__field(unsigned int, num_levels)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->data_size = i_size_read(inode);
|
||||
__entry->tree_size = params->tree_size;
|
||||
__entry->merkle_block = params->block_size;
|
||||
__entry->num_levels = params->num_levels;
|
||||
),
|
||||
TP_printk("ino %lu data_size %llu tree_size %llu merkle_block %u levels %u",
|
||||
(unsigned long) __entry->ino,
|
||||
__entry->data_size,
|
||||
__entry->tree_size,
|
||||
__entry->merkle_block,
|
||||
__entry->num_levels)
|
||||
);
|
||||
|
||||
TRACE_EVENT(fsverity_tree_done,
|
||||
TP_PROTO(const struct inode *inode, const struct fsverity_info *vi,
|
||||
const struct merkle_tree_params *params),
|
||||
TP_ARGS(inode, vi, params),
|
||||
TP_STRUCT__entry(
|
||||
__field(ino_t, ino)
|
||||
__field(u64, data_size)
|
||||
__field(u64, tree_size)
|
||||
__field(unsigned int, merkle_block)
|
||||
__field(unsigned int, levels)
|
||||
__dynamic_array(u8, root_hash, params->digest_size)
|
||||
__dynamic_array(u8, file_digest, params->digest_size)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->data_size = i_size_read(inode);
|
||||
__entry->tree_size = params->tree_size;
|
||||
__entry->merkle_block = params->block_size;
|
||||
__entry->levels = params->num_levels;
|
||||
memcpy(__get_dynamic_array(root_hash), vi->root_hash, __get_dynamic_array_len(root_hash));
|
||||
memcpy(__get_dynamic_array(file_digest), vi->file_digest, __get_dynamic_array_len(file_digest));
|
||||
),
|
||||
TP_printk("ino %lu data_size %llu tree_size %lld merkle_block %u levels %u root_hash %s digest %s",
|
||||
(unsigned long) __entry->ino,
|
||||
__entry->data_size,
|
||||
__entry->tree_size,
|
||||
__entry->merkle_block,
|
||||
__entry->levels,
|
||||
__print_hex_str(__get_dynamic_array(root_hash), __get_dynamic_array_len(root_hash)),
|
||||
__print_hex_str(__get_dynamic_array(file_digest), __get_dynamic_array_len(file_digest)))
|
||||
);
|
||||
|
||||
TRACE_EVENT(fsverity_verify_data_block,
|
||||
TP_PROTO(const struct inode *inode,
|
||||
const struct merkle_tree_params *params,
|
||||
u64 data_pos),
|
||||
TP_ARGS(inode, params, data_pos),
|
||||
TP_STRUCT__entry(
|
||||
__field(ino_t, ino)
|
||||
__field(u64, data_pos)
|
||||
__field(unsigned int, merkle_block)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->data_pos = data_pos;
|
||||
__entry->merkle_block = params->block_size;
|
||||
),
|
||||
TP_printk("ino %lu data_pos %llu merkle_block %u",
|
||||
(unsigned long) __entry->ino,
|
||||
__entry->data_pos,
|
||||
__entry->merkle_block)
|
||||
);
|
||||
|
||||
TRACE_EVENT(fsverity_merkle_hit,
|
||||
TP_PROTO(const struct inode *inode, u64 data_pos,
|
||||
unsigned long hblock_idx, unsigned int level,
|
||||
unsigned int hidx),
|
||||
TP_ARGS(inode, data_pos, hblock_idx, level, hidx),
|
||||
TP_STRUCT__entry(
|
||||
__field(ino_t, ino)
|
||||
__field(u64, data_pos)
|
||||
__field(unsigned long, hblock_idx)
|
||||
__field(unsigned int, level)
|
||||
__field(unsigned int, hidx)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->data_pos = data_pos;
|
||||
__entry->hblock_idx = hblock_idx;
|
||||
__entry->level = level;
|
||||
__entry->hidx = hidx;
|
||||
),
|
||||
TP_printk("ino %lu data_pos %llu hblock_idx %lu level %u hidx %u",
|
||||
(unsigned long) __entry->ino,
|
||||
__entry->data_pos,
|
||||
__entry->hblock_idx,
|
||||
__entry->level,
|
||||
__entry->hidx)
|
||||
);
|
||||
|
||||
TRACE_EVENT(fsverity_verify_merkle_block,
|
||||
TP_PROTO(const struct inode *inode, unsigned long hblock_idx,
|
||||
unsigned int level, unsigned int hidx),
|
||||
TP_ARGS(inode, hblock_idx, level, hidx),
|
||||
TP_STRUCT__entry(
|
||||
__field(ino_t, ino)
|
||||
__field(unsigned long, hblock_idx)
|
||||
__field(unsigned int, level)
|
||||
__field(unsigned int, hidx)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->ino = inode->i_ino;
|
||||
__entry->hblock_idx = hblock_idx;
|
||||
__entry->level = level;
|
||||
__entry->hidx = hidx;
|
||||
),
|
||||
TP_printk("ino %lu hblock_idx %lu level %u hidx %u",
|
||||
(unsigned long) __entry->ino,
|
||||
__entry->hblock_idx,
|
||||
__entry->level,
|
||||
__entry->hidx)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_FSVERITY_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#include <trace/define_trace.h>
|
||||
|
|
@ -253,6 +253,7 @@ struct file_attr {
|
|||
#define FS_XFLAG_FILESTREAM 0x00004000 /* use filestream allocator */
|
||||
#define FS_XFLAG_DAX 0x00008000 /* use DAX for IO */
|
||||
#define FS_XFLAG_COWEXTSIZE 0x00010000 /* CoW extent size allocator hint */
|
||||
#define FS_XFLAG_VERITY 0x00020000 /* fs-verity enabled */
|
||||
#define FS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */
|
||||
|
||||
/* the read-only stuff doesn't really belong here, but any other place is
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
//!
|
||||
//! C header: [`include/linux/seq_file.h`](srctree/include/linux/seq_file.h)
|
||||
|
||||
use crate::{bindings, c_str, fmt, str::CStrExt as _, types::NotThreadSafe, types::Opaque};
|
||||
use crate::{bindings, fmt, str::CStrExt as _, types::NotThreadSafe, types::Opaque};
|
||||
|
||||
/// A utility for generating the contents of a seq file.
|
||||
#[repr(transparent)]
|
||||
|
|
@ -36,7 +36,7 @@ impl SeqFile {
|
|||
unsafe {
|
||||
bindings::seq_printf(
|
||||
self.inner.get(),
|
||||
c_str!("%pA").as_char_ptr(),
|
||||
c"%pA".as_char_ptr(),
|
||||
core::ptr::from_ref(&args).cast::<crate::ffi::c_void>(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue