vfs-6.18-rc1.misc
-----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCaNZQMQAKCRCRxhvAZXjc omNLAQCgrwzd9sa1JTlixweu3OAxQlSEbLuMpEv7Ztm+B7Wz0AD9HtwPC44Kev03 GbMcB2DCFLC4evqYECj6IG7NBmoKsAs= =1ICf -----END PGP SIGNATURE----- Merge tag 'vfs-6.18-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull misc vfs updates from Christian Brauner: "This contains the usual selections of misc updates for this cycle. Features: - Add "initramfs_options" parameter to set initramfs mount options. This allows to add specific mount options to the rootfs to e.g., limit the memory size - Add RWF_NOSIGNAL flag for pwritev2() Add RWF_NOSIGNAL flag for pwritev2. This flag prevents the SIGPIPE signal from being raised when writing on disconnected pipes or sockets. The flag is handled directly by the pipe filesystem and converted to the existing MSG_NOSIGNAL flag for sockets - Allow to pass pid namespace as procfs mount option Ever since the introduction of pid namespaces, procfs has had very implicit behaviour surrounding them (the pidns used by a procfs mount is auto-selected based on the mounting process's active pidns, and the pidns itself is basically hidden once the mount has been constructed) This implicit behaviour has historically meant that userspace was required to do some special dances in order to configure the pidns of a procfs mount as desired. Examples include: * In order to bypass the mnt_too_revealing() check, Kubernetes creates a procfs mount from an empty pidns so that user namespaced containers can be nested (without this, the nested containers would fail to mount procfs) But this requires forking off a helper process because you cannot just one-shot this using mount(2) * Container runtimes in general need to fork into a container before configuring its mounts, which can lead to security issues in the case of shared-pidns containers (a privileged process in the pidns can interact with your container runtime process) While SUID_DUMP_DISABLE and user namespaces make this less of an issue, the strict need for this due to a minor uAPI wart is kind of unfortunate Things would be much easier if there was a way for userspace to just specify the pidns they want. So this pull request contains changes to implement a new "pidns" argument which can be set using fsconfig(2): fsconfig(procfd, FSCONFIG_SET_FD, "pidns", NULL, nsfd); fsconfig(procfd, FSCONFIG_SET_STRING, "pidns", "/proc/self/ns/pid", 0); or classic mount(2) / mount(8): // mount -t proc -o pidns=/proc/self/ns/pid proc /tmp/proc mount("proc", "/tmp/proc", "proc", MS_..., "pidns=/proc/self/ns/pid"); Cleanups: - Remove the last references to EXPORT_OP_ASYNC_LOCK - Make file_remove_privs_flags() static - Remove redundant __GFP_NOWARN when GFP_NOWAIT is used - Use try_cmpxchg() in start_dir_add() - Use try_cmpxchg() in sb_init_done_wq() - Replace offsetof() with struct_size() in ioctl_file_dedupe_range() - Remove vfs_ioctl() export - Replace rwlock() with spinlock in epoll code as rwlock causes priority inversion on preempt rt kernels - Make ns_entries in fs/proc/namespaces const - Use a switch() statement() in init_special_inode() just like we do in may_open() - Use struct_size() in dir_add() in the initramfs code - Use str_plural() in rd_load_image() - Replace strcpy() with strscpy() in find_link() - Rename generic_delete_inode() to inode_just_drop() and generic_drop_inode() to inode_generic_drop() - Remove unused arguments from fcntl_{g,s}et_rw_hint() Fixes: - Document @name parameter for name_contains_dotdot() helper - Fix spelling mistake - Always return zero from replace_fd() instead of the file descriptor number - Limit the size for copy_file_range() in compat mode to prevent a signed overflow - Fix debugfs mount options not being applied - Verify the inode mode when loading it from disk in minixfs - Verify the inode mode when loading it from disk in cramfs - Don't trigger automounts with RESOLVE_NO_XDEV If openat2() was called with RESOLVE_NO_XDEV it didn't traverse through automounts, but could still trigger them - Add FL_RECLAIM flag to show_fl_flags() macro so it appears in tracepoints - Fix unused variable warning in rd_load_image() on s390 - Make INITRAMFS_PRESERVE_MTIME depend on BLK_DEV_INITRD - Use ns_capable_noaudit() when determining net sysctl permissions - Don't call path_put() under namespace semaphore in listmount() and statmount()" * tag 'vfs-6.18-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (38 commits) fcntl: trim arguments listmount: don't call path_put() under namespace semaphore statmount: don't call path_put() under namespace semaphore pid: use ns_capable_noaudit() when determining net sysctl permissions fs: rename generic_delete_inode() and generic_drop_inode() init: INITRAMFS_PRESERVE_MTIME should depend on BLK_DEV_INITRD initramfs: Replace strcpy() with strscpy() in find_link() initrd: Use str_plural() in rd_load_image() initramfs: Use struct_size() helper to improve dir_add() initrd: Fix unused variable warning in rd_load_image() on s390 fs: use the switch statement in init_special_inode() fs/proc/namespaces: make ns_entries const filelock: add FL_RECLAIM to show_fl_flags() macro eventpoll: Replace rwlock with spinlock selftests/proc: add tests for new pidns APIs procfs: add "pidns" mount option pidns: move is-ancestor logic to helper openat2: don't trigger automounts with RESOLVE_NO_XDEV namei: move cross-device check to __traverse_mounts namei: remove LOOKUP_NO_XDEV check from handle_mounts ...
This commit is contained in:
commit
b7ce6fa90f
|
@ -6429,6 +6429,9 @@
|
|||
|
||||
rootflags= [KNL] Set root filesystem mount option string
|
||||
|
||||
initramfs_options= [KNL]
|
||||
Specify mount options for for the initramfs mount.
|
||||
|
||||
rootfstype= [KNL] Set root filesystem type
|
||||
|
||||
rootwait [KNL] Wait (indefinitely) for root device to show up.
|
||||
|
|
|
@ -340,8 +340,8 @@ of those. Caller makes sure async writeback cannot be running for the inode whil
|
|||
|
||||
->drop_inode() returns int now; it's called on final iput() with
|
||||
inode->i_lock held and it returns true if filesystems wants the inode to be
|
||||
dropped. As before, generic_drop_inode() is still the default and it's been
|
||||
updated appropriately. generic_delete_inode() is also alive and it consists
|
||||
dropped. As before, inode_generic_drop() is still the default and it's been
|
||||
updated appropriately. inode_just_drop() is also alive and it consists
|
||||
simply of return 1. Note that all actual eviction work is done by caller after
|
||||
->drop_inode() returns.
|
||||
|
||||
|
|
|
@ -2362,6 +2362,7 @@ The following mount options are supported:
|
|||
hidepid= Set /proc/<pid>/ access mode.
|
||||
gid= Set the group authorized to learn processes information.
|
||||
subset= Show only the specified subset of procfs.
|
||||
pidns= Specify a the namespace used by this procfs.
|
||||
========= ========================================================
|
||||
|
||||
hidepid=off or hidepid=0 means classic mode - everybody may access all
|
||||
|
@ -2394,6 +2395,13 @@ information about processes information, just add identd to this group.
|
|||
subset=pid hides all top level files and directories in the procfs that
|
||||
are not related to tasks.
|
||||
|
||||
pidns= specifies a pid namespace (either as a string path to something like
|
||||
`/proc/$pid/ns/pid`, or a file descriptor when using `FSCONFIG_SET_FD`) that
|
||||
will be used by the procfs instance when translating pids. By default, procfs
|
||||
will use the calling process's active pid namespace. Note that the pid
|
||||
namespace of an existing procfs instance cannot be modified (attempting to do
|
||||
so will give an `-EBUSY` error).
|
||||
|
||||
Chapter 5: Filesystem behavior
|
||||
==============================
|
||||
|
||||
|
|
|
@ -327,11 +327,11 @@ or bottom half).
|
|||
inode->i_lock spinlock held.
|
||||
|
||||
This method should be either NULL (normal UNIX filesystem
|
||||
semantics) or "generic_delete_inode" (for filesystems that do
|
||||
semantics) or "inode_just_drop" (for filesystems that do
|
||||
not want to cache inodes - causing "delete_inode" to always be
|
||||
called regardless of the value of i_nlink)
|
||||
|
||||
The "generic_delete_inode()" behavior is equivalent to the old
|
||||
The "inode_just_drop()" behavior is equivalent to the old
|
||||
practice of using "force_delete" in the put_inode() case, but
|
||||
does not have the races that the "force_delete()" approach had.
|
||||
|
||||
|
|
|
@ -412,7 +412,7 @@ static const struct super_operations bdev_sops = {
|
|||
.statfs = simple_statfs,
|
||||
.alloc_inode = bdev_alloc_inode,
|
||||
.free_inode = bdev_free_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.evict_inode = bdev_evict_inode,
|
||||
};
|
||||
|
||||
|
|
|
@ -388,7 +388,7 @@ static const struct super_operations dax_sops = {
|
|||
.alloc_inode = dax_alloc_inode,
|
||||
.destroy_inode = dax_destroy_inode,
|
||||
.free_inode = dax_free_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
};
|
||||
|
||||
static int dax_init_fs_context(struct fs_context *fc)
|
||||
|
|
|
@ -94,7 +94,7 @@ static int ibmasmfs_init_fs_context(struct fs_context *fc)
|
|||
|
||||
static const struct super_operations ibmasmfs_s_ops = {
|
||||
.statfs = simple_statfs,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
};
|
||||
|
||||
static const struct file_operations *ibmasmfs_dir_ops = &simple_dir_operations;
|
||||
|
|
|
@ -1891,7 +1891,7 @@ static struct dentry *ffs_sb_create_file(struct super_block *sb,
|
|||
/* Super block */
|
||||
static const struct super_operations ffs_sb_operations = {
|
||||
.statfs = simple_statfs,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
};
|
||||
|
||||
struct ffs_sb_fill_data {
|
||||
|
|
|
@ -2011,7 +2011,7 @@ gadgetfs_create_file (struct super_block *sb, char const *name,
|
|||
|
||||
static const struct super_operations gadget_fs_operations = {
|
||||
.statfs = simple_statfs,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
};
|
||||
|
||||
static int
|
||||
|
|
|
@ -252,7 +252,7 @@ static int v9fs_drop_inode(struct inode *inode)
|
|||
|
||||
v9ses = v9fs_inode2v9ses(inode);
|
||||
if (v9ses->cache & (CACHE_META|CACHE_LOOSE))
|
||||
return generic_drop_inode(inode);
|
||||
return inode_generic_drop(inode);
|
||||
/*
|
||||
* in case of non cached mode always drop the
|
||||
* inode because we want the inode attribute
|
||||
|
|
|
@ -723,9 +723,9 @@ int afs_drop_inode(struct inode *inode)
|
|||
_enter("");
|
||||
|
||||
if (test_bit(AFS_VNODE_PSEUDODIR, &AFS_FS_I(inode)->flags))
|
||||
return generic_delete_inode(inode);
|
||||
return inode_just_drop(inode);
|
||||
else
|
||||
return generic_drop_inode(inode);
|
||||
return inode_generic_drop(inode);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -7973,7 +7973,7 @@ int btrfs_drop_inode(struct inode *inode)
|
|||
if (btrfs_root_refs(&root->root_item) == 0)
|
||||
return 1;
|
||||
else
|
||||
return generic_drop_inode(inode);
|
||||
return inode_generic_drop(inode);
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
|
|
|
@ -1042,7 +1042,7 @@ static const struct super_operations ceph_super_ops = {
|
|||
.alloc_inode = ceph_alloc_inode,
|
||||
.free_inode = ceph_free_inode,
|
||||
.write_inode = ceph_write_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.evict_inode = ceph_evict_inode,
|
||||
.sync_fs = ceph_sync_fs,
|
||||
.put_super = ceph_put_super,
|
||||
|
|
|
@ -36,7 +36,7 @@ static void configfs_free_inode(struct inode *inode)
|
|||
|
||||
static const struct super_operations configfs_ops = {
|
||||
.statfs = simple_statfs,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.free_inode = configfs_free_inode,
|
||||
};
|
||||
|
||||
|
|
|
@ -116,9 +116,18 @@ static struct inode *get_cramfs_inode(struct super_block *sb,
|
|||
inode_nohighmem(inode);
|
||||
inode->i_data.a_ops = &cramfs_aops;
|
||||
break;
|
||||
default:
|
||||
case S_IFCHR:
|
||||
case S_IFBLK:
|
||||
case S_IFIFO:
|
||||
case S_IFSOCK:
|
||||
init_special_inode(inode, cramfs_inode->mode,
|
||||
old_decode_dev(cramfs_inode->size));
|
||||
break;
|
||||
default:
|
||||
printk(KERN_DEBUG "CRAMFS: Invalid file type 0%04o for inode %lu.\n",
|
||||
inode->i_mode, inode->i_ino);
|
||||
iget_failed(inode);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
inode->i_mode = cramfs_inode->mode;
|
||||
|
|
|
@ -2509,8 +2509,8 @@ static inline unsigned start_dir_add(struct inode *dir)
|
|||
{
|
||||
preempt_disable_nested();
|
||||
for (;;) {
|
||||
unsigned n = dir->i_dir_seq;
|
||||
if (!(n & 1) && cmpxchg(&dir->i_dir_seq, n, n + 1) == n)
|
||||
unsigned n = READ_ONCE(dir->i_dir_seq);
|
||||
if (!(n & 1) && try_cmpxchg(&dir->i_dir_seq, &n, n + 1))
|
||||
return n;
|
||||
cpu_relax();
|
||||
}
|
||||
|
|
|
@ -127,7 +127,7 @@ static int efivarfs_unfreeze_fs(struct super_block *sb);
|
|||
|
||||
static const struct super_operations efivarfs_ops = {
|
||||
.statfs = efivarfs_statfs,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.alloc_inode = efivarfs_alloc_inode,
|
||||
.free_inode = efivarfs_free_inode,
|
||||
.show_options = efivarfs_show_options,
|
||||
|
|
139
fs/eventpoll.c
139
fs/eventpoll.c
|
@ -46,10 +46,10 @@
|
|||
*
|
||||
* 1) epnested_mutex (mutex)
|
||||
* 2) ep->mtx (mutex)
|
||||
* 3) ep->lock (rwlock)
|
||||
* 3) ep->lock (spinlock)
|
||||
*
|
||||
* The acquire order is the one listed above, from 1 to 3.
|
||||
* We need a rwlock (ep->lock) because we manipulate objects
|
||||
* We need a spinlock (ep->lock) because we manipulate objects
|
||||
* from inside the poll callback, that might be triggered from
|
||||
* a wake_up() that in turn might be called from IRQ context.
|
||||
* So we can't sleep inside the poll callback and hence we need
|
||||
|
@ -195,7 +195,7 @@ struct eventpoll {
|
|||
struct list_head rdllist;
|
||||
|
||||
/* Lock which protects rdllist and ovflist */
|
||||
rwlock_t lock;
|
||||
spinlock_t lock;
|
||||
|
||||
/* RB tree root used to store monitored fd structs */
|
||||
struct rb_root_cached rbr;
|
||||
|
@ -741,10 +741,10 @@ static void ep_start_scan(struct eventpoll *ep, struct list_head *txlist)
|
|||
* in a lockless way.
|
||||
*/
|
||||
lockdep_assert_irqs_enabled();
|
||||
write_lock_irq(&ep->lock);
|
||||
spin_lock_irq(&ep->lock);
|
||||
list_splice_init(&ep->rdllist, txlist);
|
||||
WRITE_ONCE(ep->ovflist, NULL);
|
||||
write_unlock_irq(&ep->lock);
|
||||
spin_unlock_irq(&ep->lock);
|
||||
}
|
||||
|
||||
static void ep_done_scan(struct eventpoll *ep,
|
||||
|
@ -752,7 +752,7 @@ static void ep_done_scan(struct eventpoll *ep,
|
|||
{
|
||||
struct epitem *epi, *nepi;
|
||||
|
||||
write_lock_irq(&ep->lock);
|
||||
spin_lock_irq(&ep->lock);
|
||||
/*
|
||||
* During the time we spent inside the "sproc" callback, some
|
||||
* other events might have been queued by the poll callback.
|
||||
|
@ -793,7 +793,7 @@ static void ep_done_scan(struct eventpoll *ep,
|
|||
wake_up(&ep->wq);
|
||||
}
|
||||
|
||||
write_unlock_irq(&ep->lock);
|
||||
spin_unlock_irq(&ep->lock);
|
||||
}
|
||||
|
||||
static void ep_get(struct eventpoll *ep)
|
||||
|
@ -868,10 +868,10 @@ static bool __ep_remove(struct eventpoll *ep, struct epitem *epi, bool force)
|
|||
|
||||
rb_erase_cached(&epi->rbn, &ep->rbr);
|
||||
|
||||
write_lock_irq(&ep->lock);
|
||||
spin_lock_irq(&ep->lock);
|
||||
if (ep_is_linked(epi))
|
||||
list_del_init(&epi->rdllink);
|
||||
write_unlock_irq(&ep->lock);
|
||||
spin_unlock_irq(&ep->lock);
|
||||
|
||||
wakeup_source_unregister(ep_wakeup_source(epi));
|
||||
/*
|
||||
|
@ -1152,7 +1152,7 @@ static int ep_alloc(struct eventpoll **pep)
|
|||
return -ENOMEM;
|
||||
|
||||
mutex_init(&ep->mtx);
|
||||
rwlock_init(&ep->lock);
|
||||
spin_lock_init(&ep->lock);
|
||||
init_waitqueue_head(&ep->wq);
|
||||
init_waitqueue_head(&ep->poll_wait);
|
||||
INIT_LIST_HEAD(&ep->rdllist);
|
||||
|
@ -1239,100 +1239,10 @@ struct file *get_epoll_tfile_raw_ptr(struct file *file, int tfd,
|
|||
}
|
||||
#endif /* CONFIG_KCMP */
|
||||
|
||||
/*
|
||||
* Adds a new entry to the tail of the list in a lockless way, i.e.
|
||||
* multiple CPUs are allowed to call this function concurrently.
|
||||
*
|
||||
* Beware: it is necessary to prevent any other modifications of the
|
||||
* existing list until all changes are completed, in other words
|
||||
* concurrent list_add_tail_lockless() calls should be protected
|
||||
* with a read lock, where write lock acts as a barrier which
|
||||
* makes sure all list_add_tail_lockless() calls are fully
|
||||
* completed.
|
||||
*
|
||||
* Also an element can be locklessly added to the list only in one
|
||||
* direction i.e. either to the tail or to the head, otherwise
|
||||
* concurrent access will corrupt the list.
|
||||
*
|
||||
* Return: %false if element has been already added to the list, %true
|
||||
* otherwise.
|
||||
*/
|
||||
static inline bool list_add_tail_lockless(struct list_head *new,
|
||||
struct list_head *head)
|
||||
{
|
||||
struct list_head *prev;
|
||||
|
||||
/*
|
||||
* This is simple 'new->next = head' operation, but cmpxchg()
|
||||
* is used in order to detect that same element has been just
|
||||
* added to the list from another CPU: the winner observes
|
||||
* new->next == new.
|
||||
*/
|
||||
if (!try_cmpxchg(&new->next, &new, head))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Initially ->next of a new element must be updated with the head
|
||||
* (we are inserting to the tail) and only then pointers are atomically
|
||||
* exchanged. XCHG guarantees memory ordering, thus ->next should be
|
||||
* updated before pointers are actually swapped and pointers are
|
||||
* swapped before prev->next is updated.
|
||||
*/
|
||||
|
||||
prev = xchg(&head->prev, new);
|
||||
|
||||
/*
|
||||
* It is safe to modify prev->next and new->prev, because a new element
|
||||
* is added only to the tail and new->next is updated before XCHG.
|
||||
*/
|
||||
|
||||
prev->next = new;
|
||||
new->prev = prev;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Chains a new epi entry to the tail of the ep->ovflist in a lockless way,
|
||||
* i.e. multiple CPUs are allowed to call this function concurrently.
|
||||
*
|
||||
* Return: %false if epi element has been already chained, %true otherwise.
|
||||
*/
|
||||
static inline bool chain_epi_lockless(struct epitem *epi)
|
||||
{
|
||||
struct eventpoll *ep = epi->ep;
|
||||
|
||||
/* Fast preliminary check */
|
||||
if (epi->next != EP_UNACTIVE_PTR)
|
||||
return false;
|
||||
|
||||
/* Check that the same epi has not been just chained from another CPU */
|
||||
if (cmpxchg(&epi->next, EP_UNACTIVE_PTR, NULL) != EP_UNACTIVE_PTR)
|
||||
return false;
|
||||
|
||||
/* Atomically exchange tail */
|
||||
epi->next = xchg(&ep->ovflist, epi);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the callback that is passed to the wait queue wakeup
|
||||
* mechanism. It is called by the stored file descriptors when they
|
||||
* have events to report.
|
||||
*
|
||||
* This callback takes a read lock in order not to contend with concurrent
|
||||
* events from another file descriptor, thus all modifications to ->rdllist
|
||||
* or ->ovflist are lockless. Read lock is paired with the write lock from
|
||||
* ep_start/done_scan(), which stops all list modifications and guarantees
|
||||
* that lists state is seen correctly.
|
||||
*
|
||||
* Another thing worth to mention is that ep_poll_callback() can be called
|
||||
* concurrently for the same @epi from different CPUs if poll table was inited
|
||||
* with several wait queues entries. Plural wakeup from different CPUs of a
|
||||
* single wait queue is serialized by wq.lock, but the case when multiple wait
|
||||
* queues are used should be detected accordingly. This is detected using
|
||||
* cmpxchg() operation.
|
||||
*/
|
||||
static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, void *key)
|
||||
{
|
||||
|
@ -1343,7 +1253,7 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
|
|||
unsigned long flags;
|
||||
int ewake = 0;
|
||||
|
||||
read_lock_irqsave(&ep->lock, flags);
|
||||
spin_lock_irqsave(&ep->lock, flags);
|
||||
|
||||
ep_set_busy_poll_napi_id(epi);
|
||||
|
||||
|
@ -1372,12 +1282,15 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
|
|||
* chained in ep->ovflist and requeued later on.
|
||||
*/
|
||||
if (READ_ONCE(ep->ovflist) != EP_UNACTIVE_PTR) {
|
||||
if (chain_epi_lockless(epi))
|
||||
if (epi->next == EP_UNACTIVE_PTR) {
|
||||
epi->next = READ_ONCE(ep->ovflist);
|
||||
WRITE_ONCE(ep->ovflist, epi);
|
||||
ep_pm_stay_awake_rcu(epi);
|
||||
}
|
||||
} else if (!ep_is_linked(epi)) {
|
||||
/* In the usual case, add event to ready list. */
|
||||
if (list_add_tail_lockless(&epi->rdllink, &ep->rdllist))
|
||||
ep_pm_stay_awake_rcu(epi);
|
||||
list_add_tail(&epi->rdllink, &ep->rdllist);
|
||||
ep_pm_stay_awake_rcu(epi);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1410,7 +1323,7 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
|
|||
pwake++;
|
||||
|
||||
out_unlock:
|
||||
read_unlock_irqrestore(&ep->lock, flags);
|
||||
spin_unlock_irqrestore(&ep->lock, flags);
|
||||
|
||||
/* We have to call this outside the lock */
|
||||
if (pwake)
|
||||
|
@ -1745,7 +1658,7 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event,
|
|||
}
|
||||
|
||||
/* We have to drop the new item inside our item list to keep track of it */
|
||||
write_lock_irq(&ep->lock);
|
||||
spin_lock_irq(&ep->lock);
|
||||
|
||||
/* record NAPI ID of new item if present */
|
||||
ep_set_busy_poll_napi_id(epi);
|
||||
|
@ -1762,7 +1675,7 @@ static int ep_insert(struct eventpoll *ep, const struct epoll_event *event,
|
|||
pwake++;
|
||||
}
|
||||
|
||||
write_unlock_irq(&ep->lock);
|
||||
spin_unlock_irq(&ep->lock);
|
||||
|
||||
/* We have to call this outside the lock */
|
||||
if (pwake)
|
||||
|
@ -1826,7 +1739,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi,
|
|||
* list, push it inside.
|
||||
*/
|
||||
if (ep_item_poll(epi, &pt, 1)) {
|
||||
write_lock_irq(&ep->lock);
|
||||
spin_lock_irq(&ep->lock);
|
||||
if (!ep_is_linked(epi)) {
|
||||
list_add_tail(&epi->rdllink, &ep->rdllist);
|
||||
ep_pm_stay_awake(epi);
|
||||
|
@ -1837,7 +1750,7 @@ static int ep_modify(struct eventpoll *ep, struct epitem *epi,
|
|||
if (waitqueue_active(&ep->poll_wait))
|
||||
pwake++;
|
||||
}
|
||||
write_unlock_irq(&ep->lock);
|
||||
spin_unlock_irq(&ep->lock);
|
||||
}
|
||||
|
||||
/* We have to call this outside the lock */
|
||||
|
@ -2089,7 +2002,7 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
|
|||
init_wait(&wait);
|
||||
wait.func = ep_autoremove_wake_function;
|
||||
|
||||
write_lock_irq(&ep->lock);
|
||||
spin_lock_irq(&ep->lock);
|
||||
/*
|
||||
* Barrierless variant, waitqueue_active() is called under
|
||||
* the same lock on wakeup ep_poll_callback() side, so it
|
||||
|
@ -2108,7 +2021,7 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
|
|||
if (!eavail)
|
||||
__add_wait_queue_exclusive(&ep->wq, &wait);
|
||||
|
||||
write_unlock_irq(&ep->lock);
|
||||
spin_unlock_irq(&ep->lock);
|
||||
|
||||
if (!eavail)
|
||||
timed_out = !ep_schedule_timeout(to) ||
|
||||
|
@ -2124,7 +2037,7 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
|
|||
eavail = 1;
|
||||
|
||||
if (!list_empty_careful(&wait.entry)) {
|
||||
write_lock_irq(&ep->lock);
|
||||
spin_lock_irq(&ep->lock);
|
||||
/*
|
||||
* If the thread timed out and is not on the wait queue,
|
||||
* it means that the thread was woken up after its
|
||||
|
@ -2135,7 +2048,7 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
|
|||
if (timed_out)
|
||||
eavail = list_empty(&wait.entry);
|
||||
__remove_wait_queue(&ep->wq, &wait);
|
||||
write_unlock_irq(&ep->lock);
|
||||
spin_unlock_irq(&ep->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1417,7 +1417,7 @@ static struct inode *ext4_alloc_inode(struct super_block *sb)
|
|||
|
||||
static int ext4_drop_inode(struct inode *inode)
|
||||
{
|
||||
int drop = generic_drop_inode(inode);
|
||||
int drop = inode_generic_drop(inode);
|
||||
|
||||
if (!drop)
|
||||
drop = fscrypt_drop_inode(inode);
|
||||
|
|
|
@ -1768,7 +1768,7 @@ static int f2fs_drop_inode(struct inode *inode)
|
|||
trace_f2fs_drop_inode(inode, 0);
|
||||
return 0;
|
||||
}
|
||||
ret = generic_drop_inode(inode);
|
||||
ret = inode_generic_drop(inode);
|
||||
if (!ret)
|
||||
ret = fscrypt_drop_inode(inode);
|
||||
trace_f2fs_drop_inode(inode, ret);
|
||||
|
|
10
fs/fcntl.c
10
fs/fcntl.c
|
@ -355,8 +355,7 @@ static bool rw_hint_valid(u64 hint)
|
|||
}
|
||||
}
|
||||
|
||||
static long fcntl_get_rw_hint(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
static long fcntl_get_rw_hint(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
u64 __user *argp = (u64 __user *)arg;
|
||||
|
@ -367,8 +366,7 @@ static long fcntl_get_rw_hint(struct file *file, unsigned int cmd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static long fcntl_set_rw_hint(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
static long fcntl_set_rw_hint(struct file *file, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
u64 __user *argp = (u64 __user *)arg;
|
||||
|
@ -547,10 +545,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
|||
err = memfd_fcntl(filp, cmd, argi);
|
||||
break;
|
||||
case F_GET_RW_HINT:
|
||||
err = fcntl_get_rw_hint(filp, cmd, arg);
|
||||
err = fcntl_get_rw_hint(filp, arg);
|
||||
break;
|
||||
case F_SET_RW_HINT:
|
||||
err = fcntl_set_rw_hint(filp, cmd, arg);
|
||||
err = fcntl_set_rw_hint(filp, arg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -1330,7 +1330,10 @@ int replace_fd(unsigned fd, struct file *file, unsigned flags)
|
|||
err = expand_files(files, fd);
|
||||
if (unlikely(err < 0))
|
||||
goto out_unlock;
|
||||
return do_dup2(files, file, fd, flags);
|
||||
err = do_dup2(files, file, fd, flags);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&files->file_lock);
|
||||
|
|
|
@ -1123,7 +1123,7 @@ int cgroup_writeback_by_id(u64 bdi_id, int memcg_id,
|
|||
dirty = dirty * 10 / 8;
|
||||
|
||||
/* issue the writeback work */
|
||||
work = kzalloc(sizeof(*work), GFP_NOWAIT | __GFP_NOWARN);
|
||||
work = kzalloc(sizeof(*work), GFP_NOWAIT);
|
||||
if (work) {
|
||||
work->nr_pages = dirty;
|
||||
work->sync_mode = WB_SYNC_NONE;
|
||||
|
|
|
@ -1209,7 +1209,7 @@ static const struct super_operations fuse_super_operations = {
|
|||
.free_inode = fuse_free_inode,
|
||||
.evict_inode = fuse_evict_inode,
|
||||
.write_inode = fuse_write_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.umount_begin = fuse_umount_begin,
|
||||
.statfs = fuse_statfs,
|
||||
.sync_fs = fuse_sync_fs,
|
||||
|
|
|
@ -1050,7 +1050,7 @@ static int gfs2_drop_inode(struct inode *inode)
|
|||
if (test_bit(SDF_EVICTING, &sdp->sd_flags))
|
||||
return 1;
|
||||
|
||||
return generic_drop_inode(inode);
|
||||
return inode_generic_drop(inode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -261,7 +261,7 @@ static int hostfs_show_options(struct seq_file *seq, struct dentry *root)
|
|||
static const struct super_operations hostfs_sbops = {
|
||||
.alloc_inode = hostfs_alloc_inode,
|
||||
.free_inode = hostfs_free_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.evict_inode = hostfs_evict_inode,
|
||||
.statfs = hostfs_statfs,
|
||||
.show_options = hostfs_show_options,
|
||||
|
|
30
fs/inode.c
30
fs/inode.c
|
@ -1838,11 +1838,11 @@ int insert_inode_locked4(struct inode *inode, unsigned long hashval,
|
|||
EXPORT_SYMBOL(insert_inode_locked4);
|
||||
|
||||
|
||||
int generic_delete_inode(struct inode *inode)
|
||||
int inode_just_drop(struct inode *inode)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(generic_delete_inode);
|
||||
EXPORT_SYMBOL(inode_just_drop);
|
||||
|
||||
/*
|
||||
* Called when we're dropping the last reference
|
||||
|
@ -1866,7 +1866,7 @@ static void iput_final(struct inode *inode)
|
|||
if (op->drop_inode)
|
||||
drop = op->drop_inode(inode);
|
||||
else
|
||||
drop = generic_drop_inode(inode);
|
||||
drop = inode_generic_drop(inode);
|
||||
|
||||
if (!drop &&
|
||||
!(inode->i_state & I_DONTCACHE) &&
|
||||
|
@ -2189,7 +2189,7 @@ static int __remove_privs(struct mnt_idmap *idmap,
|
|||
return notify_change(idmap, dentry, &newattrs, NULL);
|
||||
}
|
||||
|
||||
int file_remove_privs_flags(struct file *file, unsigned int flags)
|
||||
static int file_remove_privs_flags(struct file *file, unsigned int flags)
|
||||
{
|
||||
struct dentry *dentry = file_dentry(file);
|
||||
struct inode *inode = file_inode(file);
|
||||
|
@ -2214,7 +2214,6 @@ int file_remove_privs_flags(struct file *file, unsigned int flags)
|
|||
inode_has_no_xattr(inode);
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(file_remove_privs_flags);
|
||||
|
||||
/**
|
||||
* file_remove_privs - remove special file privileges (suid, capabilities)
|
||||
|
@ -2519,21 +2518,28 @@ void __init inode_init(void)
|
|||
void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
|
||||
{
|
||||
inode->i_mode = mode;
|
||||
if (S_ISCHR(mode)) {
|
||||
switch (inode->i_mode & S_IFMT) {
|
||||
case S_IFCHR:
|
||||
inode->i_fop = &def_chr_fops;
|
||||
inode->i_rdev = rdev;
|
||||
} else if (S_ISBLK(mode)) {
|
||||
break;
|
||||
case S_IFBLK:
|
||||
if (IS_ENABLED(CONFIG_BLOCK))
|
||||
inode->i_fop = &def_blk_fops;
|
||||
inode->i_rdev = rdev;
|
||||
} else if (S_ISFIFO(mode))
|
||||
break;
|
||||
case S_IFIFO:
|
||||
inode->i_fop = &pipefifo_fops;
|
||||
else if (S_ISSOCK(mode))
|
||||
; /* leave it no_open_fops */
|
||||
else
|
||||
break;
|
||||
case S_IFSOCK:
|
||||
/* leave it no_open_fops */
|
||||
break;
|
||||
default:
|
||||
printk(KERN_DEBUG "init_special_inode: bogus i_mode (%o) for"
|
||||
" inode %s:%lu\n", mode, inode->i_sb->s_id,
|
||||
inode->i_ino);
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(init_special_inode);
|
||||
|
||||
|
@ -2914,7 +2920,7 @@ EXPORT_SYMBOL(mode_strip_sgid);
|
|||
*/
|
||||
void dump_inode(struct inode *inode, const char *reason)
|
||||
{
|
||||
pr_warn("%s encountered for inode %px", reason, inode);
|
||||
pr_warn("%s encountered for inode %px (%s)\n", reason, inode, inode->i_sb->s_type->name);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(dump_inode);
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
*
|
||||
* Returns 0 on success, -errno on error.
|
||||
*/
|
||||
int vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
static int vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int error = -ENOTTY;
|
||||
|
||||
|
@ -54,7 +54,6 @@ int vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
out:
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_ioctl);
|
||||
|
||||
static int ioctl_fibmap(struct file *filp, int __user *p)
|
||||
{
|
||||
|
@ -426,7 +425,7 @@ static int ioctl_file_dedupe_range(struct file *file,
|
|||
goto out;
|
||||
}
|
||||
|
||||
size = offsetof(struct file_dedupe_range, info[count]);
|
||||
size = struct_size(same, info, count);
|
||||
if (size > PAGE_SIZE) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
|
|
|
@ -57,7 +57,7 @@ static int kernfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||
|
||||
const struct super_operations kernfs_sops = {
|
||||
.statfs = kernfs_statfs,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.evict_inode = kernfs_evict_inode,
|
||||
|
||||
.show_options = kernfs_sop_show_options,
|
||||
|
|
|
@ -2328,8 +2328,8 @@ out:
|
|||
* To avoid blocking kernel daemons, such as lockd, that need to acquire POSIX
|
||||
* locks, the ->lock() interface may return asynchronously, before the lock has
|
||||
* been granted or denied by the underlying filesystem, if (and only if)
|
||||
* lm_grant is set. Additionally EXPORT_OP_ASYNC_LOCK in export_operations
|
||||
* flags need to be set.
|
||||
* lm_grant is set. Additionally FOP_ASYNC_LOCK in file_operations fop_flags
|
||||
* need to be set.
|
||||
*
|
||||
* Callers expecting ->lock() to return asynchronously will only use F_SETLK,
|
||||
* not F_SETLKW; they will set FL_SLEEP if (and only if) the request is for a
|
||||
|
|
|
@ -492,8 +492,14 @@ void minix_set_inode(struct inode *inode, dev_t rdev)
|
|||
inode->i_op = &minix_symlink_inode_operations;
|
||||
inode_nohighmem(inode);
|
||||
inode->i_mapping->a_ops = &minix_aops;
|
||||
} else
|
||||
} else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) ||
|
||||
S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
|
||||
init_special_inode(inode, inode->i_mode, rdev);
|
||||
} else {
|
||||
printk(KERN_DEBUG "MINIX-fs: Invalid file type 0%04o for inode %lu.\n",
|
||||
inode->i_mode, inode->i_ino);
|
||||
make_bad_inode(inode);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
22
fs/namei.c
22
fs/namei.c
|
@ -1449,6 +1449,10 @@ static int follow_automount(struct path *path, int *count, unsigned lookup_flags
|
|||
dentry->d_inode)
|
||||
return -EISDIR;
|
||||
|
||||
/* No need to trigger automounts if mountpoint crossing is disabled. */
|
||||
if (lookup_flags & LOOKUP_NO_XDEV)
|
||||
return -EXDEV;
|
||||
|
||||
if (count && (*count)++ >= MAXSYMLINKS)
|
||||
return -ELOOP;
|
||||
|
||||
|
@ -1472,6 +1476,10 @@ static int __traverse_mounts(struct path *path, unsigned flags, bool *jumped,
|
|||
/* Allow the filesystem to manage the transit without i_rwsem
|
||||
* being held. */
|
||||
if (flags & DCACHE_MANAGE_TRANSIT) {
|
||||
if (lookup_flags & LOOKUP_NO_XDEV) {
|
||||
ret = -EXDEV;
|
||||
break;
|
||||
}
|
||||
ret = path->dentry->d_op->d_manage(path, false);
|
||||
flags = smp_load_acquire(&path->dentry->d_flags);
|
||||
if (ret < 0)
|
||||
|
@ -1489,6 +1497,10 @@ static int __traverse_mounts(struct path *path, unsigned flags, bool *jumped,
|
|||
// here we know it's positive
|
||||
flags = path->dentry->d_flags;
|
||||
need_mntput = true;
|
||||
if (unlikely(lookup_flags & LOOKUP_NO_XDEV)) {
|
||||
ret = -EXDEV;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -1630,12 +1642,8 @@ static inline int handle_mounts(struct nameidata *nd, struct dentry *dentry,
|
|||
return -ECHILD;
|
||||
}
|
||||
ret = traverse_mounts(path, &jumped, &nd->total_link_count, nd->flags);
|
||||
if (jumped) {
|
||||
if (unlikely(nd->flags & LOOKUP_NO_XDEV))
|
||||
ret = -EXDEV;
|
||||
else
|
||||
nd->state |= ND_JUMPED;
|
||||
}
|
||||
if (jumped)
|
||||
nd->state |= ND_JUMPED;
|
||||
if (unlikely(ret)) {
|
||||
dput(path->dentry);
|
||||
if (path->mnt != nd->path.mnt)
|
||||
|
@ -4828,7 +4836,7 @@ int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
|
|||
return -EPERM;
|
||||
/*
|
||||
* Updating the link count will likely cause i_uid and i_gid to
|
||||
* be writen back improperly if their true value is unknown to
|
||||
* be written back improperly if their true value is unknown to
|
||||
* the vfs.
|
||||
*/
|
||||
if (HAS_UNMAPPED_ID(idmap, inode))
|
||||
|
|
106
fs/namespace.c
106
fs/namespace.c
|
@ -65,6 +65,15 @@ static int __init set_mphash_entries(char *str)
|
|||
}
|
||||
__setup("mphash_entries=", set_mphash_entries);
|
||||
|
||||
static char * __initdata initramfs_options;
|
||||
static int __init initramfs_options_setup(char *str)
|
||||
{
|
||||
initramfs_options = str;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("initramfs_options=", initramfs_options_setup);
|
||||
|
||||
static u64 event;
|
||||
static DEFINE_XARRAY_FLAGS(mnt_id_xa, XA_FLAGS_ALLOC);
|
||||
static DEFINE_IDA(mnt_group_ida);
|
||||
|
@ -5711,7 +5720,6 @@ static int grab_requested_root(struct mnt_namespace *ns, struct path *root)
|
|||
static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
|
||||
struct mnt_namespace *ns)
|
||||
{
|
||||
struct path root __free(path_put) = {};
|
||||
struct mount *m;
|
||||
int err;
|
||||
|
||||
|
@ -5723,7 +5731,7 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
|
|||
if (!s->mnt)
|
||||
return -ENOENT;
|
||||
|
||||
err = grab_requested_root(ns, &root);
|
||||
err = grab_requested_root(ns, &s->root);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -5732,7 +5740,7 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
|
|||
* mounts to show users.
|
||||
*/
|
||||
m = real_mount(s->mnt);
|
||||
if (!is_path_reachable(m, m->mnt.mnt_root, &root) &&
|
||||
if (!is_path_reachable(m, m->mnt.mnt_root, &s->root) &&
|
||||
!ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
|
@ -5740,8 +5748,6 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
s->root = root;
|
||||
|
||||
/*
|
||||
* Note that mount properties in mnt->mnt_flags, mnt->mnt_idmap
|
||||
* can change concurrently as we only hold the read-side of the
|
||||
|
@ -5963,28 +5969,40 @@ retry:
|
|||
if (!ret)
|
||||
ret = copy_statmount_to_user(ks);
|
||||
kvfree(ks->seq.buf);
|
||||
path_put(&ks->root);
|
||||
if (retry_statmount(ret, &seq_size))
|
||||
goto retry;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t do_listmount(struct mnt_namespace *ns, u64 mnt_parent_id,
|
||||
u64 last_mnt_id, u64 *mnt_ids, size_t nr_mnt_ids,
|
||||
bool reverse)
|
||||
struct klistmount {
|
||||
u64 last_mnt_id;
|
||||
u64 mnt_parent_id;
|
||||
u64 *kmnt_ids;
|
||||
u32 nr_mnt_ids;
|
||||
struct mnt_namespace *ns;
|
||||
struct path root;
|
||||
};
|
||||
|
||||
static ssize_t do_listmount(struct klistmount *kls, bool reverse)
|
||||
{
|
||||
struct path root __free(path_put) = {};
|
||||
struct mnt_namespace *ns = kls->ns;
|
||||
u64 mnt_parent_id = kls->mnt_parent_id;
|
||||
u64 last_mnt_id = kls->last_mnt_id;
|
||||
u64 *mnt_ids = kls->kmnt_ids;
|
||||
size_t nr_mnt_ids = kls->nr_mnt_ids;
|
||||
struct path orig;
|
||||
struct mount *r, *first;
|
||||
ssize_t ret;
|
||||
|
||||
rwsem_assert_held(&namespace_sem);
|
||||
|
||||
ret = grab_requested_root(ns, &root);
|
||||
ret = grab_requested_root(ns, &kls->root);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mnt_parent_id == LSMT_ROOT) {
|
||||
orig = root;
|
||||
orig = kls->root;
|
||||
} else {
|
||||
orig.mnt = lookup_mnt_in_ns(mnt_parent_id, ns);
|
||||
if (!orig.mnt)
|
||||
|
@ -5996,7 +6014,7 @@ static ssize_t do_listmount(struct mnt_namespace *ns, u64 mnt_parent_id,
|
|||
* Don't trigger audit denials. We just want to determine what
|
||||
* mounts to show users.
|
||||
*/
|
||||
if (!is_path_reachable(real_mount(orig.mnt), orig.dentry, &root) &&
|
||||
if (!is_path_reachable(real_mount(orig.mnt), orig.dentry, &kls->root) &&
|
||||
!ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
|
@ -6029,14 +6047,45 @@ static ssize_t do_listmount(struct mnt_namespace *ns, u64 mnt_parent_id,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void __free_klistmount_free(const struct klistmount *kls)
|
||||
{
|
||||
path_put(&kls->root);
|
||||
kvfree(kls->kmnt_ids);
|
||||
mnt_ns_release(kls->ns);
|
||||
}
|
||||
|
||||
static inline int prepare_klistmount(struct klistmount *kls, struct mnt_id_req *kreq,
|
||||
size_t nr_mnt_ids)
|
||||
{
|
||||
|
||||
u64 last_mnt_id = kreq->param;
|
||||
|
||||
/* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */
|
||||
if (last_mnt_id != 0 && last_mnt_id <= MNT_UNIQUE_ID_OFFSET)
|
||||
return -EINVAL;
|
||||
|
||||
kls->last_mnt_id = last_mnt_id;
|
||||
|
||||
kls->nr_mnt_ids = nr_mnt_ids;
|
||||
kls->kmnt_ids = kvmalloc_array(nr_mnt_ids, sizeof(*kls->kmnt_ids),
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
if (!kls->kmnt_ids)
|
||||
return -ENOMEM;
|
||||
|
||||
kls->ns = grab_requested_mnt_ns(kreq);
|
||||
if (!kls->ns)
|
||||
return -ENOENT;
|
||||
|
||||
kls->mnt_parent_id = kreq->mnt_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
|
||||
u64 __user *, mnt_ids, size_t, nr_mnt_ids, unsigned int, flags)
|
||||
{
|
||||
u64 *kmnt_ids __free(kvfree) = NULL;
|
||||
struct klistmount kls __free(klistmount_free) = {};
|
||||
const size_t maxcount = 1000000;
|
||||
struct mnt_namespace *ns __free(mnt_ns_release) = NULL;
|
||||
struct mnt_id_req kreq;
|
||||
u64 last_mnt_id;
|
||||
ssize_t ret;
|
||||
|
||||
if (flags & ~LISTMOUNT_REVERSE)
|
||||
|
@ -6057,22 +6106,12 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
last_mnt_id = kreq.param;
|
||||
/* The first valid unique mount id is MNT_UNIQUE_ID_OFFSET + 1. */
|
||||
if (last_mnt_id != 0 && last_mnt_id <= MNT_UNIQUE_ID_OFFSET)
|
||||
return -EINVAL;
|
||||
ret = prepare_klistmount(&kls, &kreq, nr_mnt_ids);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kmnt_ids = kvmalloc_array(nr_mnt_ids, sizeof(*kmnt_ids),
|
||||
GFP_KERNEL_ACCOUNT);
|
||||
if (!kmnt_ids)
|
||||
return -ENOMEM;
|
||||
|
||||
ns = grab_requested_mnt_ns(&kreq);
|
||||
if (!ns)
|
||||
return -ENOENT;
|
||||
|
||||
if (kreq.mnt_ns_id && (ns != current->nsproxy->mnt_ns) &&
|
||||
!ns_capable_noaudit(ns->user_ns, CAP_SYS_ADMIN))
|
||||
if (kreq.mnt_ns_id && (kls.ns != current->nsproxy->mnt_ns) &&
|
||||
!ns_capable_noaudit(kls.ns->user_ns, CAP_SYS_ADMIN))
|
||||
return -ENOENT;
|
||||
|
||||
/*
|
||||
|
@ -6080,12 +6119,11 @@ SYSCALL_DEFINE4(listmount, const struct mnt_id_req __user *, req,
|
|||
* listmount() doesn't care about any mount properties.
|
||||
*/
|
||||
scoped_guard(rwsem_read, &namespace_sem)
|
||||
ret = do_listmount(ns, kreq.mnt_id, last_mnt_id, kmnt_ids,
|
||||
nr_mnt_ids, (flags & LISTMOUNT_REVERSE));
|
||||
ret = do_listmount(&kls, (flags & LISTMOUNT_REVERSE));
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
|
||||
if (copy_to_user(mnt_ids, kmnt_ids, ret * sizeof(*mnt_ids)))
|
||||
if (copy_to_user(mnt_ids, kls.kmnt_ids, ret * sizeof(*mnt_ids)))
|
||||
return -EFAULT;
|
||||
|
||||
return ret;
|
||||
|
@ -6098,7 +6136,7 @@ static void __init init_mount_tree(void)
|
|||
struct mnt_namespace *ns;
|
||||
struct path root;
|
||||
|
||||
mnt = vfs_kern_mount(&rootfs_fs_type, 0, "rootfs", NULL);
|
||||
mnt = vfs_kern_mount(&rootfs_fs_type, 0, "rootfs", initramfs_options);
|
||||
if (IS_ERR(mnt))
|
||||
panic("Can't create rootfs");
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ u64 nfs_compat_user_ino64(u64 fileid)
|
|||
|
||||
int nfs_drop_inode(struct inode *inode)
|
||||
{
|
||||
return NFS_STALE(inode) || generic_drop_inode(inode);
|
||||
return NFS_STALE(inode) || inode_generic_drop(inode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nfs_drop_inode);
|
||||
|
||||
|
|
|
@ -547,7 +547,7 @@ static const struct super_operations dlmfs_ops = {
|
|||
.alloc_inode = dlmfs_alloc_inode,
|
||||
.free_inode = dlmfs_free_inode,
|
||||
.evict_inode = dlmfs_evict_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
};
|
||||
|
||||
static const struct inode_operations dlmfs_file_inode_operations = {
|
||||
|
|
|
@ -306,7 +306,7 @@ static const struct super_operations orangefs_s_ops = {
|
|||
.free_inode = orangefs_free_inode,
|
||||
.destroy_inode = orangefs_destroy_inode,
|
||||
.write_inode = orangefs_write_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.statfs = orangefs_statfs,
|
||||
.show_options = orangefs_show_options,
|
||||
};
|
||||
|
|
|
@ -280,7 +280,7 @@ static const struct super_operations ovl_super_operations = {
|
|||
.alloc_inode = ovl_alloc_inode,
|
||||
.free_inode = ovl_free_inode,
|
||||
.destroy_inode = ovl_destroy_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.put_super = ovl_put_super,
|
||||
.sync_fs = ovl_sync_fs,
|
||||
.statfs = ovl_statfs,
|
||||
|
|
|
@ -718,7 +718,7 @@ static void pidfs_evict_inode(struct inode *inode)
|
|||
}
|
||||
|
||||
static const struct super_operations pidfs_sops = {
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.evict_inode = pidfs_evict_inode,
|
||||
.statfs = simple_statfs,
|
||||
};
|
||||
|
|
|
@ -458,7 +458,8 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)
|
|||
mutex_lock(&pipe->mutex);
|
||||
|
||||
if (!pipe->readers) {
|
||||
send_sig(SIGPIPE, current, 0);
|
||||
if ((iocb->ki_flags & IOCB_NOSIGNAL) == 0)
|
||||
send_sig(SIGPIPE, current, 0);
|
||||
ret = -EPIPE;
|
||||
goto out;
|
||||
}
|
||||
|
@ -498,7 +499,8 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)
|
|||
|
||||
for (;;) {
|
||||
if (!pipe->readers) {
|
||||
send_sig(SIGPIPE, current, 0);
|
||||
if ((iocb->ki_flags & IOCB_NOSIGNAL) == 0)
|
||||
send_sig(SIGPIPE, current, 0);
|
||||
if (!ret)
|
||||
ret = -EPIPE;
|
||||
break;
|
||||
|
|
|
@ -187,7 +187,7 @@ static int proc_show_options(struct seq_file *seq, struct dentry *root)
|
|||
const struct super_operations proc_sops = {
|
||||
.alloc_inode = proc_alloc_inode,
|
||||
.free_inode = proc_free_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.evict_inode = proc_evict_inode,
|
||||
.statfs = simple_statfs,
|
||||
.show_options = proc_show_options,
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include "internal.h"
|
||||
|
||||
|
||||
static const struct proc_ns_operations *ns_entries[] = {
|
||||
static const struct proc_ns_operations *const ns_entries[] = {
|
||||
#ifdef CONFIG_NET_NS
|
||||
&netns_operations,
|
||||
#endif
|
||||
|
@ -117,7 +117,7 @@ static struct dentry *proc_ns_instantiate(struct dentry *dentry,
|
|||
static int proc_ns_dir_readdir(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
struct task_struct *task = get_proc_task(file_inode(file));
|
||||
const struct proc_ns_operations **entry, **last;
|
||||
const struct proc_ns_operations *const *entry, *const *last;
|
||||
|
||||
if (!task)
|
||||
return -ENOENT;
|
||||
|
@ -151,7 +151,7 @@ static struct dentry *proc_ns_dir_lookup(struct inode *dir,
|
|||
struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
struct task_struct *task = get_proc_task(dir);
|
||||
const struct proc_ns_operations **entry, **last;
|
||||
const struct proc_ns_operations *const *entry, *const *last;
|
||||
unsigned int len = dentry->d_name.len;
|
||||
struct dentry *res = ERR_PTR(-ENOENT);
|
||||
|
||||
|
|
|
@ -38,12 +38,14 @@ enum proc_param {
|
|||
Opt_gid,
|
||||
Opt_hidepid,
|
||||
Opt_subset,
|
||||
Opt_pidns,
|
||||
};
|
||||
|
||||
static const struct fs_parameter_spec proc_fs_parameters[] = {
|
||||
fsparam_u32("gid", Opt_gid),
|
||||
fsparam_u32("gid", Opt_gid),
|
||||
fsparam_string("hidepid", Opt_hidepid),
|
||||
fsparam_string("subset", Opt_subset),
|
||||
fsparam_file_or_string("pidns", Opt_pidns),
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -109,11 +111,66 @@ static int proc_parse_subset_param(struct fs_context *fc, char *value)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PID_NS
|
||||
static int proc_parse_pidns_param(struct fs_context *fc,
|
||||
struct fs_parameter *param,
|
||||
struct fs_parse_result *result)
|
||||
{
|
||||
struct proc_fs_context *ctx = fc->fs_private;
|
||||
struct pid_namespace *target, *active = task_active_pid_ns(current);
|
||||
struct ns_common *ns;
|
||||
struct file *ns_filp __free(fput) = NULL;
|
||||
|
||||
switch (param->type) {
|
||||
case fs_value_is_file:
|
||||
/* came through fsconfig, steal the file reference */
|
||||
ns_filp = no_free_ptr(param->file);
|
||||
break;
|
||||
case fs_value_is_string:
|
||||
ns_filp = filp_open(param->string, O_RDONLY, 0);
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(true);
|
||||
break;
|
||||
}
|
||||
if (!ns_filp)
|
||||
ns_filp = ERR_PTR(-EBADF);
|
||||
if (IS_ERR(ns_filp)) {
|
||||
errorfc(fc, "could not get file from pidns argument");
|
||||
return PTR_ERR(ns_filp);
|
||||
}
|
||||
|
||||
if (!proc_ns_file(ns_filp))
|
||||
return invalfc(fc, "pidns argument is not an nsfs file");
|
||||
ns = get_proc_ns(file_inode(ns_filp));
|
||||
if (ns->ops->type != CLONE_NEWPID)
|
||||
return invalfc(fc, "pidns argument is not a pidns file");
|
||||
target = container_of(ns, struct pid_namespace, ns);
|
||||
|
||||
/*
|
||||
* pidns= is shorthand for joining the pidns to get a fsopen fd, so the
|
||||
* permission model should be the same as pidns_install().
|
||||
*/
|
||||
if (!ns_capable(target->user_ns, CAP_SYS_ADMIN)) {
|
||||
errorfc(fc, "insufficient permissions to set pidns");
|
||||
return -EPERM;
|
||||
}
|
||||
if (!pidns_is_ancestor(target, active))
|
||||
return invalfc(fc, "cannot set pidns to non-descendant pidns");
|
||||
|
||||
put_pid_ns(ctx->pid_ns);
|
||||
ctx->pid_ns = get_pid_ns(target);
|
||||
put_user_ns(fc->user_ns);
|
||||
fc->user_ns = get_user_ns(ctx->pid_ns->user_ns);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PID_NS */
|
||||
|
||||
static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
{
|
||||
struct proc_fs_context *ctx = fc->fs_private;
|
||||
struct fs_parse_result result;
|
||||
int opt;
|
||||
int opt, err;
|
||||
|
||||
opt = fs_parse(fc, proc_fs_parameters, param, &result);
|
||||
if (opt < 0)
|
||||
|
@ -125,15 +182,39 @@ static int proc_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
|||
break;
|
||||
|
||||
case Opt_hidepid:
|
||||
if (proc_parse_hidepid_param(fc, param))
|
||||
return -EINVAL;
|
||||
err = proc_parse_hidepid_param(fc, param);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
|
||||
case Opt_subset:
|
||||
if (proc_parse_subset_param(fc, param->string) < 0)
|
||||
return -EINVAL;
|
||||
err = proc_parse_subset_param(fc, param->string);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
|
||||
case Opt_pidns:
|
||||
#ifdef CONFIG_PID_NS
|
||||
/*
|
||||
* We would have to RCU-protect every proc_pid_ns() or
|
||||
* proc_sb_info() access if we allowed this to be reconfigured
|
||||
* for an existing procfs instance. Luckily, procfs instances
|
||||
* are cheap to create, and mount-beneath would let you
|
||||
* atomically replace an instance even with overmounts.
|
||||
*/
|
||||
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
|
||||
errorfc(fc, "cannot reconfigure pidns for existing procfs");
|
||||
return -EBUSY;
|
||||
}
|
||||
err = proc_parse_pidns_param(fc, param, &result);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
#else
|
||||
errorfc(fc, "pidns mount flag not supported on this system");
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -154,6 +235,11 @@ static void proc_apply_options(struct proc_fs_info *fs_info,
|
|||
fs_info->hide_pid = ctx->hidepid;
|
||||
if (ctx->mask & (1 << Opt_subset))
|
||||
fs_info->pidonly = ctx->pidonly;
|
||||
if (ctx->mask & (1 << Opt_pidns) &&
|
||||
!WARN_ON_ONCE(fc->purpose == FS_CONTEXT_FOR_RECONFIGURE)) {
|
||||
put_pid_ns(fs_info->pid_ns);
|
||||
fs_info->pid_ns = get_pid_ns(ctx->pid_ns);
|
||||
}
|
||||
}
|
||||
|
||||
static int proc_fill_super(struct super_block *s, struct fs_context *fc)
|
||||
|
|
|
@ -282,7 +282,7 @@ static int pstore_reconfigure(struct fs_context *fc)
|
|||
|
||||
static const struct super_operations pstore_ops = {
|
||||
.statfs = simple_statfs,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.evict_inode = pstore_evict_inode,
|
||||
.show_options = pstore_show_options,
|
||||
};
|
||||
|
|
|
@ -215,7 +215,7 @@ static int ramfs_show_options(struct seq_file *m, struct dentry *root)
|
|||
|
||||
static const struct super_operations ramfs_ops = {
|
||||
.statfs = simple_statfs,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.show_options = ramfs_show_options,
|
||||
};
|
||||
|
||||
|
|
|
@ -1576,6 +1576,13 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
|
|||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Make sure return value doesn't overflow in 32bit compat mode. Also
|
||||
* limit the size for all cases except when calling ->copy_file_range().
|
||||
*/
|
||||
if (splice || !file_out->f_op->copy_file_range || in_compat_syscall())
|
||||
len = min_t(size_t, MAX_RW_COUNT, len);
|
||||
|
||||
file_start_write(file_out);
|
||||
|
||||
/*
|
||||
|
@ -1589,9 +1596,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
|
|||
len, flags);
|
||||
} else if (!splice && file_in->f_op->remap_file_range && samesb) {
|
||||
ret = file_in->f_op->remap_file_range(file_in, pos_in,
|
||||
file_out, pos_out,
|
||||
min_t(loff_t, MAX_RW_COUNT, len),
|
||||
REMAP_FILE_CAN_SHORTEN);
|
||||
file_out, pos_out, len, REMAP_FILE_CAN_SHORTEN);
|
||||
/* fallback to splice */
|
||||
if (ret <= 0)
|
||||
splice = true;
|
||||
|
@ -1624,8 +1629,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
|
|||
* to splicing from input file, while file_start_write() is held on
|
||||
* the output file on a different sb.
|
||||
*/
|
||||
ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out,
|
||||
min_t(size_t, len, MAX_RW_COUNT), 0);
|
||||
ret = do_splice_direct(file_in, &pos_in, file_out, &pos_out, len, 0);
|
||||
done:
|
||||
if (ret > 0) {
|
||||
fsnotify_access(file_in);
|
||||
|
|
|
@ -857,7 +857,7 @@ static int cifs_drop_inode(struct inode *inode)
|
|||
|
||||
/* no serverino => unconditional eviction */
|
||||
return !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) ||
|
||||
generic_drop_inode(inode);
|
||||
inode_generic_drop(inode);
|
||||
}
|
||||
|
||||
static const struct super_operations cifs_super_ops = {
|
||||
|
|
|
@ -2318,13 +2318,15 @@ int sb_init_dio_done_wq(struct super_block *sb)
|
|||
sb->s_id);
|
||||
if (!wq)
|
||||
return -ENOMEM;
|
||||
|
||||
old = NULL;
|
||||
/*
|
||||
* This has to be atomic as more DIOs can race to create the workqueue
|
||||
*/
|
||||
old = cmpxchg(&sb->s_dio_done_wq, NULL, wq);
|
||||
/* Someone created workqueue before us? Free ours... */
|
||||
if (old)
|
||||
if (!try_cmpxchg(&sb->s_dio_done_wq, &old, wq)) {
|
||||
/* Someone created workqueue before us? Free ours... */
|
||||
destroy_workqueue(wq);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sb_init_dio_done_wq);
|
||||
|
|
|
@ -335,7 +335,7 @@ static int ubifs_write_inode(struct inode *inode, struct writeback_control *wbc)
|
|||
|
||||
static int ubifs_drop_inode(struct inode *inode)
|
||||
{
|
||||
int drop = generic_drop_inode(inode);
|
||||
int drop = inode_generic_drop(inode);
|
||||
|
||||
if (!drop)
|
||||
drop = fscrypt_drop_inode(inode);
|
||||
|
|
|
@ -778,7 +778,7 @@ xfs_fs_drop_inode(
|
|||
return 0;
|
||||
}
|
||||
|
||||
return generic_drop_inode(inode);
|
||||
return inode_generic_drop(inode);
|
||||
}
|
||||
|
||||
STATIC void
|
||||
|
|
|
@ -357,6 +357,7 @@ struct readahead_control;
|
|||
#define IOCB_APPEND (__force int) RWF_APPEND
|
||||
#define IOCB_ATOMIC (__force int) RWF_ATOMIC
|
||||
#define IOCB_DONTCACHE (__force int) RWF_DONTCACHE
|
||||
#define IOCB_NOSIGNAL (__force int) RWF_NOSIGNAL
|
||||
|
||||
/* non-RWF related bits - start at 16 */
|
||||
#define IOCB_EVENTFD (1 << 16)
|
||||
|
@ -2053,8 +2054,6 @@ int vfs_fchown(struct file *file, uid_t user, gid_t group);
|
|||
int vfs_fchmod(struct file *file, umode_t mode);
|
||||
int vfs_utimes(const struct path *path, struct timespec64 *times);
|
||||
|
||||
int vfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
extern long compat_ptr_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
|
@ -3282,7 +3281,7 @@ static inline bool is_dot_dotdot(const char *name, size_t len)
|
|||
|
||||
/**
|
||||
* name_contains_dotdot - check if a file name contains ".." path components
|
||||
*
|
||||
* @name: File path string to check
|
||||
* Search for ".." surrounded by either '/' or start/end of string.
|
||||
*/
|
||||
static inline bool name_contains_dotdot(const char *name)
|
||||
|
@ -3314,8 +3313,8 @@ extern void address_space_init_once(struct address_space *mapping);
|
|||
extern struct inode * igrab(struct inode *);
|
||||
extern ino_t iunique(struct super_block *, ino_t);
|
||||
extern int inode_needs_sync(struct inode *inode);
|
||||
extern int generic_delete_inode(struct inode *inode);
|
||||
static inline int generic_drop_inode(struct inode *inode)
|
||||
extern int inode_just_drop(struct inode *inode);
|
||||
static inline int inode_generic_drop(struct inode *inode)
|
||||
{
|
||||
return !inode->i_nlink || inode_unhashed(inode);
|
||||
}
|
||||
|
@ -3394,7 +3393,6 @@ static inline struct inode *new_inode_pseudo(struct super_block *sb)
|
|||
extern struct inode *new_inode(struct super_block *sb);
|
||||
extern void free_inode_nonrcu(struct inode *inode);
|
||||
extern int setattr_should_drop_suidgid(struct mnt_idmap *, struct inode *);
|
||||
extern int file_remove_privs_flags(struct file *file, unsigned int flags);
|
||||
extern int file_remove_privs(struct file *);
|
||||
int setattr_should_drop_sgid(struct mnt_idmap *idmap,
|
||||
const struct inode *inode);
|
||||
|
|
|
@ -84,6 +84,9 @@ extern void zap_pid_ns_processes(struct pid_namespace *pid_ns);
|
|||
extern int reboot_pid_ns(struct pid_namespace *pid_ns, int cmd);
|
||||
extern void put_pid_ns(struct pid_namespace *ns);
|
||||
|
||||
extern bool pidns_is_ancestor(struct pid_namespace *child,
|
||||
struct pid_namespace *ancestor);
|
||||
|
||||
#else /* !CONFIG_PID_NS */
|
||||
#include <linux/err.h>
|
||||
|
||||
|
@ -118,6 +121,12 @@ static inline int reboot_pid_ns(struct pid_namespace *pid_ns, int cmd)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool pidns_is_ancestor(struct pid_namespace *child,
|
||||
struct pid_namespace *ancestor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_PID_NS */
|
||||
|
||||
extern struct pid_namespace *task_active_pid_ns(struct task_struct *tsk);
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
{ FL_SLEEP, "FL_SLEEP" }, \
|
||||
{ FL_DOWNGRADE_PENDING, "FL_DOWNGRADE_PENDING" }, \
|
||||
{ FL_UNLOCK_PENDING, "FL_UNLOCK_PENDING" }, \
|
||||
{ FL_OFDLCK, "FL_OFDLCK" })
|
||||
{ FL_OFDLCK, "FL_OFDLCK" }, \
|
||||
{ FL_RECLAIM, "FL_RECLAIM"})
|
||||
|
||||
#define show_fl_type(val) \
|
||||
__print_symbolic(val, \
|
||||
|
|
|
@ -430,10 +430,13 @@ typedef int __bitwise __kernel_rwf_t;
|
|||
/* buffered IO that drops the cache after reading or writing data */
|
||||
#define RWF_DONTCACHE ((__force __kernel_rwf_t)0x00000080)
|
||||
|
||||
/* prevent pipe and socket writes from raising SIGPIPE */
|
||||
#define RWF_NOSIGNAL ((__force __kernel_rwf_t)0x00000100)
|
||||
|
||||
/* mask of flags supported by the kernel */
|
||||
#define RWF_SUPPORTED (RWF_HIPRI | RWF_DSYNC | RWF_SYNC | RWF_NOWAIT |\
|
||||
RWF_APPEND | RWF_NOAPPEND | RWF_ATOMIC |\
|
||||
RWF_DONTCACHE)
|
||||
RWF_DONTCACHE | RWF_NOSIGNAL)
|
||||
|
||||
#define PROCFS_IOCTL_MAGIC 'f'
|
||||
|
||||
|
|
|
@ -1504,6 +1504,7 @@ config BOOT_CONFIG_EMBED_FILE
|
|||
|
||||
config INITRAMFS_PRESERVE_MTIME
|
||||
bool "Preserve cpio archive mtimes in initramfs"
|
||||
depends on BLK_DEV_INITRD
|
||||
default y
|
||||
help
|
||||
Each entry in an initramfs cpio archive carries an mtime value. When
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <uapi/linux/cramfs_fs.h>
|
||||
#include <linux/initrd.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "do_mounts.h"
|
||||
|
@ -186,14 +187,12 @@ static unsigned long nr_blocks(struct file *file)
|
|||
int __init rd_load_image(char *from)
|
||||
{
|
||||
int res = 0;
|
||||
unsigned long rd_blocks, devblocks;
|
||||
unsigned long rd_blocks, devblocks, nr_disks;
|
||||
int nblocks, i;
|
||||
char *buf = NULL;
|
||||
unsigned short rotate = 0;
|
||||
decompress_fn decompressor = NULL;
|
||||
#if !defined(CONFIG_S390)
|
||||
char rotator[4] = { '|' , '/' , '-' , '\\' };
|
||||
#endif
|
||||
|
||||
out_file = filp_open("/dev/ram", O_RDWR, 0);
|
||||
if (IS_ERR(out_file))
|
||||
|
@ -244,8 +243,9 @@ int __init rd_load_image(char *from)
|
|||
goto done;
|
||||
}
|
||||
|
||||
printk(KERN_NOTICE "RAMDISK: Loading %dKiB [%ld disk%s] into ram disk... ",
|
||||
nblocks, ((nblocks-1)/devblocks)+1, nblocks>devblocks ? "s" : "");
|
||||
nr_disks = (nblocks - 1) / devblocks + 1;
|
||||
pr_notice("RAMDISK: Loading %dKiB [%ld disk%s] into ram disk... ",
|
||||
nblocks, nr_disks, str_plural(nr_disks));
|
||||
for (i = 0; i < nblocks; i++) {
|
||||
if (i && (i % devblocks == 0)) {
|
||||
pr_cont("done disk #1.\n");
|
||||
|
@ -255,12 +255,10 @@ int __init rd_load_image(char *from)
|
|||
}
|
||||
kernel_read(in_file, buf, BLOCK_SIZE, &in_pos);
|
||||
kernel_write(out_file, buf, BLOCK_SIZE, &out_pos);
|
||||
#if !defined(CONFIG_S390)
|
||||
if (!(i % 16)) {
|
||||
if (!IS_ENABLED(CONFIG_S390) && !(i % 16)) {
|
||||
pr_cont("%c\b", rotator[rotate & 0x3]);
|
||||
rotate++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
pr_cont("done.\n");
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/init_syscalls.h>
|
||||
#include <linux/umh.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/overflow.h>
|
||||
|
||||
#include "do_mounts.h"
|
||||
#include "initramfs_internal.h"
|
||||
|
@ -108,7 +109,7 @@ static char __init *find_link(int major, int minor, int ino,
|
|||
q->minor = minor;
|
||||
q->ino = ino;
|
||||
q->mode = mode;
|
||||
strcpy(q->name, name);
|
||||
strscpy(q->name, name);
|
||||
q->next = NULL;
|
||||
*p = q;
|
||||
hardlink_seen = true;
|
||||
|
@ -152,7 +153,7 @@ static void __init dir_add(const char *name, size_t nlen, time64_t mtime)
|
|||
{
|
||||
struct dir_entry *de;
|
||||
|
||||
de = kmalloc(sizeof(struct dir_entry) + nlen, GFP_KERNEL);
|
||||
de = kmalloc(struct_size(de, name, nlen), GFP_KERNEL);
|
||||
if (!de)
|
||||
panic_show_mem("can't allocate dir_entry buffer");
|
||||
INIT_LIST_HEAD(&de->list);
|
||||
|
|
|
@ -788,7 +788,7 @@ static void bpf_free_inode(struct inode *inode)
|
|||
|
||||
const struct super_operations bpf_super_ops = {
|
||||
.statfs = simple_statfs,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.show_options = bpf_show_options,
|
||||
.free_inode = bpf_free_inode,
|
||||
};
|
||||
|
|
|
@ -680,7 +680,7 @@ static int pid_table_root_permissions(struct ctl_table_header *head,
|
|||
container_of(head->set, struct pid_namespace, set);
|
||||
int mode = table->mode;
|
||||
|
||||
if (ns_capable(pidns->user_ns, CAP_SYS_ADMIN) ||
|
||||
if (ns_capable_noaudit(pidns->user_ns, CAP_SYS_ADMIN) ||
|
||||
uid_eq(current_euid(), make_kuid(pidns->user_ns, 0)))
|
||||
mode = (mode & S_IRWXU) >> 6;
|
||||
else if (in_egroup_p(make_kgid(pidns->user_ns, 0)))
|
||||
|
|
|
@ -390,11 +390,23 @@ static void pidns_put(struct ns_common *ns)
|
|||
put_pid_ns(to_pid_ns(ns));
|
||||
}
|
||||
|
||||
bool pidns_is_ancestor(struct pid_namespace *child,
|
||||
struct pid_namespace *ancestor)
|
||||
{
|
||||
struct pid_namespace *ns;
|
||||
|
||||
if (child->level < ancestor->level)
|
||||
return false;
|
||||
for (ns = child; ns->level > ancestor->level; ns = ns->parent)
|
||||
;
|
||||
return ns == ancestor;
|
||||
}
|
||||
|
||||
static int pidns_install(struct nsset *nsset, struct ns_common *ns)
|
||||
{
|
||||
struct nsproxy *nsproxy = nsset->nsproxy;
|
||||
struct pid_namespace *active = task_active_pid_ns(current);
|
||||
struct pid_namespace *ancestor, *new = to_pid_ns(ns);
|
||||
struct pid_namespace *new = to_pid_ns(ns);
|
||||
|
||||
if (!ns_capable(new->user_ns, CAP_SYS_ADMIN) ||
|
||||
!ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
|
||||
|
@ -408,13 +420,7 @@ static int pidns_install(struct nsset *nsset, struct ns_common *ns)
|
|||
* this maintains the property that processes and their
|
||||
* children can not escape their current pid namespace.
|
||||
*/
|
||||
if (new->level < active->level)
|
||||
return -EINVAL;
|
||||
|
||||
ancestor = new;
|
||||
while (ancestor->level > active->level)
|
||||
ancestor = ancestor->parent;
|
||||
if (ancestor != active)
|
||||
if (!pidns_is_ancestor(new, active))
|
||||
return -EINVAL;
|
||||
|
||||
put_pid_ns(nsproxy->pid_ns_for_children);
|
||||
|
|
|
@ -5341,7 +5341,7 @@ static const struct super_operations shmem_ops = {
|
|||
.get_dquots = shmem_get_dquots,
|
||||
#endif
|
||||
.evict_inode = shmem_evict_inode,
|
||||
.drop_inode = generic_delete_inode,
|
||||
.drop_inode = inode_just_drop,
|
||||
.put_super = shmem_put_super,
|
||||
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||
.nr_cached_objects = shmem_unused_huge_count,
|
||||
|
|
|
@ -1176,6 +1176,9 @@ static ssize_t sock_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
if (sock->type == SOCK_SEQPACKET)
|
||||
msg.msg_flags |= MSG_EOR;
|
||||
|
||||
if (iocb->ki_flags & IOCB_NOSIGNAL)
|
||||
msg.msg_flags |= MSG_NOSIGNAL;
|
||||
|
||||
res = __sock_sendmsg(sock, &msg);
|
||||
*from = msg.msg_iter;
|
||||
return res;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
/proc-tid0
|
||||
/proc-uptime-001
|
||||
/proc-uptime-002
|
||||
/proc-pidns
|
||||
/read
|
||||
/self
|
||||
/setns-dcache
|
||||
|
|
|
@ -28,5 +28,6 @@ TEST_GEN_PROGS += setns-sysvipc
|
|||
TEST_GEN_PROGS += thread-self
|
||||
TEST_GEN_PROGS += proc-multiple-procfs
|
||||
TEST_GEN_PROGS += proc-fsconfig-hidepid
|
||||
TEST_GEN_PROGS += proc-pidns
|
||||
|
||||
include ../lib.mk
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Author: Aleksa Sarai <cyphar@cyphar.com>
|
||||
* Copyright (C) 2025 SUSE LLC.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
#include "../kselftest_harness.h"
|
||||
|
||||
#define ASSERT_ERRNO(expected, _t, seen) \
|
||||
__EXPECT(expected, #expected, \
|
||||
({__typeof__(seen) _tmp_seen = (seen); \
|
||||
_tmp_seen >= 0 ? _tmp_seen : -errno; }), #seen, _t, 1)
|
||||
|
||||
#define ASSERT_ERRNO_EQ(expected, seen) \
|
||||
ASSERT_ERRNO(expected, ==, seen)
|
||||
|
||||
#define ASSERT_SUCCESS(seen) \
|
||||
ASSERT_ERRNO(0, <=, seen)
|
||||
|
||||
static int touch(char *path)
|
||||
{
|
||||
int fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC, 0644);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
FIXTURE(ns)
|
||||
{
|
||||
int host_mntns, host_pidns;
|
||||
int dummy_pidns;
|
||||
};
|
||||
|
||||
FIXTURE_SETUP(ns)
|
||||
{
|
||||
/* Stash the old mntns. */
|
||||
self->host_mntns = open("/proc/self/ns/mnt", O_RDONLY|O_CLOEXEC);
|
||||
ASSERT_SUCCESS(self->host_mntns);
|
||||
|
||||
/* Create a new mount namespace and make it private. */
|
||||
ASSERT_SUCCESS(unshare(CLONE_NEWNS));
|
||||
ASSERT_SUCCESS(mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL));
|
||||
|
||||
/*
|
||||
* Create a proper tmpfs that we can use and will disappear once we
|
||||
* leave this mntns.
|
||||
*/
|
||||
ASSERT_SUCCESS(mount("tmpfs", "/tmp", "tmpfs", 0, NULL));
|
||||
|
||||
/*
|
||||
* Create a pidns we can use for later tests. We need to fork off a
|
||||
* child so that we get a usable nsfd that we can bind-mount and open.
|
||||
*/
|
||||
ASSERT_SUCCESS(mkdir("/tmp/dummy", 0755));
|
||||
ASSERT_SUCCESS(touch("/tmp/dummy/pidns"));
|
||||
ASSERT_SUCCESS(mkdir("/tmp/dummy/proc", 0755));
|
||||
|
||||
self->host_pidns = open("/proc/self/ns/pid", O_RDONLY|O_CLOEXEC);
|
||||
ASSERT_SUCCESS(self->host_pidns);
|
||||
ASSERT_SUCCESS(unshare(CLONE_NEWPID));
|
||||
|
||||
pid_t pid = fork();
|
||||
ASSERT_SUCCESS(pid);
|
||||
if (!pid) {
|
||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||
ASSERT_SUCCESS(mount("/proc/self/ns/pid", "/tmp/dummy/pidns", NULL, MS_BIND, NULL));
|
||||
ASSERT_SUCCESS(mount("proc", "/tmp/dummy/proc", "proc", 0, NULL));
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int wstatus;
|
||||
ASSERT_EQ(waitpid(pid, &wstatus, 0), pid);
|
||||
ASSERT_TRUE(WIFEXITED(wstatus));
|
||||
ASSERT_EQ(WEXITSTATUS(wstatus), 0);
|
||||
|
||||
ASSERT_SUCCESS(setns(self->host_pidns, CLONE_NEWPID));
|
||||
|
||||
self->dummy_pidns = open("/tmp/dummy/pidns", O_RDONLY|O_CLOEXEC);
|
||||
ASSERT_SUCCESS(self->dummy_pidns);
|
||||
}
|
||||
|
||||
FIXTURE_TEARDOWN(ns)
|
||||
{
|
||||
ASSERT_SUCCESS(setns(self->host_mntns, CLONE_NEWNS));
|
||||
ASSERT_SUCCESS(close(self->host_mntns));
|
||||
|
||||
ASSERT_SUCCESS(close(self->host_pidns));
|
||||
ASSERT_SUCCESS(close(self->dummy_pidns));
|
||||
}
|
||||
|
||||
TEST_F(ns, pidns_mount_string_path)
|
||||
{
|
||||
ASSERT_SUCCESS(mkdir("/tmp/proc-host", 0755));
|
||||
ASSERT_SUCCESS(mount("proc", "/tmp/proc-host", "proc", 0, "pidns=/proc/self/ns/pid"));
|
||||
ASSERT_SUCCESS(access("/tmp/proc-host/self/", X_OK));
|
||||
|
||||
ASSERT_SUCCESS(mkdir("/tmp/proc-dummy", 0755));
|
||||
ASSERT_SUCCESS(mount("proc", "/tmp/proc-dummy", "proc", 0, "pidns=/tmp/dummy/pidns"));
|
||||
ASSERT_ERRNO_EQ(-ENOENT, access("/tmp/proc-dummy/1/", X_OK));
|
||||
ASSERT_ERRNO_EQ(-ENOENT, access("/tmp/proc-dummy/self/", X_OK));
|
||||
}
|
||||
|
||||
TEST_F(ns, pidns_fsconfig_string_path)
|
||||
{
|
||||
int fsfd = fsopen("proc", FSOPEN_CLOEXEC);
|
||||
ASSERT_SUCCESS(fsfd);
|
||||
|
||||
ASSERT_SUCCESS(fsconfig(fsfd, FSCONFIG_SET_STRING, "pidns", "/tmp/dummy/pidns", 0));
|
||||
ASSERT_SUCCESS(fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0));
|
||||
|
||||
int mountfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
|
||||
ASSERT_SUCCESS(mountfd);
|
||||
|
||||
ASSERT_ERRNO_EQ(-ENOENT, faccessat(mountfd, "1/", X_OK, 0));
|
||||
ASSERT_ERRNO_EQ(-ENOENT, faccessat(mountfd, "self/", X_OK, 0));
|
||||
|
||||
ASSERT_SUCCESS(close(fsfd));
|
||||
ASSERT_SUCCESS(close(mountfd));
|
||||
}
|
||||
|
||||
TEST_F(ns, pidns_fsconfig_fd)
|
||||
{
|
||||
int fsfd = fsopen("proc", FSOPEN_CLOEXEC);
|
||||
ASSERT_SUCCESS(fsfd);
|
||||
|
||||
ASSERT_SUCCESS(fsconfig(fsfd, FSCONFIG_SET_FD, "pidns", NULL, self->dummy_pidns));
|
||||
ASSERT_SUCCESS(fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0));
|
||||
|
||||
int mountfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
|
||||
ASSERT_SUCCESS(mountfd);
|
||||
|
||||
ASSERT_ERRNO_EQ(-ENOENT, faccessat(mountfd, "1/", X_OK, 0));
|
||||
ASSERT_ERRNO_EQ(-ENOENT, faccessat(mountfd, "self/", X_OK, 0));
|
||||
|
||||
ASSERT_SUCCESS(close(fsfd));
|
||||
ASSERT_SUCCESS(close(mountfd));
|
||||
}
|
||||
|
||||
TEST_F(ns, pidns_reconfigure_remount)
|
||||
{
|
||||
ASSERT_SUCCESS(mkdir("/tmp/proc", 0755));
|
||||
ASSERT_SUCCESS(mount("proc", "/tmp/proc", "proc", 0, ""));
|
||||
|
||||
ASSERT_SUCCESS(access("/tmp/proc/1/", X_OK));
|
||||
ASSERT_SUCCESS(access("/tmp/proc/self/", X_OK));
|
||||
|
||||
ASSERT_ERRNO_EQ(-EBUSY, mount(NULL, "/tmp/proc", NULL, MS_REMOUNT, "pidns=/tmp/dummy/pidns"));
|
||||
|
||||
ASSERT_SUCCESS(access("/tmp/proc/1/", X_OK));
|
||||
ASSERT_SUCCESS(access("/tmp/proc/self/", X_OK));
|
||||
}
|
||||
|
||||
TEST_F(ns, pidns_reconfigure_fsconfig_string_path)
|
||||
{
|
||||
int fsfd = fsopen("proc", FSOPEN_CLOEXEC);
|
||||
ASSERT_SUCCESS(fsfd);
|
||||
|
||||
ASSERT_SUCCESS(fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0));
|
||||
|
||||
int mountfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
|
||||
ASSERT_SUCCESS(mountfd);
|
||||
|
||||
ASSERT_SUCCESS(faccessat(mountfd, "1/", X_OK, 0));
|
||||
ASSERT_SUCCESS(faccessat(mountfd, "self/", X_OK, 0));
|
||||
|
||||
ASSERT_ERRNO_EQ(-EBUSY, fsconfig(fsfd, FSCONFIG_SET_STRING, "pidns", "/tmp/dummy/pidns", 0));
|
||||
ASSERT_SUCCESS(fsconfig(fsfd, FSCONFIG_CMD_RECONFIGURE, NULL, NULL, 0)); /* noop */
|
||||
|
||||
ASSERT_SUCCESS(faccessat(mountfd, "1/", X_OK, 0));
|
||||
ASSERT_SUCCESS(faccessat(mountfd, "self/", X_OK, 0));
|
||||
|
||||
ASSERT_SUCCESS(close(fsfd));
|
||||
ASSERT_SUCCESS(close(mountfd));
|
||||
}
|
||||
|
||||
TEST_F(ns, pidns_reconfigure_fsconfig_fd)
|
||||
{
|
||||
int fsfd = fsopen("proc", FSOPEN_CLOEXEC);
|
||||
ASSERT_SUCCESS(fsfd);
|
||||
|
||||
ASSERT_SUCCESS(fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0));
|
||||
|
||||
int mountfd = fsmount(fsfd, FSMOUNT_CLOEXEC, 0);
|
||||
ASSERT_SUCCESS(mountfd);
|
||||
|
||||
ASSERT_SUCCESS(faccessat(mountfd, "1/", X_OK, 0));
|
||||
ASSERT_SUCCESS(faccessat(mountfd, "self/", X_OK, 0));
|
||||
|
||||
ASSERT_ERRNO_EQ(-EBUSY, fsconfig(fsfd, FSCONFIG_SET_FD, "pidns", NULL, self->dummy_pidns));
|
||||
ASSERT_SUCCESS(fsconfig(fsfd, FSCONFIG_CMD_RECONFIGURE, NULL, NULL, 0)); /* noop */
|
||||
|
||||
ASSERT_SUCCESS(faccessat(mountfd, "1/", X_OK, 0));
|
||||
ASSERT_SUCCESS(faccessat(mountfd, "self/", X_OK, 0));
|
||||
|
||||
ASSERT_SUCCESS(close(fsfd));
|
||||
ASSERT_SUCCESS(close(mountfd));
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
Loading…
Reference in New Issue