vfs-6.12.misc
-----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCZuQEGwAKCRCRxhvAZXjc ojIuAQC433+hBkvjvmQ7H0r5rgZSjUuCTG3bSmdU7RJmPHUHhwEA85v/NGq53f+W IhandK6t+Cf0JYpFZ3N0bT88hDYVhQQ= =9zGL -----END PGP SIGNATURE----- Merge tag 'vfs-6.12.misc' of gitolite.kernel.org:pub/scm/linux/kernel/git/vfs/vfs Pull misc vfs updates from Christian Brauner: "This contains the usual pile of misc updates: Features: - Add F_CREATED_QUERY fcntl() that allows userspace to query whether a file was actually created. Often userspace wants to know whether an O_CREATE request did actually create a file without using O_EXCL. The current logic is that to first attempts to open the file without O_CREAT | O_EXCL and if ENOENT is returned userspace tries again with both flags. If that succeeds all is well. If it now reports EEXIST it retries. That works fairly well but some corner cases make this more involved. If this operates on a dangling symlink the first openat() without O_CREAT | O_EXCL will return ENOENT but the second openat() with O_CREAT | O_EXCL will fail with EEXIST. The reason is that openat() without O_CREAT | O_EXCL follows the symlink while O_CREAT | O_EXCL doesn't for security reasons. So it's not something we can really change unless we add an explicit opt-in via O_FOLLOW which seems really ugly. All available workarounds are really nasty (fanotify, bpf lsm etc) so add a simple fcntl(). - Try an opportunistic lookup for O_CREAT. Today, when opening a file we'll typically do a fast lookup, but if O_CREAT is set, the kernel always takes the exclusive inode lock. This was likely done with the expectation that O_CREAT means that we always expect to do the create, but that's often not the case. Many programs set O_CREAT even in scenarios where the file already exists (see related F_CREATED_QUERY patch motivation above). The series contained in the pr rearranges the pathwalk-for-open code to also attempt a fast_lookup in certain O_CREAT cases. If a positive dentry is found, the inode_lock can be avoided altogether and it can stay in rcuwalk mode for the last step_into. - Expose the 64 bit mount id via name_to_handle_at() Now that we provide a unique 64-bit mount ID interface in statx(2), we can now provide a race-free way for name_to_handle_at(2) to provide a file handle and corresponding mount without needing to worry about racing with /proc/mountinfo parsing or having to open a file just to do statx(2). While this is not necessary if you are using AT_EMPTY_PATH and don't care about an extra statx(2) call, users that pass full paths into name_to_handle_at(2) need to know which mount the file handle comes from (to make sure they don't try to open_by_handle_at a file handle from a different filesystem) and switching to AT_EMPTY_PATH would require allocating a file for every name_to_handle_at(2) call - Add a per dentry expire timeout to autofs There are two fairly well known automounter map formats, the autofs format and the amd format (more or less System V and Berkley). Some time ago Linux autofs added an amd map format parser that implemented a fair amount of the amd functionality. This was done within the autofs infrastructure and some functionality wasn't implemented because it either didn't make sense or required extra kernel changes. The idea was to restrict changes to be within the existing autofs functionality as much as possible and leave changes with a wider scope to be considered later. One of these changes is implementing the amd options: 1) "unmount", expire this mount according to a timeout (same as the current autofs default). 2) "nounmount", don't expire this mount (same as setting the autofs timeout to 0 except only for this specific mount) . 3) "utimeout=<seconds>", expire this mount using the specified timeout (again same as setting the autofs timeout but only for this mount) To implement these options per-dentry expire timeouts need to be implemented for autofs indirect mounts. This is because all map keys (mounts) for autofs indirect mounts use an expire timeout stored in the autofs mount super block info. structure and all indirect mounts use the same expire timeout. Fixes: - Fix missing fput for FSCONFIG_SET_FD in autofs - Use param->file for FSCONFIG_SET_FD in coda - Delete the 'fs/netfs' proc subtreee when netfs module exits - Make sure that struct uid_gid_map fits into a single cacheline - Don't flush in-flight wb switches for superblocks without cgroup writeback - Correcting the idmapping mount example in the idmapping documentation - Fix a race between evice_inodes() and find_inode() and iput() - Refine the show_inode_state() macro definition in writeback code - Prevent dump_mapping() from accessing invalid dentry.d_name.name - Show actual source for debugfs in /proc/mounts - Annotate data-race of busy_poll_usecs in eventpoll - Don't WARN for racy path_noexec check in exec code - Handle OOM on mnt_warn_timestamp_expiry() - Fix some spelling in the iomap design documentation - Fix typo in procfs comment - Fix typo in fs/namespace.c comment Cleanups: - Add the VFS git tree to the MAINTAINERS file - Move FMODE_UNSIGNED_OFFSET to fop_flags freeing up another f_mode bit in struct file bringing us to 5 free f_mode bits - Remove the __I_DIO_WAKEUP bit from i_state flags as we can simplify the wait mechanism - Remove the unused path_put_init() helper - Replace a __u32 with u32 for s_fsnotify_mask as __u32 is uapi specific - Replace the unsigned long i_state member with a u32 i_state member in struct inode freeing up 4 bytes in struct inode. Instead of using the bit based wait apis we're now using the var event apis and using the individual bytes of the i_state member to wait on state changes - Explain how per-syscall AT_* flags should be allocated - Use in_group_or_capable() helper to simplify the posix acl mode update code - Switch to LIST_HEAD() in fsync_buffers_list() to simplify the code - Removed comment about d_rcu_to_refcount() as that function doesn't exist anymore - Add kernel documentation for lookup_fast() - Don't re-zero evenpoll fields - Remove outdated comment after close_fd() - Fix imprecise wording in comment about the pipe filesystem - Drop GFP_NOFAIL mode from alloc_page_buffers - Missing blank line warnings and struct declaration improved in file_table - Annotate struct poll_list with __counted_by() - Remove the unused read parameter in percpu-rwsem - Remove linux/prefetch.h include from direct-io code - Use kmemdup_array instead of kmemdup for multiple allocation in mnt_idmapping code - Remove unused mnt_cursor_del() declaration Performance tweaks: - Dodge smp_mb in break_lease and break_deleg in the common case - Only read fops once in fops_{get,put}() - Use RCU in ilookup() - Elide smp_mb in iversion handling in the common case - Drop one lock trip in evict()" * tag 'vfs-6.12.misc' of gitolite.kernel.org:pub/scm/linux/kernel/git/vfs/vfs: (58 commits) uidgid: make sure we fit into one cacheline proc: Fix typo in the comment fs/pipe: Correct imprecise wording in comment fhandle: expose u64 mount id to name_to_handle_at(2) uapi: explain how per-syscall AT_* flags should be allocated fs: drop GFP_NOFAIL mode from alloc_page_buffers writeback: Refine the show_inode_state() macro definition fs/inode: Prevent dump_mapping() accessing invalid dentry.d_name.name mnt_idmapping: Use kmemdup_array instead of kmemdup for multiple allocation netfs: Delete subtree of 'fs/netfs' when netfs module exits fs: use LIST_HEAD() to simplify code inode: make i_state a u32 inode: port __I_LRU_ISOLATING to var event vfs: fix race between evice_inodes() and find_inode()&iput() inode: port __I_NEW to var event inode: port __I_SYNC to var event fs: reorder i_state bits fs: add i_state helpers MAINTAINERS: add the VFS git tree fs: s/__u32/u32/ for s_fsnotify_mask ...
This commit is contained in:
commit
8f72c31f45
@ -821,7 +821,7 @@ the same idmapping to the mount. We now perform three steps:
|
|||||||
/* Map the userspace id down into a kernel id in the filesystem's idmapping. */
|
/* Map the userspace id down into a kernel id in the filesystem's idmapping. */
|
||||||
make_kuid(u0:k20000:r10000, u1000) = k21000
|
make_kuid(u0:k20000:r10000, u1000) = k21000
|
||||||
|
|
||||||
2. Verify that the caller's kernel ids can be mapped to userspace ids in the
|
3. Verify that the caller's kernel ids can be mapped to userspace ids in the
|
||||||
filesystem's idmapping::
|
filesystem's idmapping::
|
||||||
|
|
||||||
from_kuid(u0:k20000:r10000, k21000) = u1000
|
from_kuid(u0:k20000:r10000, k21000) = u1000
|
||||||
@ -854,10 +854,10 @@ The same translation algorithm works with the third example.
|
|||||||
/* Map the userspace id down into a kernel id in the filesystem's idmapping. */
|
/* Map the userspace id down into a kernel id in the filesystem's idmapping. */
|
||||||
make_kuid(u0:k0:r4294967295, u1000) = k1000
|
make_kuid(u0:k0:r4294967295, u1000) = k1000
|
||||||
|
|
||||||
2. Verify that the caller's kernel ids can be mapped to userspace ids in the
|
3. Verify that the caller's kernel ids can be mapped to userspace ids in the
|
||||||
filesystem's idmapping::
|
filesystem's idmapping::
|
||||||
|
|
||||||
from_kuid(u0:k0:r4294967295, k21000) = u1000
|
from_kuid(u0:k0:r4294967295, k1000) = u1000
|
||||||
|
|
||||||
So the ownership that lands on disk will be ``u1000``.
|
So the ownership that lands on disk will be ``u1000``.
|
||||||
|
|
||||||
@ -994,7 +994,7 @@ from above:::
|
|||||||
/* Map the userspace id down into a kernel id in the filesystem's idmapping. */
|
/* Map the userspace id down into a kernel id in the filesystem's idmapping. */
|
||||||
make_kuid(u0:k0:r4294967295, u1000) = k1000
|
make_kuid(u0:k0:r4294967295, u1000) = k1000
|
||||||
|
|
||||||
2. Verify that the caller's filesystem ids can be mapped to userspace ids in the
|
3. Verify that the caller's filesystem ids can be mapped to userspace ids in the
|
||||||
filesystem's idmapping::
|
filesystem's idmapping::
|
||||||
|
|
||||||
from_kuid(u0:k0:r4294967295, k1000) = u1000
|
from_kuid(u0:k0:r4294967295, k1000) = u1000
|
||||||
|
@ -142,9 +142,9 @@ Definitions
|
|||||||
* **pure overwrite**: A write operation that does not require any
|
* **pure overwrite**: A write operation that does not require any
|
||||||
metadata or zeroing operations to perform during either submission
|
metadata or zeroing operations to perform during either submission
|
||||||
or completion.
|
or completion.
|
||||||
This implies that the fileystem must have already allocated space
|
This implies that the filesystem must have already allocated space
|
||||||
on disk as ``IOMAP_MAPPED`` and the filesystem must not place any
|
on disk as ``IOMAP_MAPPED`` and the filesystem must not place any
|
||||||
constaints on IO alignment or size.
|
constraints on IO alignment or size.
|
||||||
The only constraints on I/O alignment are device level (minimum I/O
|
The only constraints on I/O alignment are device level (minimum I/O
|
||||||
size and alignment, typically sector size).
|
size and alignment, typically sector size).
|
||||||
|
|
||||||
@ -394,7 +394,7 @@ iomap is concerned:
|
|||||||
|
|
||||||
* The **upper** level primitive is provided by the filesystem to
|
* The **upper** level primitive is provided by the filesystem to
|
||||||
coordinate access to different iomap operations.
|
coordinate access to different iomap operations.
|
||||||
The exact primitive is specifc to the filesystem and operation,
|
The exact primitive is specific to the filesystem and operation,
|
||||||
but is often a VFS inode, pagecache invalidation, or folio lock.
|
but is often a VFS inode, pagecache invalidation, or folio lock.
|
||||||
For example, a filesystem might take ``i_rwsem`` before calling
|
For example, a filesystem might take ``i_rwsem`` before calling
|
||||||
``iomap_file_buffered_write`` and ``iomap_file_unshare`` to prevent
|
``iomap_file_buffered_write`` and ``iomap_file_unshare`` to prevent
|
||||||
|
@ -8635,6 +8635,7 @@ M: Christian Brauner <brauner@kernel.org>
|
|||||||
R: Jan Kara <jack@suse.cz>
|
R: Jan Kara <jack@suse.cz>
|
||||||
L: linux-fsdevel@vger.kernel.org
|
L: linux-fsdevel@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
T: git https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
|
||||||
F: fs/*
|
F: fs/*
|
||||||
F: include/linux/fs.h
|
F: include/linux/fs.h
|
||||||
F: include/linux/fs_types.h
|
F: include/linux/fs_types.h
|
||||||
|
@ -14,12 +14,6 @@
|
|||||||
|
|
||||||
#define MAX_BUF_SZ PAGE_SIZE
|
#define MAX_BUF_SZ PAGE_SIZE
|
||||||
|
|
||||||
static int adi_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
file->f_mode |= FMODE_UNSIGNED_OFFSET;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int read_mcd_tag(unsigned long addr)
|
static int read_mcd_tag(unsigned long addr)
|
||||||
{
|
{
|
||||||
long err;
|
long err;
|
||||||
@ -206,9 +200,9 @@ static loff_t adi_llseek(struct file *file, loff_t offset, int whence)
|
|||||||
static const struct file_operations adi_fops = {
|
static const struct file_operations adi_fops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.llseek = adi_llseek,
|
.llseek = adi_llseek,
|
||||||
.open = adi_open,
|
|
||||||
.read = adi_read,
|
.read = adi_read,
|
||||||
.write = adi_write,
|
.write = adi_write,
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct miscdevice adi_miscdev = {
|
static struct miscdevice adi_miscdev = {
|
||||||
|
@ -643,6 +643,7 @@ static const struct file_operations __maybe_unused mem_fops = {
|
|||||||
.get_unmapped_area = get_unmapped_area_mem,
|
.get_unmapped_area = get_unmapped_area_mem,
|
||||||
.mmap_capabilities = memory_mmap_capabilities,
|
.mmap_capabilities = memory_mmap_capabilities,
|
||||||
#endif
|
#endif
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct file_operations null_fops = {
|
static const struct file_operations null_fops = {
|
||||||
@ -693,7 +694,7 @@ static const struct memdev {
|
|||||||
umode_t mode;
|
umode_t mode;
|
||||||
} devlist[] = {
|
} devlist[] = {
|
||||||
#ifdef CONFIG_DEVMEM
|
#ifdef CONFIG_DEVMEM
|
||||||
[DEVMEM_MINOR] = { "mem", &mem_fops, FMODE_UNSIGNED_OFFSET, 0 },
|
[DEVMEM_MINOR] = { "mem", &mem_fops, 0, 0 },
|
||||||
#endif
|
#endif
|
||||||
[3] = { "null", &null_fops, FMODE_NOWAIT, 0666 },
|
[3] = { "null", &null_fops, FMODE_NOWAIT, 0666 },
|
||||||
#ifdef CONFIG_DEVPORT
|
#ifdef CONFIG_DEVPORT
|
||||||
|
@ -2908,6 +2908,7 @@ static const struct file_operations amdgpu_driver_kms_fops = {
|
|||||||
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
||||||
.show_fdinfo = drm_show_fdinfo,
|
.show_fdinfo = drm_show_fdinfo,
|
||||||
#endif
|
#endif
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
int amdgpu_file_to_fpriv(struct file *filp, struct amdgpu_fpriv **fpriv)
|
int amdgpu_file_to_fpriv(struct file *filp, struct amdgpu_fpriv **fpriv)
|
||||||
|
@ -318,6 +318,8 @@ int drm_open_helper(struct file *filp, struct drm_minor *minor)
|
|||||||
if (dev->switch_power_state != DRM_SWITCH_POWER_ON &&
|
if (dev->switch_power_state != DRM_SWITCH_POWER_ON &&
|
||||||
dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF)
|
dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
if (WARN_ON_ONCE(!(filp->f_op->fop_flags & FOP_UNSIGNED_OFFSET)))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
drm_dbg_core(dev, "comm=\"%s\", pid=%d, minor=%d\n",
|
drm_dbg_core(dev, "comm=\"%s\", pid=%d, minor=%d\n",
|
||||||
current->comm, task_pid_nr(current), minor->index);
|
current->comm, task_pid_nr(current), minor->index);
|
||||||
@ -335,7 +337,6 @@ int drm_open_helper(struct file *filp, struct drm_minor *minor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
filp->private_data = priv;
|
filp->private_data = priv;
|
||||||
filp->f_mode |= FMODE_UNSIGNED_OFFSET;
|
|
||||||
priv->filp = filp;
|
priv->filp = filp;
|
||||||
|
|
||||||
mutex_lock(&dev->filelist_mutex);
|
mutex_lock(&dev->filelist_mutex);
|
||||||
|
@ -498,6 +498,7 @@ static const struct file_operations psb_gem_fops = {
|
|||||||
.mmap = drm_gem_mmap,
|
.mmap = drm_gem_mmap,
|
||||||
.poll = drm_poll,
|
.poll = drm_poll,
|
||||||
.read = drm_read,
|
.read = drm_read,
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct drm_driver driver = {
|
static const struct drm_driver driver = {
|
||||||
|
@ -1671,6 +1671,7 @@ static const struct file_operations i915_driver_fops = {
|
|||||||
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
||||||
.show_fdinfo = drm_show_fdinfo,
|
.show_fdinfo = drm_show_fdinfo,
|
||||||
#endif
|
#endif
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -1274,6 +1274,7 @@ nouveau_driver_fops = {
|
|||||||
.compat_ioctl = nouveau_compat_ioctl,
|
.compat_ioctl = nouveau_compat_ioctl,
|
||||||
#endif
|
#endif
|
||||||
.llseek = noop_llseek,
|
.llseek = noop_llseek,
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct drm_driver
|
static struct drm_driver
|
||||||
|
@ -520,6 +520,7 @@ static const struct file_operations radeon_driver_kms_fops = {
|
|||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
.compat_ioctl = radeon_kms_compat_ioctl,
|
.compat_ioctl = radeon_kms_compat_ioctl,
|
||||||
#endif
|
#endif
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct drm_ioctl_desc radeon_ioctls_kms[] = {
|
static const struct drm_ioctl_desc radeon_ioctls_kms[] = {
|
||||||
|
@ -801,6 +801,7 @@ static const struct file_operations tegra_drm_fops = {
|
|||||||
.read = drm_read,
|
.read = drm_read,
|
||||||
.compat_ioctl = drm_compat_ioctl,
|
.compat_ioctl = drm_compat_ioctl,
|
||||||
.llseek = noop_llseek,
|
.llseek = noop_llseek,
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int tegra_drm_context_cleanup(int id, void *p, void *data)
|
static int tegra_drm_context_cleanup(int id, void *p, void *data)
|
||||||
|
@ -1609,6 +1609,7 @@ static const struct file_operations vmwgfx_driver_fops = {
|
|||||||
.compat_ioctl = vmw_compat_ioctl,
|
.compat_ioctl = vmw_compat_ioctl,
|
||||||
#endif
|
#endif
|
||||||
.llseek = noop_llseek,
|
.llseek = noop_llseek,
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct drm_driver driver = {
|
static const struct drm_driver driver = {
|
||||||
|
@ -241,6 +241,7 @@ static const struct file_operations xe_driver_fops = {
|
|||||||
#ifdef CONFIG_PROC_FS
|
#ifdef CONFIG_PROC_FS
|
||||||
.show_fdinfo = drm_show_fdinfo,
|
.show_fdinfo = drm_show_fdinfo,
|
||||||
#endif
|
#endif
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct drm_driver driver = {
|
static struct drm_driver driver = {
|
||||||
|
@ -360,7 +360,7 @@ static int read_file_page(struct file *file, unsigned long index,
|
|||||||
pr_debug("read bitmap file (%dB @ %llu)\n", (int)PAGE_SIZE,
|
pr_debug("read bitmap file (%dB @ %llu)\n", (int)PAGE_SIZE,
|
||||||
(unsigned long long)index << PAGE_SHIFT);
|
(unsigned long long)index << PAGE_SHIFT);
|
||||||
|
|
||||||
bh = alloc_page_buffers(page, blocksize, false);
|
bh = alloc_page_buffers(page, blocksize);
|
||||||
if (!bh) {
|
if (!bh) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
|
2
fs/aio.c
2
fs/aio.c
@ -100,7 +100,7 @@ struct kioctx {
|
|||||||
|
|
||||||
unsigned long user_id;
|
unsigned long user_id;
|
||||||
|
|
||||||
struct __percpu kioctx_cpu *cpu;
|
struct kioctx_cpu __percpu *cpu;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For percpu reqs_available, number of slots we move to/from global
|
* For percpu reqs_available, number of slots we move to/from global
|
||||||
|
@ -62,6 +62,7 @@ struct autofs_info {
|
|||||||
struct list_head expiring;
|
struct list_head expiring;
|
||||||
|
|
||||||
struct autofs_sb_info *sbi;
|
struct autofs_sb_info *sbi;
|
||||||
|
unsigned long exp_timeout;
|
||||||
unsigned long last_used;
|
unsigned long last_used;
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
@ -81,6 +82,9 @@ struct autofs_info {
|
|||||||
*/
|
*/
|
||||||
#define AUTOFS_INF_PENDING (1<<2) /* dentry pending mount */
|
#define AUTOFS_INF_PENDING (1<<2) /* dentry pending mount */
|
||||||
|
|
||||||
|
#define AUTOFS_INF_EXPIRE_SET (1<<3) /* per-dentry expire timeout set for
|
||||||
|
this mount point.
|
||||||
|
*/
|
||||||
struct autofs_wait_queue {
|
struct autofs_wait_queue {
|
||||||
wait_queue_head_t queue;
|
wait_queue_head_t queue;
|
||||||
struct autofs_wait_queue *next;
|
struct autofs_wait_queue *next;
|
||||||
|
@ -128,7 +128,13 @@ static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Setting the per-dentry expire timeout requires a trailing
|
||||||
|
* path component, ie. no '/', so invert the logic of the
|
||||||
|
* check_name() return for AUTOFS_DEV_IOCTL_TIMEOUT_CMD.
|
||||||
|
*/
|
||||||
err = check_name(param->path);
|
err = check_name(param->path);
|
||||||
|
if (cmd == AUTOFS_DEV_IOCTL_TIMEOUT_CMD)
|
||||||
|
err = err ? 0 : -EINVAL;
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_warn("invalid path supplied for cmd(0x%08x)\n",
|
pr_warn("invalid path supplied for cmd(0x%08x)\n",
|
||||||
cmd);
|
cmd);
|
||||||
@ -396,16 +402,97 @@ static int autofs_dev_ioctl_catatonic(struct file *fp,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the autofs mount timeout */
|
/*
|
||||||
|
* Set the autofs mount expire timeout.
|
||||||
|
*
|
||||||
|
* There are two places an expire timeout can be set, in the autofs
|
||||||
|
* super block info. (this is all that's needed for direct and offset
|
||||||
|
* mounts because there's a distinct mount corresponding to each of
|
||||||
|
* these) and per-dentry within within the dentry info. If a per-dentry
|
||||||
|
* timeout is set it will override the expire timeout set in the parent
|
||||||
|
* autofs super block info.
|
||||||
|
*
|
||||||
|
* If setting the autofs super block expire timeout the autofs_dev_ioctl
|
||||||
|
* size field will be equal to the autofs_dev_ioctl structure size. If
|
||||||
|
* setting the per-dentry expire timeout the mount point name is passed
|
||||||
|
* in the autofs_dev_ioctl path field and the size field updated to
|
||||||
|
* reflect this.
|
||||||
|
*
|
||||||
|
* Setting the autofs mount expire timeout sets the timeout in the super
|
||||||
|
* block info. struct. Setting the per-dentry timeout does a little more.
|
||||||
|
* If the timeout is equal to -1 the per-dentry timeout (and flag) is
|
||||||
|
* cleared which reverts to using the super block timeout, otherwise if
|
||||||
|
* timeout is 0 the timeout is set to this value and the flag is left
|
||||||
|
* set which disables expiration for the mount point, lastly the flag
|
||||||
|
* and the timeout are set enabling the dentry to use this timeout.
|
||||||
|
*/
|
||||||
static int autofs_dev_ioctl_timeout(struct file *fp,
|
static int autofs_dev_ioctl_timeout(struct file *fp,
|
||||||
struct autofs_sb_info *sbi,
|
struct autofs_sb_info *sbi,
|
||||||
struct autofs_dev_ioctl *param)
|
struct autofs_dev_ioctl *param)
|
||||||
{
|
{
|
||||||
unsigned long timeout;
|
unsigned long timeout = param->timeout.timeout;
|
||||||
|
|
||||||
|
/* If setting the expire timeout for an individual indirect
|
||||||
|
* mount point dentry the mount trailing component path is
|
||||||
|
* placed in param->path and param->size adjusted to account
|
||||||
|
* for it otherwise param->size it is set to the structure
|
||||||
|
* size.
|
||||||
|
*/
|
||||||
|
if (param->size == AUTOFS_DEV_IOCTL_SIZE) {
|
||||||
|
param->timeout.timeout = sbi->exp_timeout / HZ;
|
||||||
|
sbi->exp_timeout = timeout * HZ;
|
||||||
|
} else {
|
||||||
|
struct dentry *base = fp->f_path.dentry;
|
||||||
|
struct inode *inode = base->d_inode;
|
||||||
|
int path_len = param->size - AUTOFS_DEV_IOCTL_SIZE - 1;
|
||||||
|
struct dentry *dentry;
|
||||||
|
struct autofs_info *ino;
|
||||||
|
|
||||||
|
if (!autofs_type_indirect(sbi->type))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* An expire timeout greater than the superblock timeout
|
||||||
|
* could be a problem at shutdown but the super block
|
||||||
|
* timeout itself can change so all we can really do is
|
||||||
|
* warn the user.
|
||||||
|
*/
|
||||||
|
if (timeout >= sbi->exp_timeout)
|
||||||
|
pr_warn("per-mount expire timeout is greater than "
|
||||||
|
"the parent autofs mount timeout which could "
|
||||||
|
"prevent shutdown\n");
|
||||||
|
|
||||||
|
inode_lock_shared(inode);
|
||||||
|
dentry = try_lookup_one_len(param->path, base, path_len);
|
||||||
|
inode_unlock_shared(inode);
|
||||||
|
if (IS_ERR_OR_NULL(dentry))
|
||||||
|
return dentry ? PTR_ERR(dentry) : -ENOENT;
|
||||||
|
ino = autofs_dentry_ino(dentry);
|
||||||
|
if (!ino) {
|
||||||
|
dput(dentry);
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ino->exp_timeout && ino->flags & AUTOFS_INF_EXPIRE_SET)
|
||||||
|
param->timeout.timeout = ino->exp_timeout / HZ;
|
||||||
|
else
|
||||||
|
param->timeout.timeout = sbi->exp_timeout / HZ;
|
||||||
|
|
||||||
|
if (timeout == -1) {
|
||||||
|
/* Revert to using the super block timeout */
|
||||||
|
ino->flags &= ~AUTOFS_INF_EXPIRE_SET;
|
||||||
|
ino->exp_timeout = 0;
|
||||||
|
} else {
|
||||||
|
/* Set the dentry expire flag and timeout.
|
||||||
|
*
|
||||||
|
* If timeout is 0 it will prevent the expire
|
||||||
|
* of this particular automount.
|
||||||
|
*/
|
||||||
|
ino->flags |= AUTOFS_INF_EXPIRE_SET;
|
||||||
|
ino->exp_timeout = timeout * HZ;
|
||||||
|
}
|
||||||
|
dput(dentry);
|
||||||
|
}
|
||||||
|
|
||||||
timeout = param->timeout.timeout;
|
|
||||||
param->timeout.timeout = sbi->exp_timeout / HZ;
|
|
||||||
sbi->exp_timeout = timeout * HZ;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,8 +429,6 @@ static struct dentry *autofs_expire_indirect(struct super_block *sb,
|
|||||||
if (!root)
|
if (!root)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
timeout = sbi->exp_timeout;
|
|
||||||
|
|
||||||
dentry = NULL;
|
dentry = NULL;
|
||||||
while ((dentry = get_next_positive_subdir(dentry, root))) {
|
while ((dentry = get_next_positive_subdir(dentry, root))) {
|
||||||
spin_lock(&sbi->fs_lock);
|
spin_lock(&sbi->fs_lock);
|
||||||
@ -441,6 +439,11 @@ static struct dentry *autofs_expire_indirect(struct super_block *sb,
|
|||||||
}
|
}
|
||||||
spin_unlock(&sbi->fs_lock);
|
spin_unlock(&sbi->fs_lock);
|
||||||
|
|
||||||
|
if (ino->flags & AUTOFS_INF_EXPIRE_SET)
|
||||||
|
timeout = ino->exp_timeout;
|
||||||
|
else
|
||||||
|
timeout = sbi->exp_timeout;
|
||||||
|
|
||||||
expired = should_expire(dentry, mnt, timeout, how);
|
expired = should_expire(dentry, mnt, timeout, how);
|
||||||
if (!expired)
|
if (!expired)
|
||||||
continue;
|
continue;
|
||||||
|
@ -19,6 +19,7 @@ struct autofs_info *autofs_new_ino(struct autofs_sb_info *sbi)
|
|||||||
INIT_LIST_HEAD(&ino->expiring);
|
INIT_LIST_HEAD(&ino->expiring);
|
||||||
ino->last_used = jiffies;
|
ino->last_used = jiffies;
|
||||||
ino->sbi = sbi;
|
ino->sbi = sbi;
|
||||||
|
ino->exp_timeout = -1;
|
||||||
ino->count = 1;
|
ino->count = 1;
|
||||||
}
|
}
|
||||||
return ino;
|
return ino;
|
||||||
@ -28,6 +29,7 @@ void autofs_clean_ino(struct autofs_info *ino)
|
|||||||
{
|
{
|
||||||
ino->uid = GLOBAL_ROOT_UID;
|
ino->uid = GLOBAL_ROOT_UID;
|
||||||
ino->gid = GLOBAL_ROOT_GID;
|
ino->gid = GLOBAL_ROOT_GID;
|
||||||
|
ino->exp_timeout = -1;
|
||||||
ino->last_used = jiffies;
|
ino->last_used = jiffies;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,8 +174,7 @@ static int autofs_parse_fd(struct fs_context *fc, struct autofs_sb_info *sbi,
|
|||||||
ret = autofs_check_pipe(pipe);
|
ret = autofs_check_pipe(pipe);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
errorf(fc, "Invalid/unusable pipe");
|
errorf(fc, "Invalid/unusable pipe");
|
||||||
if (param->type != fs_value_is_file)
|
fput(pipe);
|
||||||
fput(pipe);
|
|
||||||
return -EBADF;
|
return -EBADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1652,14 +1652,16 @@ again:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (clean_pass && this_pass_clean) {
|
} else if (clean_pass && this_pass_clean) {
|
||||||
wait_queue_head_t *wq = bit_waitqueue(&inode->v.i_state, __I_NEW);
|
struct wait_bit_queue_entry wqe;
|
||||||
DEFINE_WAIT_BIT(wait, &inode->v.i_state, __I_NEW);
|
struct wait_queue_head *wq_head;
|
||||||
|
|
||||||
prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE);
|
wq_head = inode_bit_waitqueue(&wqe, &inode->v, __I_NEW);
|
||||||
|
prepare_to_wait_event(wq_head, &wqe.wq_entry,
|
||||||
|
TASK_UNINTERRUPTIBLE);
|
||||||
mutex_unlock(&c->vfs_inodes_lock);
|
mutex_unlock(&c->vfs_inodes_lock);
|
||||||
|
|
||||||
schedule();
|
schedule();
|
||||||
finish_wait(wq, &wait.wq_entry);
|
finish_wait(wq_head, &wqe.wq_entry);
|
||||||
goto again;
|
goto again;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -774,12 +774,11 @@ EXPORT_SYMBOL(block_dirty_folio);
|
|||||||
static int fsync_buffers_list(spinlock_t *lock, struct list_head *list)
|
static int fsync_buffers_list(spinlock_t *lock, struct list_head *list)
|
||||||
{
|
{
|
||||||
struct buffer_head *bh;
|
struct buffer_head *bh;
|
||||||
struct list_head tmp;
|
|
||||||
struct address_space *mapping;
|
struct address_space *mapping;
|
||||||
int err = 0, err2;
|
int err = 0, err2;
|
||||||
struct blk_plug plug;
|
struct blk_plug plug;
|
||||||
|
LIST_HEAD(tmp);
|
||||||
|
|
||||||
INIT_LIST_HEAD(&tmp);
|
|
||||||
blk_start_plug(&plug);
|
blk_start_plug(&plug);
|
||||||
|
|
||||||
spin_lock(lock);
|
spin_lock(lock);
|
||||||
@ -958,12 +957,9 @@ no_grow:
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(folio_alloc_buffers);
|
EXPORT_SYMBOL_GPL(folio_alloc_buffers);
|
||||||
|
|
||||||
struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,
|
struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size)
|
||||||
bool retry)
|
|
||||||
{
|
{
|
||||||
gfp_t gfp = GFP_NOFS | __GFP_ACCOUNT;
|
gfp_t gfp = GFP_NOFS | __GFP_ACCOUNT;
|
||||||
if (retry)
|
|
||||||
gfp |= __GFP_NOFAIL;
|
|
||||||
|
|
||||||
return folio_alloc_buffers(page_folio(page), size, gfp);
|
return folio_alloc_buffers(page_folio(page), size, gfp);
|
||||||
}
|
}
|
||||||
|
@ -119,31 +119,43 @@ static const struct fs_parameter_spec coda_param_specs[] = {
|
|||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
static int coda_parse_fd(struct fs_context *fc, int fd)
|
static int coda_set_idx(struct fs_context *fc, struct file *file)
|
||||||
{
|
{
|
||||||
struct coda_fs_context *ctx = fc->fs_private;
|
struct coda_fs_context *ctx = fc->fs_private;
|
||||||
struct fd f;
|
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
int idx;
|
int idx;
|
||||||
|
|
||||||
f = fdget(fd);
|
inode = file_inode(file);
|
||||||
if (!f.file)
|
|
||||||
return -EBADF;
|
|
||||||
inode = file_inode(f.file);
|
|
||||||
if (!S_ISCHR(inode->i_mode) || imajor(inode) != CODA_PSDEV_MAJOR) {
|
if (!S_ISCHR(inode->i_mode) || imajor(inode) != CODA_PSDEV_MAJOR) {
|
||||||
fdput(f);
|
return invalf(fc, "coda: Not coda psdev");
|
||||||
return invalf(fc, "code: Not coda psdev");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
idx = iminor(inode);
|
idx = iminor(inode);
|
||||||
fdput(f);
|
|
||||||
|
|
||||||
if (idx < 0 || idx >= MAX_CODADEVS)
|
if (idx < 0 || idx >= MAX_CODADEVS)
|
||||||
return invalf(fc, "coda: Bad minor number");
|
return invalf(fc, "coda: Bad minor number");
|
||||||
ctx->idx = idx;
|
ctx->idx = idx;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int coda_parse_fd(struct fs_context *fc, struct fs_parameter *param,
|
||||||
|
struct fs_parse_result *result)
|
||||||
|
{
|
||||||
|
struct file *file;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (param->type == fs_value_is_file) {
|
||||||
|
file = param->file;
|
||||||
|
param->file = NULL;
|
||||||
|
} else {
|
||||||
|
file = fget(result->uint_32);
|
||||||
|
}
|
||||||
|
if (!file)
|
||||||
|
return -EBADF;
|
||||||
|
|
||||||
|
err = coda_set_idx(fc, file);
|
||||||
|
fput(file);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int coda_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
static int coda_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||||
{
|
{
|
||||||
struct fs_parse_result result;
|
struct fs_parse_result result;
|
||||||
@ -155,7 +167,7 @@ static int coda_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
|||||||
|
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case Opt_fd:
|
case Opt_fd:
|
||||||
return coda_parse_fd(fc, result.uint_32);
|
return coda_parse_fd(fc, param, &result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -167,6 +179,7 @@ static int coda_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
|||||||
*/
|
*/
|
||||||
static int coda_parse_monolithic(struct fs_context *fc, void *_data)
|
static int coda_parse_monolithic(struct fs_context *fc, void *_data)
|
||||||
{
|
{
|
||||||
|
struct file *file;
|
||||||
struct coda_mount_data *data = _data;
|
struct coda_mount_data *data = _data;
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
@ -175,7 +188,11 @@ static int coda_parse_monolithic(struct fs_context *fc, void *_data)
|
|||||||
if (data->version != CODA_MOUNT_VERSION)
|
if (data->version != CODA_MOUNT_VERSION)
|
||||||
return invalf(fc, "coda: Bad mount version");
|
return invalf(fc, "coda: Bad mount version");
|
||||||
|
|
||||||
coda_parse_fd(fc, data->fd);
|
file = fget(data->fd);
|
||||||
|
if (file) {
|
||||||
|
coda_set_idx(fc, file);
|
||||||
|
fput(file);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
fs/dcache.c
10
fs/dcache.c
@ -1913,8 +1913,13 @@ void d_instantiate_new(struct dentry *entry, struct inode *inode)
|
|||||||
__d_instantiate(entry, inode);
|
__d_instantiate(entry, inode);
|
||||||
WARN_ON(!(inode->i_state & I_NEW));
|
WARN_ON(!(inode->i_state & I_NEW));
|
||||||
inode->i_state &= ~I_NEW & ~I_CREATING;
|
inode->i_state &= ~I_NEW & ~I_CREATING;
|
||||||
|
/*
|
||||||
|
* Pairs with the barrier in prepare_to_wait_event() to make sure
|
||||||
|
* ___wait_var_event() either sees the bit cleared or
|
||||||
|
* waitqueue_active() check in wake_up_var() sees the waiter.
|
||||||
|
*/
|
||||||
smp_mb();
|
smp_mb();
|
||||||
wake_up_bit(&inode->i_state, __I_NEW);
|
inode_wake_up_bit(inode, __I_NEW);
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(d_instantiate_new);
|
EXPORT_SYMBOL(d_instantiate_new);
|
||||||
@ -2168,9 +2173,6 @@ seqretry:
|
|||||||
* without taking d_lock and checking d_seq sequence count against @seq
|
* without taking d_lock and checking d_seq sequence count against @seq
|
||||||
* returned here.
|
* returned here.
|
||||||
*
|
*
|
||||||
* A refcount may be taken on the found dentry with the d_rcu_to_refcount
|
|
||||||
* function.
|
|
||||||
*
|
|
||||||
* Alternatively, __d_lookup_rcu may be called again to look up the child of
|
* Alternatively, __d_lookup_rcu may be called again to look up the child of
|
||||||
* the returned dentry, so long as its parent's seqlock is checked after the
|
* the returned dentry, so long as its parent's seqlock is checked after the
|
||||||
* child is looked up. Thus, an interlocking stepping of sequence lock checks
|
* child is looked up. Thus, an interlocking stepping of sequence lock checks
|
||||||
|
@ -89,12 +89,14 @@ enum {
|
|||||||
Opt_uid,
|
Opt_uid,
|
||||||
Opt_gid,
|
Opt_gid,
|
||||||
Opt_mode,
|
Opt_mode,
|
||||||
|
Opt_source,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct fs_parameter_spec debugfs_param_specs[] = {
|
static const struct fs_parameter_spec debugfs_param_specs[] = {
|
||||||
fsparam_gid ("gid", Opt_gid),
|
fsparam_gid ("gid", Opt_gid),
|
||||||
fsparam_u32oct ("mode", Opt_mode),
|
fsparam_u32oct ("mode", Opt_mode),
|
||||||
fsparam_uid ("uid", Opt_uid),
|
fsparam_uid ("uid", Opt_uid),
|
||||||
|
fsparam_string ("source", Opt_source),
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -126,6 +128,12 @@ static int debugfs_parse_param(struct fs_context *fc, struct fs_parameter *param
|
|||||||
case Opt_mode:
|
case Opt_mode:
|
||||||
opts->mode = result.uint_32 & S_IALLUGO;
|
opts->mode = result.uint_32 & S_IALLUGO;
|
||||||
break;
|
break;
|
||||||
|
case Opt_source:
|
||||||
|
if (fc->source)
|
||||||
|
return invalfc(fc, "Multiple sources specified");
|
||||||
|
fc->source = param->string;
|
||||||
|
param->string = NULL;
|
||||||
|
break;
|
||||||
/*
|
/*
|
||||||
* We might like to report bad mount options here;
|
* We might like to report bad mount options here;
|
||||||
* but traditionally debugfs has ignored all mount options
|
* but traditionally debugfs has ignored all mount options
|
||||||
|
@ -37,7 +37,6 @@
|
|||||||
#include <linux/rwsem.h>
|
#include <linux/rwsem.h>
|
||||||
#include <linux/uio.h>
|
#include <linux/uio.h>
|
||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
#include <linux/prefetch.h>
|
|
||||||
|
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
@ -1121,11 +1120,6 @@ ssize_t __blockdev_direct_IO(struct kiocb *iocb, struct inode *inode,
|
|||||||
struct blk_plug plug;
|
struct blk_plug plug;
|
||||||
unsigned long align = offset | iov_iter_alignment(iter);
|
unsigned long align = offset | iov_iter_alignment(iter);
|
||||||
|
|
||||||
/*
|
|
||||||
* Avoid references to bdev if not absolutely needed to give
|
|
||||||
* the early prefetch in the caller enough time.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* watch out for a 0 len io from a tricksy fs */
|
/* watch out for a 0 len io from a tricksy fs */
|
||||||
if (iov_iter_rw(iter) == READ && !count)
|
if (iov_iter_rw(iter) == READ && !count)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -420,7 +420,7 @@ static bool busy_loop_ep_timeout(unsigned long start_time,
|
|||||||
|
|
||||||
static bool ep_busy_loop_on(struct eventpoll *ep)
|
static bool ep_busy_loop_on(struct eventpoll *ep)
|
||||||
{
|
{
|
||||||
return !!ep->busy_poll_usecs || net_busy_loop_on();
|
return !!READ_ONCE(ep->busy_poll_usecs) || net_busy_loop_on();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ep_busy_loop_end(void *p, unsigned long start_time)
|
static bool ep_busy_loop_end(void *p, unsigned long start_time)
|
||||||
@ -2200,11 +2200,6 @@ static int do_epoll_create(int flags)
|
|||||||
error = PTR_ERR(file);
|
error = PTR_ERR(file);
|
||||||
goto out_free_fd;
|
goto out_free_fd;
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_NET_RX_BUSY_POLL
|
|
||||||
ep->busy_poll_usecs = 0;
|
|
||||||
ep->busy_poll_budget = 0;
|
|
||||||
ep->prefer_busy_poll = false;
|
|
||||||
#endif
|
|
||||||
ep->file = file;
|
ep->file = file;
|
||||||
fd_install(fd, file);
|
fd_install(fd, file);
|
||||||
return fd;
|
return fd;
|
||||||
|
31
fs/exec.c
31
fs/exec.c
@ -145,13 +145,11 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* may_open() has already checked for this, so it should be
|
* Check do_open_execat() for an explanation.
|
||||||
* impossible to trip now. But we need to be extra cautious
|
|
||||||
* and check again at the very end too.
|
|
||||||
*/
|
*/
|
||||||
error = -EACCES;
|
error = -EACCES;
|
||||||
if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode) ||
|
if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)) ||
|
||||||
path_noexec(&file->f_path)))
|
path_noexec(&file->f_path))
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
error = -ENOEXEC;
|
error = -ENOEXEC;
|
||||||
@ -954,7 +952,6 @@ EXPORT_SYMBOL(transfer_args_to_stack);
|
|||||||
static struct file *do_open_execat(int fd, struct filename *name, int flags)
|
static struct file *do_open_execat(int fd, struct filename *name, int flags)
|
||||||
{
|
{
|
||||||
struct file *file;
|
struct file *file;
|
||||||
int err;
|
|
||||||
struct open_flags open_exec_flags = {
|
struct open_flags open_exec_flags = {
|
||||||
.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
|
.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
|
||||||
.acc_mode = MAY_EXEC,
|
.acc_mode = MAY_EXEC,
|
||||||
@ -971,24 +968,20 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)
|
|||||||
|
|
||||||
file = do_filp_open(fd, name, &open_exec_flags);
|
file = do_filp_open(fd, name, &open_exec_flags);
|
||||||
if (IS_ERR(file))
|
if (IS_ERR(file))
|
||||||
goto out;
|
return file;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* may_open() has already checked for this, so it should be
|
* In the past the regular type check was here. It moved to may_open() in
|
||||||
* impossible to trip now. But we need to be extra cautious
|
* 633fb6ac3980 ("exec: move S_ISREG() check earlier"). Since then it is
|
||||||
* and check again at the very end too.
|
* an invariant that all non-regular files error out before we get here.
|
||||||
*/
|
*/
|
||||||
err = -EACCES;
|
if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)) ||
|
||||||
if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode) ||
|
path_noexec(&file->f_path)) {
|
||||||
path_noexec(&file->f_path)))
|
fput(file);
|
||||||
goto exit;
|
return ERR_PTR(-EACCES);
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
|
||||||
return file;
|
return file;
|
||||||
|
|
||||||
exit:
|
|
||||||
fput(file);
|
|
||||||
return ERR_PTR(err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
10
fs/fcntl.c
10
fs/fcntl.c
@ -343,6 +343,12 @@ static long f_dupfd_query(int fd, struct file *filp)
|
|||||||
return f.file == filp;
|
return f.file == filp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Let the caller figure out whether a given file was just created. */
|
||||||
|
static long f_created_query(const struct file *filp)
|
||||||
|
{
|
||||||
|
return !!(filp->f_mode & FMODE_CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
||||||
struct file *filp)
|
struct file *filp)
|
||||||
{
|
{
|
||||||
@ -352,6 +358,9 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
|||||||
long err = -EINVAL;
|
long err = -EINVAL;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
case F_CREATED_QUERY:
|
||||||
|
err = f_created_query(filp);
|
||||||
|
break;
|
||||||
case F_DUPFD:
|
case F_DUPFD:
|
||||||
err = f_dupfd(argi, filp, 0);
|
err = f_dupfd(argi, filp, 0);
|
||||||
break;
|
break;
|
||||||
@ -463,6 +472,7 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
|||||||
static int check_fcntl_cmd(unsigned cmd)
|
static int check_fcntl_cmd(unsigned cmd)
|
||||||
{
|
{
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
case F_CREATED_QUERY:
|
||||||
case F_DUPFD:
|
case F_DUPFD:
|
||||||
case F_DUPFD_CLOEXEC:
|
case F_DUPFD_CLOEXEC:
|
||||||
case F_DUPFD_QUERY:
|
case F_DUPFD_QUERY:
|
||||||
|
29
fs/fhandle.c
29
fs/fhandle.c
@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
static long do_sys_name_to_handle(const struct path *path,
|
static long do_sys_name_to_handle(const struct path *path,
|
||||||
struct file_handle __user *ufh,
|
struct file_handle __user *ufh,
|
||||||
int __user *mnt_id, int fh_flags)
|
void __user *mnt_id, bool unique_mntid,
|
||||||
|
int fh_flags)
|
||||||
{
|
{
|
||||||
long retval;
|
long retval;
|
||||||
struct file_handle f_handle;
|
struct file_handle f_handle;
|
||||||
@ -69,9 +70,19 @@ static long do_sys_name_to_handle(const struct path *path,
|
|||||||
} else
|
} else
|
||||||
retval = 0;
|
retval = 0;
|
||||||
/* copy the mount id */
|
/* copy the mount id */
|
||||||
if (put_user(real_mount(path->mnt)->mnt_id, mnt_id) ||
|
if (unique_mntid) {
|
||||||
copy_to_user(ufh, handle,
|
if (put_user(real_mount(path->mnt)->mnt_id_unique,
|
||||||
struct_size(handle, f_handle, handle_bytes)))
|
(u64 __user *) mnt_id))
|
||||||
|
retval = -EFAULT;
|
||||||
|
} else {
|
||||||
|
if (put_user(real_mount(path->mnt)->mnt_id,
|
||||||
|
(int __user *) mnt_id))
|
||||||
|
retval = -EFAULT;
|
||||||
|
}
|
||||||
|
/* copy the handle */
|
||||||
|
if (retval != -EFAULT &&
|
||||||
|
copy_to_user(ufh, handle,
|
||||||
|
struct_size(handle, f_handle, handle_bytes)))
|
||||||
retval = -EFAULT;
|
retval = -EFAULT;
|
||||||
kfree(handle);
|
kfree(handle);
|
||||||
return retval;
|
return retval;
|
||||||
@ -83,6 +94,7 @@ static long do_sys_name_to_handle(const struct path *path,
|
|||||||
* @name: name that should be converted to handle.
|
* @name: name that should be converted to handle.
|
||||||
* @handle: resulting file handle
|
* @handle: resulting file handle
|
||||||
* @mnt_id: mount id of the file system containing the file
|
* @mnt_id: mount id of the file system containing the file
|
||||||
|
* (u64 if AT_HANDLE_MNT_ID_UNIQUE, otherwise int)
|
||||||
* @flag: flag value to indicate whether to follow symlink or not
|
* @flag: flag value to indicate whether to follow symlink or not
|
||||||
* and whether a decodable file handle is required.
|
* and whether a decodable file handle is required.
|
||||||
*
|
*
|
||||||
@ -92,7 +104,7 @@ static long do_sys_name_to_handle(const struct path *path,
|
|||||||
* value required.
|
* value required.
|
||||||
*/
|
*/
|
||||||
SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
|
SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
|
||||||
struct file_handle __user *, handle, int __user *, mnt_id,
|
struct file_handle __user *, handle, void __user *, mnt_id,
|
||||||
int, flag)
|
int, flag)
|
||||||
{
|
{
|
||||||
struct path path;
|
struct path path;
|
||||||
@ -100,7 +112,8 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
|
|||||||
int fh_flags;
|
int fh_flags;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | AT_HANDLE_FID))
|
if (flag & ~(AT_SYMLINK_FOLLOW | AT_EMPTY_PATH | AT_HANDLE_FID |
|
||||||
|
AT_HANDLE_MNT_ID_UNIQUE))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0;
|
lookup_flags = (flag & AT_SYMLINK_FOLLOW) ? LOOKUP_FOLLOW : 0;
|
||||||
@ -109,7 +122,9 @@ SYSCALL_DEFINE5(name_to_handle_at, int, dfd, const char __user *, name,
|
|||||||
lookup_flags |= LOOKUP_EMPTY;
|
lookup_flags |= LOOKUP_EMPTY;
|
||||||
err = user_path_at(dfd, name, lookup_flags, &path);
|
err = user_path_at(dfd, name, lookup_flags, &path);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
err = do_sys_name_to_handle(&path, handle, mnt_id, fh_flags);
|
err = do_sys_name_to_handle(&path, handle, mnt_id,
|
||||||
|
flag & AT_HANDLE_MNT_ID_UNIQUE,
|
||||||
|
fh_flags);
|
||||||
path_put(&path);
|
path_put(&path);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
|
@ -672,7 +672,7 @@ int close_fd(unsigned fd)
|
|||||||
|
|
||||||
return filp_close(file, files);
|
return filp_close(file, files);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(close_fd); /* for ksys_close() */
|
EXPORT_SYMBOL(close_fd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* last_fd - return last valid index into fd table
|
* last_fd - return last valid index into fd table
|
||||||
|
@ -136,6 +136,7 @@ static int __init init_fs_stat_sysctls(void)
|
|||||||
register_sysctl_init("fs", fs_stat_sysctls);
|
register_sysctl_init("fs", fs_stat_sysctls);
|
||||||
if (IS_ENABLED(CONFIG_BINFMT_MISC)) {
|
if (IS_ENABLED(CONFIG_BINFMT_MISC)) {
|
||||||
struct ctl_table_header *hdr;
|
struct ctl_table_header *hdr;
|
||||||
|
|
||||||
hdr = register_sysctl_mount_point("fs/binfmt_misc");
|
hdr = register_sysctl_mount_point("fs/binfmt_misc");
|
||||||
kmemleak_not_leak(hdr);
|
kmemleak_not_leak(hdr);
|
||||||
}
|
}
|
||||||
@ -383,7 +384,9 @@ EXPORT_SYMBOL_GPL(alloc_file_pseudo_noaccount);
|
|||||||
struct file *alloc_file_clone(struct file *base, int flags,
|
struct file *alloc_file_clone(struct file *base, int flags,
|
||||||
const struct file_operations *fops)
|
const struct file_operations *fops)
|
||||||
{
|
{
|
||||||
struct file *f = alloc_file(&base->f_path, flags, fops);
|
struct file *f;
|
||||||
|
|
||||||
|
f = alloc_file(&base->f_path, flags, fops);
|
||||||
if (!IS_ERR(f)) {
|
if (!IS_ERR(f)) {
|
||||||
path_get(&f->f_path);
|
path_get(&f->f_path);
|
||||||
f->f_mapping = base->f_mapping;
|
f->f_mapping = base->f_mapping;
|
||||||
|
@ -1132,6 +1132,7 @@ out_bdi_put:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* cgroup_writeback_umount - flush inode wb switches for umount
|
* cgroup_writeback_umount - flush inode wb switches for umount
|
||||||
|
* @sb: target super_block
|
||||||
*
|
*
|
||||||
* This function is called when a super_block is about to be destroyed and
|
* This function is called when a super_block is about to be destroyed and
|
||||||
* flushes in-flight inode wb switches. An inode wb switch goes through
|
* flushes in-flight inode wb switches. An inode wb switch goes through
|
||||||
@ -1140,8 +1141,12 @@ out_bdi_put:
|
|||||||
* rare occurrences and synchronize_rcu() can take a while, perform
|
* rare occurrences and synchronize_rcu() can take a while, perform
|
||||||
* flushing iff wb switches are in flight.
|
* flushing iff wb switches are in flight.
|
||||||
*/
|
*/
|
||||||
void cgroup_writeback_umount(void)
|
void cgroup_writeback_umount(struct super_block *sb)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (!(sb->s_bdi->capabilities & BDI_CAP_WRITEBACK))
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SB_ACTIVE should be reliably cleared before checking
|
* SB_ACTIVE should be reliably cleared before checking
|
||||||
* isw_nr_in_flight, see generic_shutdown_super().
|
* isw_nr_in_flight, see generic_shutdown_super().
|
||||||
@ -1381,12 +1386,13 @@ static void requeue_io(struct inode *inode, struct bdi_writeback *wb)
|
|||||||
|
|
||||||
static void inode_sync_complete(struct inode *inode)
|
static void inode_sync_complete(struct inode *inode)
|
||||||
{
|
{
|
||||||
|
assert_spin_locked(&inode->i_lock);
|
||||||
|
|
||||||
inode->i_state &= ~I_SYNC;
|
inode->i_state &= ~I_SYNC;
|
||||||
/* If inode is clean an unused, put it into LRU now... */
|
/* If inode is clean an unused, put it into LRU now... */
|
||||||
inode_add_lru(inode);
|
inode_add_lru(inode);
|
||||||
/* Waiters must see I_SYNC cleared before being woken up */
|
/* Called with inode->i_lock which ensures memory ordering. */
|
||||||
smp_mb();
|
inode_wake_up_bit(inode, __I_SYNC);
|
||||||
wake_up_bit(&inode->i_state, __I_SYNC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool inode_dirtied_after(struct inode *inode, unsigned long t)
|
static bool inode_dirtied_after(struct inode *inode, unsigned long t)
|
||||||
@ -1505,30 +1511,27 @@ static int write_inode(struct inode *inode, struct writeback_control *wbc)
|
|||||||
* Wait for writeback on an inode to complete. Called with i_lock held.
|
* Wait for writeback on an inode to complete. Called with i_lock held.
|
||||||
* Caller must make sure inode cannot go away when we drop i_lock.
|
* Caller must make sure inode cannot go away when we drop i_lock.
|
||||||
*/
|
*/
|
||||||
static void __inode_wait_for_writeback(struct inode *inode)
|
|
||||||
__releases(inode->i_lock)
|
|
||||||
__acquires(inode->i_lock)
|
|
||||||
{
|
|
||||||
DEFINE_WAIT_BIT(wq, &inode->i_state, __I_SYNC);
|
|
||||||
wait_queue_head_t *wqh;
|
|
||||||
|
|
||||||
wqh = bit_waitqueue(&inode->i_state, __I_SYNC);
|
|
||||||
while (inode->i_state & I_SYNC) {
|
|
||||||
spin_unlock(&inode->i_lock);
|
|
||||||
__wait_on_bit(wqh, &wq, bit_wait,
|
|
||||||
TASK_UNINTERRUPTIBLE);
|
|
||||||
spin_lock(&inode->i_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Wait for writeback on an inode to complete. Caller must have inode pinned.
|
|
||||||
*/
|
|
||||||
void inode_wait_for_writeback(struct inode *inode)
|
void inode_wait_for_writeback(struct inode *inode)
|
||||||
{
|
{
|
||||||
spin_lock(&inode->i_lock);
|
struct wait_bit_queue_entry wqe;
|
||||||
__inode_wait_for_writeback(inode);
|
struct wait_queue_head *wq_head;
|
||||||
spin_unlock(&inode->i_lock);
|
|
||||||
|
assert_spin_locked(&inode->i_lock);
|
||||||
|
|
||||||
|
if (!(inode->i_state & I_SYNC))
|
||||||
|
return;
|
||||||
|
|
||||||
|
wq_head = inode_bit_waitqueue(&wqe, inode, __I_SYNC);
|
||||||
|
for (;;) {
|
||||||
|
prepare_to_wait_event(wq_head, &wqe.wq_entry, TASK_UNINTERRUPTIBLE);
|
||||||
|
/* Checking I_SYNC with inode->i_lock guarantees memory ordering. */
|
||||||
|
if (!(inode->i_state & I_SYNC))
|
||||||
|
break;
|
||||||
|
spin_unlock(&inode->i_lock);
|
||||||
|
schedule();
|
||||||
|
spin_lock(&inode->i_lock);
|
||||||
|
}
|
||||||
|
finish_wait(wq_head, &wqe.wq_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1539,16 +1542,20 @@ void inode_wait_for_writeback(struct inode *inode)
|
|||||||
static void inode_sleep_on_writeback(struct inode *inode)
|
static void inode_sleep_on_writeback(struct inode *inode)
|
||||||
__releases(inode->i_lock)
|
__releases(inode->i_lock)
|
||||||
{
|
{
|
||||||
DEFINE_WAIT(wait);
|
struct wait_bit_queue_entry wqe;
|
||||||
wait_queue_head_t *wqh = bit_waitqueue(&inode->i_state, __I_SYNC);
|
struct wait_queue_head *wq_head;
|
||||||
int sleep;
|
bool sleep;
|
||||||
|
|
||||||
prepare_to_wait(wqh, &wait, TASK_UNINTERRUPTIBLE);
|
assert_spin_locked(&inode->i_lock);
|
||||||
sleep = inode->i_state & I_SYNC;
|
|
||||||
|
wq_head = inode_bit_waitqueue(&wqe, inode, __I_SYNC);
|
||||||
|
prepare_to_wait_event(wq_head, &wqe.wq_entry, TASK_UNINTERRUPTIBLE);
|
||||||
|
/* Checking I_SYNC with inode->i_lock guarantees memory ordering. */
|
||||||
|
sleep = !!(inode->i_state & I_SYNC);
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
if (sleep)
|
if (sleep)
|
||||||
schedule();
|
schedule();
|
||||||
finish_wait(wqh, &wait);
|
finish_wait(wq_head, &wqe.wq_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1752,7 +1759,7 @@ static int writeback_single_inode(struct inode *inode,
|
|||||||
*/
|
*/
|
||||||
if (wbc->sync_mode != WB_SYNC_ALL)
|
if (wbc->sync_mode != WB_SYNC_ALL)
|
||||||
goto out;
|
goto out;
|
||||||
__inode_wait_for_writeback(inode);
|
inode_wait_for_writeback(inode);
|
||||||
}
|
}
|
||||||
WARN_ON(inode->i_state & I_SYNC);
|
WARN_ON(inode->i_state & I_SYNC);
|
||||||
/*
|
/*
|
||||||
|
116
fs/inode.c
116
fs/inode.c
@ -472,6 +472,17 @@ static void __inode_add_lru(struct inode *inode, bool rotate)
|
|||||||
inode->i_state |= I_REFERENCED;
|
inode->i_state |= I_REFERENCED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct wait_queue_head *inode_bit_waitqueue(struct wait_bit_queue_entry *wqe,
|
||||||
|
struct inode *inode, u32 bit)
|
||||||
|
{
|
||||||
|
void *bit_address;
|
||||||
|
|
||||||
|
bit_address = inode_state_wait_address(inode, bit);
|
||||||
|
init_wait_var_entry(wqe, bit_address, 0);
|
||||||
|
return __var_waitqueue(bit_address);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(inode_bit_waitqueue);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add inode to LRU if needed (inode is unused and clean).
|
* Add inode to LRU if needed (inode is unused and clean).
|
||||||
*
|
*
|
||||||
@ -500,25 +511,35 @@ static void inode_unpin_lru_isolating(struct inode *inode)
|
|||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
WARN_ON(!(inode->i_state & I_LRU_ISOLATING));
|
WARN_ON(!(inode->i_state & I_LRU_ISOLATING));
|
||||||
inode->i_state &= ~I_LRU_ISOLATING;
|
inode->i_state &= ~I_LRU_ISOLATING;
|
||||||
smp_mb();
|
/* Called with inode->i_lock which ensures memory ordering. */
|
||||||
wake_up_bit(&inode->i_state, __I_LRU_ISOLATING);
|
inode_wake_up_bit(inode, __I_LRU_ISOLATING);
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inode_wait_for_lru_isolating(struct inode *inode)
|
static void inode_wait_for_lru_isolating(struct inode *inode)
|
||||||
{
|
{
|
||||||
spin_lock(&inode->i_lock);
|
struct wait_bit_queue_entry wqe;
|
||||||
if (inode->i_state & I_LRU_ISOLATING) {
|
struct wait_queue_head *wq_head;
|
||||||
DEFINE_WAIT_BIT(wq, &inode->i_state, __I_LRU_ISOLATING);
|
|
||||||
wait_queue_head_t *wqh;
|
|
||||||
|
|
||||||
wqh = bit_waitqueue(&inode->i_state, __I_LRU_ISOLATING);
|
lockdep_assert_held(&inode->i_lock);
|
||||||
|
if (!(inode->i_state & I_LRU_ISOLATING))
|
||||||
|
return;
|
||||||
|
|
||||||
|
wq_head = inode_bit_waitqueue(&wqe, inode, __I_LRU_ISOLATING);
|
||||||
|
for (;;) {
|
||||||
|
prepare_to_wait_event(wq_head, &wqe.wq_entry, TASK_UNINTERRUPTIBLE);
|
||||||
|
/*
|
||||||
|
* Checking I_LRU_ISOLATING with inode->i_lock guarantees
|
||||||
|
* memory ordering.
|
||||||
|
*/
|
||||||
|
if (!(inode->i_state & I_LRU_ISOLATING))
|
||||||
|
break;
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
__wait_on_bit(wqh, &wq, bit_wait, TASK_UNINTERRUPTIBLE);
|
schedule();
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
WARN_ON(inode->i_state & I_LRU_ISOLATING);
|
|
||||||
}
|
}
|
||||||
spin_unlock(&inode->i_lock);
|
finish_wait(wq_head, &wqe.wq_entry);
|
||||||
|
WARN_ON(inode->i_state & I_LRU_ISOLATING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -595,6 +616,7 @@ void dump_mapping(const struct address_space *mapping)
|
|||||||
struct hlist_node *dentry_first;
|
struct hlist_node *dentry_first;
|
||||||
struct dentry *dentry_ptr;
|
struct dentry *dentry_ptr;
|
||||||
struct dentry dentry;
|
struct dentry dentry;
|
||||||
|
char fname[64] = {};
|
||||||
unsigned long ino;
|
unsigned long ino;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -631,11 +653,14 @@ void dump_mapping(const struct address_space *mapping)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strncpy_from_kernel_nofault(fname, dentry.d_name.name, 63) < 0)
|
||||||
|
strscpy(fname, "<invalid>");
|
||||||
/*
|
/*
|
||||||
* if dentry is corrupted, the %pd handler may still crash,
|
* Even if strncpy_from_kernel_nofault() succeeded,
|
||||||
* but it's unlikely that we reach here with a corrupt mapping
|
* the fname could be unreliable
|
||||||
*/
|
*/
|
||||||
pr_warn("aops:%ps ino:%lx dentry name:\"%pd\"\n", a_ops, ino, &dentry);
|
pr_warn("aops:%ps ino:%lx dentry name(?):\"%s\"\n",
|
||||||
|
a_ops, ino, fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_inode(struct inode *inode)
|
void clear_inode(struct inode *inode)
|
||||||
@ -690,6 +715,7 @@ static void evict(struct inode *inode)
|
|||||||
|
|
||||||
inode_sb_list_del(inode);
|
inode_sb_list_del(inode);
|
||||||
|
|
||||||
|
spin_lock(&inode->i_lock);
|
||||||
inode_wait_for_lru_isolating(inode);
|
inode_wait_for_lru_isolating(inode);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -699,6 +725,7 @@ static void evict(struct inode *inode)
|
|||||||
* the inode. We just have to wait for running writeback to finish.
|
* the inode. We just have to wait for running writeback to finish.
|
||||||
*/
|
*/
|
||||||
inode_wait_for_writeback(inode);
|
inode_wait_for_writeback(inode);
|
||||||
|
spin_unlock(&inode->i_lock);
|
||||||
|
|
||||||
if (op->evict_inode) {
|
if (op->evict_inode) {
|
||||||
op->evict_inode(inode);
|
op->evict_inode(inode);
|
||||||
@ -722,7 +749,13 @@ static void evict(struct inode *inode)
|
|||||||
* used as an indicator whether blocking on it is safe.
|
* used as an indicator whether blocking on it is safe.
|
||||||
*/
|
*/
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
wake_up_bit(&inode->i_state, __I_NEW);
|
/*
|
||||||
|
* Pairs with the barrier in prepare_to_wait_event() to make sure
|
||||||
|
* ___wait_var_event() either sees the bit cleared or
|
||||||
|
* waitqueue_active() check in wake_up_var() sees the waiter.
|
||||||
|
*/
|
||||||
|
smp_mb();
|
||||||
|
inode_wake_up_bit(inode, __I_NEW);
|
||||||
BUG_ON(inode->i_state != (I_FREEING | I_CLEAR));
|
BUG_ON(inode->i_state != (I_FREEING | I_CLEAR));
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
|
|
||||||
@ -770,6 +803,10 @@ again:
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
|
if (atomic_read(&inode->i_count)) {
|
||||||
|
spin_unlock(&inode->i_lock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) {
|
if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) {
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
continue;
|
continue;
|
||||||
@ -1130,8 +1167,13 @@ void unlock_new_inode(struct inode *inode)
|
|||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
WARN_ON(!(inode->i_state & I_NEW));
|
WARN_ON(!(inode->i_state & I_NEW));
|
||||||
inode->i_state &= ~I_NEW & ~I_CREATING;
|
inode->i_state &= ~I_NEW & ~I_CREATING;
|
||||||
|
/*
|
||||||
|
* Pairs with the barrier in prepare_to_wait_event() to make sure
|
||||||
|
* ___wait_var_event() either sees the bit cleared or
|
||||||
|
* waitqueue_active() check in wake_up_var() sees the waiter.
|
||||||
|
*/
|
||||||
smp_mb();
|
smp_mb();
|
||||||
wake_up_bit(&inode->i_state, __I_NEW);
|
inode_wake_up_bit(inode, __I_NEW);
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(unlock_new_inode);
|
EXPORT_SYMBOL(unlock_new_inode);
|
||||||
@ -1142,8 +1184,13 @@ void discard_new_inode(struct inode *inode)
|
|||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
WARN_ON(!(inode->i_state & I_NEW));
|
WARN_ON(!(inode->i_state & I_NEW));
|
||||||
inode->i_state &= ~I_NEW;
|
inode->i_state &= ~I_NEW;
|
||||||
|
/*
|
||||||
|
* Pairs with the barrier in prepare_to_wait_event() to make sure
|
||||||
|
* ___wait_var_event() either sees the bit cleared or
|
||||||
|
* waitqueue_active() check in wake_up_var() sees the waiter.
|
||||||
|
*/
|
||||||
smp_mb();
|
smp_mb();
|
||||||
wake_up_bit(&inode->i_state, __I_NEW);
|
inode_wake_up_bit(inode, __I_NEW);
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
iput(inode);
|
iput(inode);
|
||||||
}
|
}
|
||||||
@ -1570,9 +1617,7 @@ struct inode *ilookup(struct super_block *sb, unsigned long ino)
|
|||||||
struct hlist_head *head = inode_hashtable + hash(sb, ino);
|
struct hlist_head *head = inode_hashtable + hash(sb, ino);
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
again:
|
again:
|
||||||
spin_lock(&inode_hash_lock);
|
inode = find_inode_fast(sb, head, ino, false);
|
||||||
inode = find_inode_fast(sb, head, ino, true);
|
|
||||||
spin_unlock(&inode_hash_lock);
|
|
||||||
|
|
||||||
if (inode) {
|
if (inode) {
|
||||||
if (IS_ERR(inode))
|
if (IS_ERR(inode))
|
||||||
@ -2334,8 +2379,8 @@ EXPORT_SYMBOL(inode_needs_sync);
|
|||||||
*/
|
*/
|
||||||
static void __wait_on_freeing_inode(struct inode *inode, bool is_inode_hash_locked)
|
static void __wait_on_freeing_inode(struct inode *inode, bool is_inode_hash_locked)
|
||||||
{
|
{
|
||||||
wait_queue_head_t *wq;
|
struct wait_bit_queue_entry wqe;
|
||||||
DEFINE_WAIT_BIT(wait, &inode->i_state, __I_NEW);
|
struct wait_queue_head *wq_head;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle racing against evict(), see that routine for more details.
|
* Handle racing against evict(), see that routine for more details.
|
||||||
@ -2346,14 +2391,14 @@ static void __wait_on_freeing_inode(struct inode *inode, bool is_inode_hash_lock
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wq = bit_waitqueue(&inode->i_state, __I_NEW);
|
wq_head = inode_bit_waitqueue(&wqe, inode, __I_NEW);
|
||||||
prepare_to_wait(wq, &wait.wq_entry, TASK_UNINTERRUPTIBLE);
|
prepare_to_wait_event(wq_head, &wqe.wq_entry, TASK_UNINTERRUPTIBLE);
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
if (is_inode_hash_locked)
|
if (is_inode_hash_locked)
|
||||||
spin_unlock(&inode_hash_lock);
|
spin_unlock(&inode_hash_lock);
|
||||||
schedule();
|
schedule();
|
||||||
finish_wait(wq, &wait.wq_entry);
|
finish_wait(wq_head, &wqe.wq_entry);
|
||||||
if (is_inode_hash_locked)
|
if (is_inode_hash_locked)
|
||||||
spin_lock(&inode_hash_lock);
|
spin_lock(&inode_hash_lock);
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
@ -2502,18 +2547,11 @@ EXPORT_SYMBOL(inode_owner_or_capable);
|
|||||||
/*
|
/*
|
||||||
* Direct i/o helper functions
|
* Direct i/o helper functions
|
||||||
*/
|
*/
|
||||||
static void __inode_dio_wait(struct inode *inode)
|
bool inode_dio_finished(const struct inode *inode)
|
||||||
{
|
{
|
||||||
wait_queue_head_t *wq = bit_waitqueue(&inode->i_state, __I_DIO_WAKEUP);
|
return atomic_read(&inode->i_dio_count) == 0;
|
||||||
DEFINE_WAIT_BIT(q, &inode->i_state, __I_DIO_WAKEUP);
|
|
||||||
|
|
||||||
do {
|
|
||||||
prepare_to_wait(wq, &q.wq_entry, TASK_UNINTERRUPTIBLE);
|
|
||||||
if (atomic_read(&inode->i_dio_count))
|
|
||||||
schedule();
|
|
||||||
} while (atomic_read(&inode->i_dio_count));
|
|
||||||
finish_wait(wq, &q.wq_entry);
|
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(inode_dio_finished);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* inode_dio_wait - wait for outstanding DIO requests to finish
|
* inode_dio_wait - wait for outstanding DIO requests to finish
|
||||||
@ -2527,11 +2565,17 @@ static void __inode_dio_wait(struct inode *inode)
|
|||||||
*/
|
*/
|
||||||
void inode_dio_wait(struct inode *inode)
|
void inode_dio_wait(struct inode *inode)
|
||||||
{
|
{
|
||||||
if (atomic_read(&inode->i_dio_count))
|
wait_var_event(&inode->i_dio_count, inode_dio_finished(inode));
|
||||||
__inode_dio_wait(inode);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(inode_dio_wait);
|
EXPORT_SYMBOL(inode_dio_wait);
|
||||||
|
|
||||||
|
void inode_dio_wait_interruptible(struct inode *inode)
|
||||||
|
{
|
||||||
|
wait_var_event_interruptible(&inode->i_dio_count,
|
||||||
|
inode_dio_finished(inode));
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(inode_dio_wait_interruptible);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* inode_set_flags - atomically set some inode flags
|
* inode_set_flags - atomically set some inode flags
|
||||||
*
|
*
|
||||||
|
28
fs/libfs.c
28
fs/libfs.c
@ -2003,13 +2003,19 @@ bool inode_maybe_inc_iversion(struct inode *inode, bool force)
|
|||||||
* information, but the legacy inode_inc_iversion code used a spinlock
|
* information, but the legacy inode_inc_iversion code used a spinlock
|
||||||
* to serialize increments.
|
* to serialize increments.
|
||||||
*
|
*
|
||||||
* Here, we add full memory barriers to ensure that any de-facto
|
* We add a full memory barrier to ensure that any de facto ordering
|
||||||
* ordering with other info is preserved.
|
* with other state is preserved (either implicitly coming from cmpxchg
|
||||||
|
* or explicitly from smp_mb if we don't know upfront if we will execute
|
||||||
|
* the former).
|
||||||
*
|
*
|
||||||
* This barrier pairs with the barrier in inode_query_iversion()
|
* These barriers pair with inode_query_iversion().
|
||||||
*/
|
*/
|
||||||
smp_mb();
|
|
||||||
cur = inode_peek_iversion_raw(inode);
|
cur = inode_peek_iversion_raw(inode);
|
||||||
|
if (!force && !(cur & I_VERSION_QUERIED)) {
|
||||||
|
smp_mb();
|
||||||
|
cur = inode_peek_iversion_raw(inode);
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
/* If flag is clear then we needn't do anything */
|
/* If flag is clear then we needn't do anything */
|
||||||
if (!force && !(cur & I_VERSION_QUERIED))
|
if (!force && !(cur & I_VERSION_QUERIED))
|
||||||
@ -2038,20 +2044,22 @@ EXPORT_SYMBOL(inode_maybe_inc_iversion);
|
|||||||
u64 inode_query_iversion(struct inode *inode)
|
u64 inode_query_iversion(struct inode *inode)
|
||||||
{
|
{
|
||||||
u64 cur, new;
|
u64 cur, new;
|
||||||
|
bool fenced = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Memory barriers (implicit in cmpxchg, explicit in smp_mb) pair with
|
||||||
|
* inode_maybe_inc_iversion(), see that routine for more details.
|
||||||
|
*/
|
||||||
cur = inode_peek_iversion_raw(inode);
|
cur = inode_peek_iversion_raw(inode);
|
||||||
do {
|
do {
|
||||||
/* If flag is already set, then no need to swap */
|
/* If flag is already set, then no need to swap */
|
||||||
if (cur & I_VERSION_QUERIED) {
|
if (cur & I_VERSION_QUERIED) {
|
||||||
/*
|
if (!fenced)
|
||||||
* This barrier (and the implicit barrier in the
|
smp_mb();
|
||||||
* cmpxchg below) pairs with the barrier in
|
|
||||||
* inode_maybe_inc_iversion().
|
|
||||||
*/
|
|
||||||
smp_mb();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fenced = true;
|
||||||
new = cur | I_VERSION_QUERIED;
|
new = cur | I_VERSION_QUERIED;
|
||||||
} while (!atomic64_try_cmpxchg(&inode->i_version, &cur, new));
|
} while (!atomic64_try_cmpxchg(&inode->i_version, &cur, new));
|
||||||
return cur >> I_VERSION_QUERIED_SHIFT;
|
return cur >> I_VERSION_QUERIED_SHIFT;
|
||||||
|
@ -228,15 +228,15 @@ static int copy_mnt_idmap(struct uid_gid_map *map_from,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
forward = kmemdup(map_from->forward,
|
forward = kmemdup_array(map_from->forward, nr_extents,
|
||||||
nr_extents * sizeof(struct uid_gid_extent),
|
sizeof(struct uid_gid_extent),
|
||||||
GFP_KERNEL_ACCOUNT);
|
GFP_KERNEL_ACCOUNT);
|
||||||
if (!forward)
|
if (!forward)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
reverse = kmemdup(map_from->reverse,
|
reverse = kmemdup_array(map_from->reverse, nr_extents,
|
||||||
nr_extents * sizeof(struct uid_gid_extent),
|
sizeof(struct uid_gid_extent),
|
||||||
GFP_KERNEL_ACCOUNT);
|
GFP_KERNEL_ACCOUNT);
|
||||||
if (!reverse) {
|
if (!reverse) {
|
||||||
kfree(forward);
|
kfree(forward);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -153,5 +153,4 @@ static inline void move_from_ns(struct mount *mnt, struct list_head *dt_list)
|
|||||||
list_add_tail(&mnt->mnt_list, dt_list);
|
list_add_tail(&mnt->mnt_list, dt_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void mnt_cursor_del(struct mnt_namespace *ns, struct mount *cursor);
|
|
||||||
bool has_locked_children(struct mount *mnt, struct dentry *dentry);
|
bool has_locked_children(struct mount *mnt, struct dentry *dentry);
|
||||||
|
75
fs/namei.c
75
fs/namei.c
@ -1639,6 +1639,20 @@ struct dentry *lookup_one_qstr_excl(const struct qstr *name,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(lookup_one_qstr_excl);
|
EXPORT_SYMBOL(lookup_one_qstr_excl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* lookup_fast - do fast lockless (but racy) lookup of a dentry
|
||||||
|
* @nd: current nameidata
|
||||||
|
*
|
||||||
|
* Do a fast, but racy lookup in the dcache for the given dentry, and
|
||||||
|
* revalidate it. Returns a valid dentry pointer or NULL if one wasn't
|
||||||
|
* found. On error, an ERR_PTR will be returned.
|
||||||
|
*
|
||||||
|
* If this function returns a valid dentry and the walk is no longer
|
||||||
|
* lazy, the dentry will carry a reference that must later be put. If
|
||||||
|
* RCU mode is still in force, then this is not the case and the dentry
|
||||||
|
* must be legitimized before use. If this returns NULL, then the walk
|
||||||
|
* will no longer be in RCU mode.
|
||||||
|
*/
|
||||||
static struct dentry *lookup_fast(struct nameidata *nd)
|
static struct dentry *lookup_fast(struct nameidata *nd)
|
||||||
{
|
{
|
||||||
struct dentry *dentry, *parent = nd->path.dentry;
|
struct dentry *dentry, *parent = nd->path.dentry;
|
||||||
@ -3521,6 +3535,9 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
|
|||||||
return dentry;
|
return dentry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (open_flag & O_CREAT)
|
||||||
|
audit_inode(nd->name, dir, AUDIT_INODE_PARENT);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checking write permission is tricky, bacuse we don't know if we are
|
* Checking write permission is tricky, bacuse we don't know if we are
|
||||||
* going to actually need it: O_CREAT opens should work as long as the
|
* going to actually need it: O_CREAT opens should work as long as the
|
||||||
@ -3591,6 +3608,42 @@ out_dput:
|
|||||||
return ERR_PTR(error);
|
return ERR_PTR(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool trailing_slashes(struct nameidata *nd)
|
||||||
|
{
|
||||||
|
return (bool)nd->last.name[nd->last.len];
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct dentry *lookup_fast_for_open(struct nameidata *nd, int open_flag)
|
||||||
|
{
|
||||||
|
struct dentry *dentry;
|
||||||
|
|
||||||
|
if (open_flag & O_CREAT) {
|
||||||
|
if (trailing_slashes(nd))
|
||||||
|
return ERR_PTR(-EISDIR);
|
||||||
|
|
||||||
|
/* Don't bother on an O_EXCL create */
|
||||||
|
if (open_flag & O_EXCL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trailing_slashes(nd))
|
||||||
|
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
|
||||||
|
|
||||||
|
dentry = lookup_fast(nd);
|
||||||
|
if (IS_ERR_OR_NULL(dentry))
|
||||||
|
return dentry;
|
||||||
|
|
||||||
|
if (open_flag & O_CREAT) {
|
||||||
|
/* Discard negative dentries. Need inode_lock to do the create */
|
||||||
|
if (!dentry->d_inode) {
|
||||||
|
if (!(nd->flags & LOOKUP_RCU))
|
||||||
|
dput(dentry);
|
||||||
|
dentry = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dentry;
|
||||||
|
}
|
||||||
|
|
||||||
static const char *open_last_lookups(struct nameidata *nd,
|
static const char *open_last_lookups(struct nameidata *nd,
|
||||||
struct file *file, const struct open_flags *op)
|
struct file *file, const struct open_flags *op)
|
||||||
{
|
{
|
||||||
@ -3608,28 +3661,22 @@ static const char *open_last_lookups(struct nameidata *nd,
|
|||||||
return handle_dots(nd, nd->last_type);
|
return handle_dots(nd, nd->last_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(open_flag & O_CREAT)) {
|
/* We _can_ be in RCU mode here */
|
||||||
if (nd->last.name[nd->last.len])
|
dentry = lookup_fast_for_open(nd, open_flag);
|
||||||
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
|
if (IS_ERR(dentry))
|
||||||
/* we _can_ be in RCU mode here */
|
return ERR_CAST(dentry);
|
||||||
dentry = lookup_fast(nd);
|
|
||||||
if (IS_ERR(dentry))
|
|
||||||
return ERR_CAST(dentry);
|
|
||||||
if (likely(dentry))
|
|
||||||
goto finish_lookup;
|
|
||||||
|
|
||||||
|
if (likely(dentry))
|
||||||
|
goto finish_lookup;
|
||||||
|
|
||||||
|
if (!(open_flag & O_CREAT)) {
|
||||||
if (WARN_ON_ONCE(nd->flags & LOOKUP_RCU))
|
if (WARN_ON_ONCE(nd->flags & LOOKUP_RCU))
|
||||||
return ERR_PTR(-ECHILD);
|
return ERR_PTR(-ECHILD);
|
||||||
} else {
|
} else {
|
||||||
/* create side of things */
|
|
||||||
if (nd->flags & LOOKUP_RCU) {
|
if (nd->flags & LOOKUP_RCU) {
|
||||||
if (!try_to_unlazy(nd))
|
if (!try_to_unlazy(nd))
|
||||||
return ERR_PTR(-ECHILD);
|
return ERR_PTR(-ECHILD);
|
||||||
}
|
}
|
||||||
audit_inode(nd->name, dir, AUDIT_INODE_PARENT);
|
|
||||||
/* trailing slashes? */
|
|
||||||
if (unlikely(nd->last.name[nd->last.len]))
|
|
||||||
return ERR_PTR(-EISDIR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
|
if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
|
||||||
|
@ -1774,7 +1774,7 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how)
|
|||||||
list_del_init(&p->mnt_child);
|
list_del_init(&p->mnt_child);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add propogated mounts to the tmp_list */
|
/* Add propagated mounts to the tmp_list */
|
||||||
if (how & UMOUNT_PROPAGATE)
|
if (how & UMOUNT_PROPAGATE)
|
||||||
propagate_umount(&tmp_list);
|
propagate_umount(&tmp_list);
|
||||||
|
|
||||||
@ -2921,8 +2921,15 @@ static void mnt_warn_timestamp_expiry(struct path *mountpoint, struct vfsmount *
|
|||||||
if (!__mnt_is_readonly(mnt) &&
|
if (!__mnt_is_readonly(mnt) &&
|
||||||
(!(sb->s_iflags & SB_I_TS_EXPIRY_WARNED)) &&
|
(!(sb->s_iflags & SB_I_TS_EXPIRY_WARNED)) &&
|
||||||
(ktime_get_real_seconds() + TIME_UPTIME_SEC_MAX > sb->s_time_max)) {
|
(ktime_get_real_seconds() + TIME_UPTIME_SEC_MAX > sb->s_time_max)) {
|
||||||
char *buf = (char *)__get_free_page(GFP_KERNEL);
|
char *buf, *mntpath;
|
||||||
char *mntpath = buf ? d_path(mountpoint, buf, PAGE_SIZE) : ERR_PTR(-ENOMEM);
|
|
||||||
|
buf = (char *)__get_free_page(GFP_KERNEL);
|
||||||
|
if (buf)
|
||||||
|
mntpath = d_path(mountpoint, buf, PAGE_SIZE);
|
||||||
|
else
|
||||||
|
mntpath = ERR_PTR(-ENOMEM);
|
||||||
|
if (IS_ERR(mntpath))
|
||||||
|
mntpath = "(unknown)";
|
||||||
|
|
||||||
pr_warn("%s filesystem being %s at %s supports timestamps until %ptTd (0x%llx)\n",
|
pr_warn("%s filesystem being %s at %s supports timestamps until %ptTd (0x%llx)\n",
|
||||||
sb->s_type->name,
|
sb->s_type->name,
|
||||||
@ -2930,8 +2937,9 @@ static void mnt_warn_timestamp_expiry(struct path *mountpoint, struct vfsmount *
|
|||||||
mntpath, &sb->s_time_max,
|
mntpath, &sb->s_time_max,
|
||||||
(unsigned long long)sb->s_time_max);
|
(unsigned long long)sb->s_time_max);
|
||||||
|
|
||||||
free_page((unsigned long)buf);
|
|
||||||
sb->s_iflags |= SB_I_TS_EXPIRY_WARNED;
|
sb->s_iflags |= SB_I_TS_EXPIRY_WARNED;
|
||||||
|
if (buf)
|
||||||
|
free_page((unsigned long)buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5605,7 +5613,7 @@ static bool mnt_already_visible(struct mnt_namespace *ns,
|
|||||||
/* Only worry about locked mounts */
|
/* Only worry about locked mounts */
|
||||||
if (!(child->mnt.mnt_flags & MNT_LOCKED))
|
if (!(child->mnt.mnt_flags & MNT_LOCKED))
|
||||||
continue;
|
continue;
|
||||||
/* Is the directory permanetly empty? */
|
/* Is the directory permanently empty? */
|
||||||
if (!is_empty_dir_inode(inode))
|
if (!is_empty_dir_inode(inode))
|
||||||
goto next;
|
goto next;
|
||||||
}
|
}
|
||||||
|
@ -19,25 +19,13 @@
|
|||||||
* Must be called under a lock that serializes taking new references
|
* Must be called under a lock that serializes taking new references
|
||||||
* to i_dio_count, usually by inode->i_mutex.
|
* to i_dio_count, usually by inode->i_mutex.
|
||||||
*/
|
*/
|
||||||
static int inode_dio_wait_interruptible(struct inode *inode)
|
static int netfs_inode_dio_wait_interruptible(struct inode *inode)
|
||||||
{
|
{
|
||||||
if (!atomic_read(&inode->i_dio_count))
|
if (inode_dio_finished(inode))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
wait_queue_head_t *wq = bit_waitqueue(&inode->i_state, __I_DIO_WAKEUP);
|
inode_dio_wait_interruptible(inode);
|
||||||
DEFINE_WAIT_BIT(q, &inode->i_state, __I_DIO_WAKEUP);
|
return !inode_dio_finished(inode) ? -ERESTARTSYS : 0;
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
prepare_to_wait(wq, &q.wq_entry, TASK_INTERRUPTIBLE);
|
|
||||||
if (!atomic_read(&inode->i_dio_count))
|
|
||||||
break;
|
|
||||||
if (signal_pending(current))
|
|
||||||
break;
|
|
||||||
schedule();
|
|
||||||
}
|
|
||||||
finish_wait(wq, &q.wq_entry);
|
|
||||||
|
|
||||||
return atomic_read(&inode->i_dio_count) ? -ERESTARTSYS : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call with exclusively locked inode->i_rwsem */
|
/* Call with exclusively locked inode->i_rwsem */
|
||||||
@ -46,7 +34,7 @@ static int netfs_block_o_direct(struct netfs_inode *ictx)
|
|||||||
if (!test_bit(NETFS_ICTX_ODIRECT, &ictx->flags))
|
if (!test_bit(NETFS_ICTX_ODIRECT, &ictx->flags))
|
||||||
return 0;
|
return 0;
|
||||||
clear_bit(NETFS_ICTX_ODIRECT, &ictx->flags);
|
clear_bit(NETFS_ICTX_ODIRECT, &ictx->flags);
|
||||||
return inode_dio_wait_interruptible(&ictx->inode);
|
return netfs_inode_dio_wait_interruptible(&ictx->inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -142,7 +142,7 @@ static int __init netfs_init(void)
|
|||||||
|
|
||||||
error_fscache:
|
error_fscache:
|
||||||
error_procfile:
|
error_procfile:
|
||||||
remove_proc_entry("fs/netfs", NULL);
|
remove_proc_subtree("fs/netfs", NULL);
|
||||||
error_proc:
|
error_proc:
|
||||||
mempool_exit(&netfs_subrequest_pool);
|
mempool_exit(&netfs_subrequest_pool);
|
||||||
error_subreqpool:
|
error_subreqpool:
|
||||||
@ -159,7 +159,7 @@ fs_initcall(netfs_init);
|
|||||||
static void __exit netfs_exit(void)
|
static void __exit netfs_exit(void)
|
||||||
{
|
{
|
||||||
fscache_exit();
|
fscache_exit();
|
||||||
remove_proc_entry("fs/netfs", NULL);
|
remove_proc_subtree("fs/netfs", NULL);
|
||||||
mempool_exit(&netfs_subrequest_pool);
|
mempool_exit(&netfs_subrequest_pool);
|
||||||
kmem_cache_destroy(netfs_subrequest_slab);
|
kmem_cache_destroy(netfs_subrequest_slab);
|
||||||
mempool_exit(&netfs_request_pool);
|
mempool_exit(&netfs_request_pool);
|
||||||
|
@ -1427,7 +1427,7 @@ static const struct super_operations pipefs_ops = {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* pipefs should _never_ be mounted by userland - too much of security hassle,
|
* pipefs should _never_ be mounted by userland - too much of security hassle,
|
||||||
* no real gain from having the whole whorehouse mounted. So we don't need
|
* no real gain from having the whole file system mounted. So we don't need
|
||||||
* any operations on the root directory. However, we need a non-trivial
|
* any operations on the root directory. However, we need a non-trivial
|
||||||
* d_name - pipe: will go nicely and kill the special-casing in procfs.
|
* d_name - pipe: will go nicely and kill the special-casing in procfs.
|
||||||
*/
|
*/
|
||||||
|
@ -715,8 +715,8 @@ int posix_acl_update_mode(struct mnt_idmap *idmap,
|
|||||||
return error;
|
return error;
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
*acl = NULL;
|
*acl = NULL;
|
||||||
if (!vfsgid_in_group_p(i_gid_into_vfsgid(idmap, inode)) &&
|
if (!in_group_or_capable(idmap, inode,
|
||||||
!capable_wrt_inode_uidgid(idmap, inode, CAP_FSETID))
|
i_gid_into_vfsgid(idmap, inode)))
|
||||||
mode &= ~S_ISGID;
|
mode &= ~S_ISGID;
|
||||||
*mode_p = mode;
|
*mode_p = mode;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -827,12 +827,9 @@ static int __mem_open(struct inode *inode, struct file *file, unsigned int mode)
|
|||||||
|
|
||||||
static int mem_open(struct inode *inode, struct file *file)
|
static int mem_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
int ret = __mem_open(inode, file, PTRACE_MODE_ATTACH);
|
if (WARN_ON_ONCE(!(file->f_op->fop_flags & FOP_UNSIGNED_OFFSET)))
|
||||||
|
return -EINVAL;
|
||||||
/* OK to pass negative loff_t, we can catch out-of-range */
|
return __mem_open(inode, file, PTRACE_MODE_ATTACH);
|
||||||
file->f_mode |= FMODE_UNSIGNED_OFFSET;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t mem_rw(struct file *file, char __user *buf,
|
static ssize_t mem_rw(struct file *file, char __user *buf,
|
||||||
@ -932,6 +929,7 @@ static const struct file_operations proc_mem_operations = {
|
|||||||
.write = mem_write,
|
.write = mem_write,
|
||||||
.open = mem_open,
|
.open = mem_open,
|
||||||
.release = mem_release,
|
.release = mem_release,
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int environ_open(struct inode *inode, struct file *file)
|
static int environ_open(struct inode *inode, struct file *file)
|
||||||
|
@ -59,7 +59,7 @@ static int seq_show(struct seq_file *m, void *v)
|
|||||||
real_mount(file->f_path.mnt)->mnt_id,
|
real_mount(file->f_path.mnt)->mnt_id,
|
||||||
file_inode(file)->i_ino);
|
file_inode(file)->i_ino);
|
||||||
|
|
||||||
/* show_fd_locks() never deferences files so a stale value is safe */
|
/* show_fd_locks() never dereferences files, so a stale value is safe */
|
||||||
show_fd_locks(m, file, files);
|
show_fd_locks(m, file, files);
|
||||||
if (seq_has_overflowed(m))
|
if (seq_has_overflowed(m))
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -235,7 +235,7 @@ static int kcore_ram_list(struct list_head *list)
|
|||||||
int nid, ret;
|
int nid, ret;
|
||||||
unsigned long end_pfn;
|
unsigned long end_pfn;
|
||||||
|
|
||||||
/* Not inialized....update now */
|
/* Not initialized....update now */
|
||||||
/* find out "max pfn" */
|
/* find out "max pfn" */
|
||||||
end_pfn = 0;
|
end_pfn = 0;
|
||||||
for_each_node_state(nid, N_MEMORY) {
|
for_each_node_state(nid, N_MEMORY) {
|
||||||
|
@ -36,7 +36,7 @@ EXPORT_SYMBOL(generic_ro_fops);
|
|||||||
|
|
||||||
static inline bool unsigned_offsets(struct file *file)
|
static inline bool unsigned_offsets(struct file *file)
|
||||||
{
|
{
|
||||||
return file->f_mode & FMODE_UNSIGNED_OFFSET;
|
return file->f_op->fop_flags & FOP_UNSIGNED_OFFSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -840,7 +840,7 @@ SYSCALL_DEFINE1(old_select, struct sel_arg_struct __user *, arg)
|
|||||||
struct poll_list {
|
struct poll_list {
|
||||||
struct poll_list *next;
|
struct poll_list *next;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
struct pollfd entries[];
|
struct pollfd entries[] __counted_by(len);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define POLLFD_PER_PAGE ((PAGE_SIZE-sizeof(struct poll_list)) / sizeof(struct pollfd))
|
#define POLLFD_PER_PAGE ((PAGE_SIZE-sizeof(struct poll_list)) / sizeof(struct pollfd))
|
||||||
|
@ -621,7 +621,7 @@ void generic_shutdown_super(struct super_block *sb)
|
|||||||
sync_filesystem(sb);
|
sync_filesystem(sb);
|
||||||
sb->s_flags &= ~SB_ACTIVE;
|
sb->s_flags &= ~SB_ACTIVE;
|
||||||
|
|
||||||
cgroup_writeback_umount();
|
cgroup_writeback_umount(sb);
|
||||||
|
|
||||||
/* Evict all inodes with zero refcount. */
|
/* Evict all inodes with zero refcount. */
|
||||||
evict_inodes(sb);
|
evict_inodes(sb);
|
||||||
@ -1905,7 +1905,7 @@ static void lockdep_sb_freeze_release(struct super_block *sb)
|
|||||||
int level;
|
int level;
|
||||||
|
|
||||||
for (level = SB_FREEZE_LEVELS - 1; level >= 0; level--)
|
for (level = SB_FREEZE_LEVELS - 1; level >= 0; level--)
|
||||||
percpu_rwsem_release(sb->s_writers.rw_sem + level, 0, _THIS_IP_);
|
percpu_rwsem_release(sb->s_writers.rw_sem + level, _THIS_IP_);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -28,7 +28,8 @@
|
|||||||
.poll = drm_poll,\
|
.poll = drm_poll,\
|
||||||
.read = drm_read,\
|
.read = drm_read,\
|
||||||
.llseek = noop_llseek, \
|
.llseek = noop_llseek, \
|
||||||
.mmap = drm_gem_mmap
|
.mmap = drm_gem_mmap, \
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DEFINE_DRM_ACCEL_FOPS() - macro to generate file operations for accelerators drivers
|
* DEFINE_DRM_ACCEL_FOPS() - macro to generate file operations for accelerators drivers
|
||||||
|
@ -447,7 +447,8 @@ struct drm_gem_object {
|
|||||||
.poll = drm_poll,\
|
.poll = drm_poll,\
|
||||||
.read = drm_read,\
|
.read = drm_read,\
|
||||||
.llseek = noop_llseek,\
|
.llseek = noop_llseek,\
|
||||||
.mmap = drm_gem_mmap
|
.mmap = drm_gem_mmap, \
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DEFINE_DRM_GEM_FOPS() - macro to generate file operations for GEM drivers
|
* DEFINE_DRM_GEM_FOPS() - macro to generate file operations for GEM drivers
|
||||||
|
@ -267,6 +267,7 @@ unsigned long drm_gem_dma_get_unmapped_area(struct file *filp,
|
|||||||
.read = drm_read,\
|
.read = drm_read,\
|
||||||
.llseek = noop_llseek,\
|
.llseek = noop_llseek,\
|
||||||
.mmap = drm_gem_mmap,\
|
.mmap = drm_gem_mmap,\
|
||||||
|
.fop_flags = FOP_UNSIGNED_OFFSET, \
|
||||||
DRM_GEM_DMA_UNMAPPED_AREA_FOPS \
|
DRM_GEM_DMA_UNMAPPED_AREA_FOPS \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,8 +199,7 @@ void folio_set_bh(struct buffer_head *bh, struct folio *folio,
|
|||||||
unsigned long offset);
|
unsigned long offset);
|
||||||
struct buffer_head *folio_alloc_buffers(struct folio *folio, unsigned long size,
|
struct buffer_head *folio_alloc_buffers(struct folio *folio, unsigned long size,
|
||||||
gfp_t gfp);
|
gfp_t gfp);
|
||||||
struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size,
|
struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size);
|
||||||
bool retry);
|
|
||||||
struct buffer_head *create_empty_buffers(struct folio *folio,
|
struct buffer_head *create_empty_buffers(struct folio *folio,
|
||||||
unsigned long blocksize, unsigned long b_state);
|
unsigned long blocksize, unsigned long b_state);
|
||||||
void end_buffer_read_sync(struct buffer_head *bh, int uptodate);
|
void end_buffer_read_sync(struct buffer_head *bh, int uptodate);
|
||||||
|
@ -420,28 +420,38 @@ static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl)
|
|||||||
#ifdef CONFIG_FILE_LOCKING
|
#ifdef CONFIG_FILE_LOCKING
|
||||||
static inline int break_lease(struct inode *inode, unsigned int mode)
|
static inline int break_lease(struct inode *inode, unsigned int mode)
|
||||||
{
|
{
|
||||||
|
struct file_lock_context *flctx;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since this check is lockless, we must ensure that any refcounts
|
* Since this check is lockless, we must ensure that any refcounts
|
||||||
* taken are done before checking i_flctx->flc_lease. Otherwise, we
|
* taken are done before checking i_flctx->flc_lease. Otherwise, we
|
||||||
* could end up racing with tasks trying to set a new lease on this
|
* could end up racing with tasks trying to set a new lease on this
|
||||||
* file.
|
* file.
|
||||||
*/
|
*/
|
||||||
|
flctx = READ_ONCE(inode->i_flctx);
|
||||||
|
if (!flctx)
|
||||||
|
return 0;
|
||||||
smp_mb();
|
smp_mb();
|
||||||
if (inode->i_flctx && !list_empty_careful(&inode->i_flctx->flc_lease))
|
if (!list_empty_careful(&flctx->flc_lease))
|
||||||
return __break_lease(inode, mode, FL_LEASE);
|
return __break_lease(inode, mode, FL_LEASE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int break_deleg(struct inode *inode, unsigned int mode)
|
static inline int break_deleg(struct inode *inode, unsigned int mode)
|
||||||
{
|
{
|
||||||
|
struct file_lock_context *flctx;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since this check is lockless, we must ensure that any refcounts
|
* Since this check is lockless, we must ensure that any refcounts
|
||||||
* taken are done before checking i_flctx->flc_lease. Otherwise, we
|
* taken are done before checking i_flctx->flc_lease. Otherwise, we
|
||||||
* could end up racing with tasks trying to set a new lease on this
|
* could end up racing with tasks trying to set a new lease on this
|
||||||
* file.
|
* file.
|
||||||
*/
|
*/
|
||||||
|
flctx = READ_ONCE(inode->i_flctx);
|
||||||
|
if (!flctx)
|
||||||
|
return 0;
|
||||||
smp_mb();
|
smp_mb();
|
||||||
if (inode->i_flctx && !list_empty_careful(&inode->i_flctx->flc_lease))
|
if (!list_empty_careful(&flctx->flc_lease))
|
||||||
return __break_lease(inode, mode, FL_DELEG);
|
return __break_lease(inode, mode, FL_DELEG);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -146,8 +146,7 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
|
|||||||
/* Expect random access pattern */
|
/* Expect random access pattern */
|
||||||
#define FMODE_RANDOM ((__force fmode_t)(1 << 12))
|
#define FMODE_RANDOM ((__force fmode_t)(1 << 12))
|
||||||
|
|
||||||
/* File is huge (eg. /dev/mem): treat loff_t as unsigned */
|
/* FMODE_* bit 13 */
|
||||||
#define FMODE_UNSIGNED_OFFSET ((__force fmode_t)(1 << 13))
|
|
||||||
|
|
||||||
/* File is opened with O_PATH; almost nothing can be done with it */
|
/* File is opened with O_PATH; almost nothing can be done with it */
|
||||||
#define FMODE_PATH ((__force fmode_t)(1 << 14))
|
#define FMODE_PATH ((__force fmode_t)(1 << 14))
|
||||||
@ -683,7 +682,8 @@ struct inode {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Misc */
|
/* Misc */
|
||||||
unsigned long i_state;
|
u32 i_state;
|
||||||
|
/* 32-bit hole */
|
||||||
struct rw_semaphore i_rwsem;
|
struct rw_semaphore i_rwsem;
|
||||||
|
|
||||||
unsigned long dirtied_when; /* jiffies of first dirtying */
|
unsigned long dirtied_when; /* jiffies of first dirtying */
|
||||||
@ -746,6 +746,21 @@ struct inode {
|
|||||||
void *i_private; /* fs or device private pointer */
|
void *i_private; /* fs or device private pointer */
|
||||||
} __randomize_layout;
|
} __randomize_layout;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get bit address from inode->i_state to use with wait_var_event()
|
||||||
|
* infrastructre.
|
||||||
|
*/
|
||||||
|
#define inode_state_wait_address(inode, bit) ((char *)&(inode)->i_state + (bit))
|
||||||
|
|
||||||
|
struct wait_queue_head *inode_bit_waitqueue(struct wait_bit_queue_entry *wqe,
|
||||||
|
struct inode *inode, u32 bit);
|
||||||
|
|
||||||
|
static inline void inode_wake_up_bit(struct inode *inode, u32 bit)
|
||||||
|
{
|
||||||
|
/* Caller is responsible for correct memory barriers. */
|
||||||
|
wake_up_var(inode_state_wait_address(inode, bit));
|
||||||
|
}
|
||||||
|
|
||||||
struct timespec64 timestamp_truncate(struct timespec64 t, struct inode *inode);
|
struct timespec64 timestamp_truncate(struct timespec64 t, struct inode *inode);
|
||||||
|
|
||||||
static inline unsigned int i_blocksize(const struct inode *node)
|
static inline unsigned int i_blocksize(const struct inode *node)
|
||||||
@ -1268,7 +1283,7 @@ struct super_block {
|
|||||||
time64_t s_time_min;
|
time64_t s_time_min;
|
||||||
time64_t s_time_max;
|
time64_t s_time_max;
|
||||||
#ifdef CONFIG_FSNOTIFY
|
#ifdef CONFIG_FSNOTIFY
|
||||||
__u32 s_fsnotify_mask;
|
u32 s_fsnotify_mask;
|
||||||
struct fsnotify_sb_info *s_fsnotify_info;
|
struct fsnotify_sb_info *s_fsnotify_info;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1684,7 +1699,7 @@ static inline bool __sb_start_write_trylock(struct super_block *sb, int level)
|
|||||||
#define __sb_writers_acquired(sb, lev) \
|
#define __sb_writers_acquired(sb, lev) \
|
||||||
percpu_rwsem_acquire(&(sb)->s_writers.rw_sem[(lev)-1], 1, _THIS_IP_)
|
percpu_rwsem_acquire(&(sb)->s_writers.rw_sem[(lev)-1], 1, _THIS_IP_)
|
||||||
#define __sb_writers_release(sb, lev) \
|
#define __sb_writers_release(sb, lev) \
|
||||||
percpu_rwsem_release(&(sb)->s_writers.rw_sem[(lev)-1], 1, _THIS_IP_)
|
percpu_rwsem_release(&(sb)->s_writers.rw_sem[(lev)-1], _THIS_IP_)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __sb_write_started - check if sb freeze level is held
|
* __sb_write_started - check if sb freeze level is held
|
||||||
@ -2074,6 +2089,8 @@ struct file_operations {
|
|||||||
#define FOP_DIO_PARALLEL_WRITE ((__force fop_flags_t)(1 << 3))
|
#define FOP_DIO_PARALLEL_WRITE ((__force fop_flags_t)(1 << 3))
|
||||||
/* Contains huge pages */
|
/* Contains huge pages */
|
||||||
#define FOP_HUGE_PAGES ((__force fop_flags_t)(1 << 4))
|
#define FOP_HUGE_PAGES ((__force fop_flags_t)(1 << 4))
|
||||||
|
/* Treat loff_t as unsigned (e.g., /dev/mem) */
|
||||||
|
#define FOP_UNSIGNED_OFFSET ((__force fop_flags_t)(1 << 5))
|
||||||
|
|
||||||
/* Wrap a directory iterator that needs exclusive inode access */
|
/* Wrap a directory iterator that needs exclusive inode access */
|
||||||
int wrap_directory_iterator(struct file *, struct dir_context *,
|
int wrap_directory_iterator(struct file *, struct dir_context *,
|
||||||
@ -2373,8 +2390,6 @@ static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,
|
|||||||
*
|
*
|
||||||
* I_REFERENCED Marks the inode as recently references on the LRU list.
|
* I_REFERENCED Marks the inode as recently references on the LRU list.
|
||||||
*
|
*
|
||||||
* I_DIO_WAKEUP Never set. Only used as a key for wait_on_bit().
|
|
||||||
*
|
|
||||||
* I_WB_SWITCH Cgroup bdi_writeback switching in progress. Used to
|
* I_WB_SWITCH Cgroup bdi_writeback switching in progress. Used to
|
||||||
* synchronize competing switching instances and to tell
|
* synchronize competing switching instances and to tell
|
||||||
* wb stat updates to grab the i_pages lock. See
|
* wb stat updates to grab the i_pages lock. See
|
||||||
@ -2397,30 +2412,32 @@ static inline void kiocb_clone(struct kiocb *kiocb, struct kiocb *kiocb_src,
|
|||||||
* i_count.
|
* i_count.
|
||||||
*
|
*
|
||||||
* Q: What is the difference between I_WILL_FREE and I_FREEING?
|
* Q: What is the difference between I_WILL_FREE and I_FREEING?
|
||||||
|
*
|
||||||
|
* __I_{SYNC,NEW,LRU_ISOLATING} are used to derive unique addresses to wait
|
||||||
|
* upon. There's one free address left.
|
||||||
*/
|
*/
|
||||||
#define I_DIRTY_SYNC (1 << 0)
|
#define __I_NEW 0
|
||||||
#define I_DIRTY_DATASYNC (1 << 1)
|
|
||||||
#define I_DIRTY_PAGES (1 << 2)
|
|
||||||
#define __I_NEW 3
|
|
||||||
#define I_NEW (1 << __I_NEW)
|
#define I_NEW (1 << __I_NEW)
|
||||||
#define I_WILL_FREE (1 << 4)
|
#define __I_SYNC 1
|
||||||
#define I_FREEING (1 << 5)
|
|
||||||
#define I_CLEAR (1 << 6)
|
|
||||||
#define __I_SYNC 7
|
|
||||||
#define I_SYNC (1 << __I_SYNC)
|
#define I_SYNC (1 << __I_SYNC)
|
||||||
#define I_REFERENCED (1 << 8)
|
#define __I_LRU_ISOLATING 2
|
||||||
#define __I_DIO_WAKEUP 9
|
#define I_LRU_ISOLATING (1 << __I_LRU_ISOLATING)
|
||||||
#define I_DIO_WAKEUP (1 << __I_DIO_WAKEUP)
|
|
||||||
|
#define I_DIRTY_SYNC (1 << 3)
|
||||||
|
#define I_DIRTY_DATASYNC (1 << 4)
|
||||||
|
#define I_DIRTY_PAGES (1 << 5)
|
||||||
|
#define I_WILL_FREE (1 << 6)
|
||||||
|
#define I_FREEING (1 << 7)
|
||||||
|
#define I_CLEAR (1 << 8)
|
||||||
|
#define I_REFERENCED (1 << 9)
|
||||||
#define I_LINKABLE (1 << 10)
|
#define I_LINKABLE (1 << 10)
|
||||||
#define I_DIRTY_TIME (1 << 11)
|
#define I_DIRTY_TIME (1 << 11)
|
||||||
#define I_WB_SWITCH (1 << 13)
|
#define I_WB_SWITCH (1 << 12)
|
||||||
#define I_OVL_INUSE (1 << 14)
|
#define I_OVL_INUSE (1 << 13)
|
||||||
#define I_CREATING (1 << 15)
|
#define I_CREATING (1 << 14)
|
||||||
#define I_DONTCACHE (1 << 16)
|
#define I_DONTCACHE (1 << 15)
|
||||||
#define I_SYNC_QUEUED (1 << 17)
|
#define I_SYNC_QUEUED (1 << 16)
|
||||||
#define I_PINNING_NETFS_WB (1 << 18)
|
#define I_PINNING_NETFS_WB (1 << 17)
|
||||||
#define __I_LRU_ISOLATING 19
|
|
||||||
#define I_LRU_ISOLATING (1 << __I_LRU_ISOLATING)
|
|
||||||
|
|
||||||
#define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC)
|
#define I_DIRTY_INODE (I_DIRTY_SYNC | I_DIRTY_DATASYNC)
|
||||||
#define I_DIRTY (I_DIRTY_INODE | I_DIRTY_PAGES)
|
#define I_DIRTY (I_DIRTY_INODE | I_DIRTY_PAGES)
|
||||||
@ -2554,10 +2571,17 @@ struct super_block *sget(struct file_system_type *type,
|
|||||||
struct super_block *sget_dev(struct fs_context *fc, dev_t dev);
|
struct super_block *sget_dev(struct fs_context *fc, dev_t dev);
|
||||||
|
|
||||||
/* Alas, no aliases. Too much hassle with bringing module.h everywhere */
|
/* Alas, no aliases. Too much hassle with bringing module.h everywhere */
|
||||||
#define fops_get(fops) \
|
#define fops_get(fops) ({ \
|
||||||
(((fops) && try_module_get((fops)->owner) ? (fops) : NULL))
|
const struct file_operations *_fops = (fops); \
|
||||||
#define fops_put(fops) \
|
(((_fops) && try_module_get((_fops)->owner) ? (_fops) : NULL)); \
|
||||||
do { if (fops) module_put((fops)->owner); } while(0)
|
})
|
||||||
|
|
||||||
|
#define fops_put(fops) ({ \
|
||||||
|
const struct file_operations *_fops = (fops); \
|
||||||
|
if (_fops) \
|
||||||
|
module_put((_fops)->owner); \
|
||||||
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This one is to be used *ONLY* from ->open() instances.
|
* This one is to be used *ONLY* from ->open() instances.
|
||||||
* fops must be non-NULL, pinned down *and* module dependencies
|
* fops must be non-NULL, pinned down *and* module dependencies
|
||||||
@ -3220,7 +3244,9 @@ static inline ssize_t blockdev_direct_IO(struct kiocb *iocb,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool inode_dio_finished(const struct inode *inode);
|
||||||
void inode_dio_wait(struct inode *inode);
|
void inode_dio_wait(struct inode *inode);
|
||||||
|
void inode_dio_wait_interruptible(struct inode *inode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* inode_dio_begin - signal start of a direct I/O requests
|
* inode_dio_begin - signal start of a direct I/O requests
|
||||||
@ -3244,7 +3270,7 @@ static inline void inode_dio_begin(struct inode *inode)
|
|||||||
static inline void inode_dio_end(struct inode *inode)
|
static inline void inode_dio_end(struct inode *inode)
|
||||||
{
|
{
|
||||||
if (atomic_dec_and_test(&inode->i_dio_count))
|
if (atomic_dec_and_test(&inode->i_dio_count))
|
||||||
wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);
|
wake_up_var(&inode->i_dio_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void inode_set_flags(struct inode *inode, unsigned int flags,
|
extern void inode_set_flags(struct inode *inode, unsigned int flags,
|
||||||
|
@ -18,12 +18,6 @@ static inline int path_equal(const struct path *path1, const struct path *path2)
|
|||||||
return path1->mnt == path2->mnt && path1->dentry == path2->dentry;
|
return path1->mnt == path2->mnt && path1->dentry == path2->dentry;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void path_put_init(struct path *path)
|
|
||||||
{
|
|
||||||
path_put(path);
|
|
||||||
*path = (struct path) { };
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cleanup macro for use with __free(path_put). Avoids dereference and
|
* Cleanup macro for use with __free(path_put). Avoids dereference and
|
||||||
* copying @path unlike DEFINE_FREE(). path_put() will handle the empty
|
* copying @path unlike DEFINE_FREE(). path_put() will handle the empty
|
||||||
|
@ -145,7 +145,7 @@ extern void percpu_free_rwsem(struct percpu_rw_semaphore *);
|
|||||||
#define percpu_rwsem_assert_held(sem) lockdep_assert_held(sem)
|
#define percpu_rwsem_assert_held(sem) lockdep_assert_held(sem)
|
||||||
|
|
||||||
static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem,
|
static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem,
|
||||||
bool read, unsigned long ip)
|
unsigned long ip)
|
||||||
{
|
{
|
||||||
lock_release(&sem->dep_map, ip);
|
lock_release(&sem->dep_map, ip);
|
||||||
}
|
}
|
||||||
|
@ -870,7 +870,7 @@ asmlinkage long sys_fanotify_mark(int fanotify_fd, unsigned int flags,
|
|||||||
#endif
|
#endif
|
||||||
asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name,
|
asmlinkage long sys_name_to_handle_at(int dfd, const char __user *name,
|
||||||
struct file_handle __user *handle,
|
struct file_handle __user *handle,
|
||||||
int __user *mnt_id, int flag);
|
void __user *mnt_id, int flag);
|
||||||
asmlinkage long sys_open_by_handle_at(int mountdirfd,
|
asmlinkage long sys_open_by_handle_at(int mountdirfd,
|
||||||
struct file_handle __user *handle,
|
struct file_handle __user *handle,
|
||||||
int flags);
|
int flags);
|
||||||
|
@ -21,9 +21,11 @@ struct uid_gid_extent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct uid_gid_map { /* 64 bytes -- 1 cache line */
|
struct uid_gid_map { /* 64 bytes -- 1 cache line */
|
||||||
u32 nr_extents;
|
|
||||||
union {
|
union {
|
||||||
struct uid_gid_extent extent[UID_GID_MAP_MAX_BASE_EXTENTS];
|
struct {
|
||||||
|
struct uid_gid_extent extent[UID_GID_MAP_MAX_BASE_EXTENTS];
|
||||||
|
u32 nr_extents;
|
||||||
|
};
|
||||||
struct {
|
struct {
|
||||||
struct uid_gid_extent *forward;
|
struct uid_gid_extent *forward;
|
||||||
struct uid_gid_extent *reverse;
|
struct uid_gid_extent *reverse;
|
||||||
|
@ -200,7 +200,8 @@ void inode_io_list_del(struct inode *inode);
|
|||||||
/* writeback.h requires fs.h; it, too, is not included from here. */
|
/* writeback.h requires fs.h; it, too, is not included from here. */
|
||||||
static inline void wait_on_inode(struct inode *inode)
|
static inline void wait_on_inode(struct inode *inode)
|
||||||
{
|
{
|
||||||
wait_on_bit(&inode->i_state, __I_NEW, TASK_UNINTERRUPTIBLE);
|
wait_var_event(inode_state_wait_address(inode, __I_NEW),
|
||||||
|
!(READ_ONCE(inode->i_state) & I_NEW));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_CGROUP_WRITEBACK
|
#ifdef CONFIG_CGROUP_WRITEBACK
|
||||||
@ -217,7 +218,7 @@ void wbc_account_cgroup_owner(struct writeback_control *wbc, struct page *page,
|
|||||||
size_t bytes);
|
size_t bytes);
|
||||||
int cgroup_writeback_by_id(u64 bdi_id, int memcg_id,
|
int cgroup_writeback_by_id(u64 bdi_id, int memcg_id,
|
||||||
enum wb_reason reason, struct wb_completion *done);
|
enum wb_reason reason, struct wb_completion *done);
|
||||||
void cgroup_writeback_umount(void);
|
void cgroup_writeback_umount(struct super_block *sb);
|
||||||
bool cleanup_offline_cgwb(struct bdi_writeback *wb);
|
bool cleanup_offline_cgwb(struct bdi_writeback *wb);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -324,7 +325,7 @@ static inline void wbc_account_cgroup_owner(struct writeback_control *wbc,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void cgroup_writeback_umount(void)
|
static inline void cgroup_writeback_umount(struct super_block *sb)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,15 @@
|
|||||||
{I_CLEAR, "I_CLEAR"}, \
|
{I_CLEAR, "I_CLEAR"}, \
|
||||||
{I_SYNC, "I_SYNC"}, \
|
{I_SYNC, "I_SYNC"}, \
|
||||||
{I_DIRTY_TIME, "I_DIRTY_TIME"}, \
|
{I_DIRTY_TIME, "I_DIRTY_TIME"}, \
|
||||||
{I_REFERENCED, "I_REFERENCED"} \
|
{I_REFERENCED, "I_REFERENCED"}, \
|
||||||
|
{I_LINKABLE, "I_LINKABLE"}, \
|
||||||
|
{I_WB_SWITCH, "I_WB_SWITCH"}, \
|
||||||
|
{I_OVL_INUSE, "I_OVL_INUSE"}, \
|
||||||
|
{I_CREATING, "I_CREATING"}, \
|
||||||
|
{I_DONTCACHE, "I_DONTCACHE"}, \
|
||||||
|
{I_SYNC_QUEUED, "I_SYNC_QUEUED"}, \
|
||||||
|
{I_PINNING_NETFS_WB, "I_PINNING_NETFS_WB"}, \
|
||||||
|
{I_LRU_ISOLATING, "I_LRU_ISOLATING"} \
|
||||||
)
|
)
|
||||||
|
|
||||||
/* enums need to be exported to user space */
|
/* enums need to be exported to user space */
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#define AUTOFS_MIN_PROTO_VERSION 3
|
#define AUTOFS_MIN_PROTO_VERSION 3
|
||||||
#define AUTOFS_MAX_PROTO_VERSION 5
|
#define AUTOFS_MAX_PROTO_VERSION 5
|
||||||
|
|
||||||
#define AUTOFS_PROTO_SUBVERSION 5
|
#define AUTOFS_PROTO_SUBVERSION 6
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The wait_queue_token (autofs_wqt_t) is part of a structure which is passed
|
* The wait_queue_token (autofs_wqt_t) is part of a structure which is passed
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3)
|
#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3)
|
||||||
|
|
||||||
|
/* Was the file just created? */
|
||||||
|
#define F_CREATED_QUERY (F_LINUX_SPECIFIC_BASE + 4)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cancel a blocking posix lock; internal use only until we expose an
|
* Cancel a blocking posix lock; internal use only until we expose an
|
||||||
* asynchronous lock api to userspace:
|
* asynchronous lock api to userspace:
|
||||||
@ -87,37 +90,70 @@
|
|||||||
#define DN_ATTRIB 0x00000020 /* File changed attibutes */
|
#define DN_ATTRIB 0x00000020 /* File changed attibutes */
|
||||||
#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
|
#define DN_MULTISHOT 0x80000000 /* Don't remove notifier */
|
||||||
|
|
||||||
|
#define AT_FDCWD -100 /* Special value for dirfd used to
|
||||||
|
indicate openat should use the
|
||||||
|
current working directory. */
|
||||||
|
|
||||||
|
|
||||||
|
/* Generic flags for the *at(2) family of syscalls. */
|
||||||
|
|
||||||
|
/* Reserved for per-syscall flags 0xff. */
|
||||||
|
#define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic
|
||||||
|
links. */
|
||||||
|
/* Reserved for per-syscall flags 0x200 */
|
||||||
|
#define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */
|
||||||
|
#define AT_NO_AUTOMOUNT 0x800 /* Suppress terminal automount
|
||||||
|
traversal. */
|
||||||
|
#define AT_EMPTY_PATH 0x1000 /* Allow empty relative
|
||||||
|
pathname to operate on dirfd
|
||||||
|
directly. */
|
||||||
/*
|
/*
|
||||||
* The constants AT_REMOVEDIR and AT_EACCESS have the same value. AT_EACCESS is
|
* These flags are currently statx(2)-specific, but they could be made generic
|
||||||
* meaningful only to faccessat, while AT_REMOVEDIR is meaningful only to
|
* in the future and so they should not be used for other per-syscall flags.
|
||||||
* unlinkat. The two functions do completely different things and therefore,
|
|
||||||
* the flags can be allowed to overlap. For example, passing AT_REMOVEDIR to
|
|
||||||
* faccessat would be undefined behavior and thus treating it equivalent to
|
|
||||||
* AT_EACCESS is valid undefined behavior.
|
|
||||||
*/
|
*/
|
||||||
#define AT_FDCWD -100 /* Special value used to indicate
|
#define AT_STATX_SYNC_TYPE 0x6000 /* Type of synchronisation required from statx() */
|
||||||
openat should use the current
|
#define AT_STATX_SYNC_AS_STAT 0x0000 /* - Do whatever stat() does */
|
||||||
working directory. */
|
#define AT_STATX_FORCE_SYNC 0x2000 /* - Force the attributes to be sync'd with the server */
|
||||||
#define AT_SYMLINK_NOFOLLOW 0x100 /* Do not follow symbolic links. */
|
#define AT_STATX_DONT_SYNC 0x4000 /* - Don't sync attributes with the server */
|
||||||
|
|
||||||
|
#define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Per-syscall flags for the *at(2) family of syscalls.
|
||||||
|
*
|
||||||
|
* These are flags that are so syscall-specific that a user passing these flags
|
||||||
|
* to the wrong syscall is so "clearly wrong" that we can safely call such
|
||||||
|
* usage "undefined behaviour".
|
||||||
|
*
|
||||||
|
* For example, the constants AT_REMOVEDIR and AT_EACCESS have the same value.
|
||||||
|
* AT_EACCESS is meaningful only to faccessat, while AT_REMOVEDIR is meaningful
|
||||||
|
* only to unlinkat. The two functions do completely different things and
|
||||||
|
* therefore, the flags can be allowed to overlap. For example, passing
|
||||||
|
* AT_REMOVEDIR to faccessat would be undefined behavior and thus treating it
|
||||||
|
* equivalent to AT_EACCESS is valid undefined behavior.
|
||||||
|
*
|
||||||
|
* Note for implementers: When picking a new per-syscall AT_* flag, try to
|
||||||
|
* reuse already existing flags first. This leaves us with as many unused bits
|
||||||
|
* as possible, so we can use them for generic bits in the future if necessary.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Flags for renameat2(2) (must match legacy RENAME_* flags). */
|
||||||
|
#define AT_RENAME_NOREPLACE 0x0001
|
||||||
|
#define AT_RENAME_EXCHANGE 0x0002
|
||||||
|
#define AT_RENAME_WHITEOUT 0x0004
|
||||||
|
|
||||||
|
/* Flag for faccessat(2). */
|
||||||
#define AT_EACCESS 0x200 /* Test access permitted for
|
#define AT_EACCESS 0x200 /* Test access permitted for
|
||||||
effective IDs, not real IDs. */
|
effective IDs, not real IDs. */
|
||||||
|
/* Flag for unlinkat(2). */
|
||||||
#define AT_REMOVEDIR 0x200 /* Remove directory instead of
|
#define AT_REMOVEDIR 0x200 /* Remove directory instead of
|
||||||
unlinking file. */
|
unlinking file. */
|
||||||
#define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic links. */
|
/* Flags for name_to_handle_at(2). */
|
||||||
#define AT_NO_AUTOMOUNT 0x800 /* Suppress terminal automount traversal */
|
#define AT_HANDLE_FID 0x200 /* File handle is needed to compare
|
||||||
#define AT_EMPTY_PATH 0x1000 /* Allow empty relative pathname */
|
object identity and may not be
|
||||||
|
usable with open_by_handle_at(2). */
|
||||||
|
#define AT_HANDLE_MNT_ID_UNIQUE 0x001 /* Return the u64 unique mount ID. */
|
||||||
|
|
||||||
#define AT_STATX_SYNC_TYPE 0x6000 /* Type of synchronisation required from statx() */
|
|
||||||
#define AT_STATX_SYNC_AS_STAT 0x0000 /* - Do whatever stat() does */
|
|
||||||
#define AT_STATX_FORCE_SYNC 0x2000 /* - Force the attributes to be sync'd with the server */
|
|
||||||
#define AT_STATX_DONT_SYNC 0x4000 /* - Don't sync attributes with the server */
|
|
||||||
|
|
||||||
#define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */
|
|
||||||
|
|
||||||
/* Flags for name_to_handle_at(2). We reuse AT_ flag space to save bits... */
|
|
||||||
#define AT_HANDLE_FID AT_REMOVEDIR /* file handle is needed to
|
|
||||||
compare object identity and may not
|
|
||||||
be usable to open_by_handle_at(2) */
|
|
||||||
#if defined(__KERNEL__)
|
#if defined(__KERNEL__)
|
||||||
#define AT_GETATTR_NOSEC 0x80000000
|
#define AT_GETATTR_NOSEC 0x80000000
|
||||||
#endif
|
#endif
|
||||||
|
@ -36,33 +36,33 @@ EXPORT_SYMBOL_GPL(init_binfmt_misc);
|
|||||||
*/
|
*/
|
||||||
struct user_namespace init_user_ns = {
|
struct user_namespace init_user_ns = {
|
||||||
.uid_map = {
|
.uid_map = {
|
||||||
.nr_extents = 1,
|
|
||||||
{
|
{
|
||||||
.extent[0] = {
|
.extent[0] = {
|
||||||
.first = 0,
|
.first = 0,
|
||||||
.lower_first = 0,
|
.lower_first = 0,
|
||||||
.count = 4294967295U,
|
.count = 4294967295U,
|
||||||
},
|
},
|
||||||
|
.nr_extents = 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.gid_map = {
|
.gid_map = {
|
||||||
.nr_extents = 1,
|
|
||||||
{
|
{
|
||||||
.extent[0] = {
|
.extent[0] = {
|
||||||
.first = 0,
|
.first = 0,
|
||||||
.lower_first = 0,
|
.lower_first = 0,
|
||||||
.count = 4294967295U,
|
.count = 4294967295U,
|
||||||
},
|
},
|
||||||
|
.nr_extents = 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.projid_map = {
|
.projid_map = {
|
||||||
.nr_extents = 1,
|
|
||||||
{
|
{
|
||||||
.extent[0] = {
|
.extent[0] = {
|
||||||
.first = 0,
|
.first = 0,
|
||||||
.lower_first = 0,
|
.lower_first = 0,
|
||||||
.count = 4294967295U,
|
.count = 4294967295U,
|
||||||
},
|
},
|
||||||
|
.nr_extents = 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.ns.count = REFCOUNT_INIT(3),
|
.ns.count = REFCOUNT_INIT(3),
|
||||||
|
@ -1229,7 +1229,7 @@ static inline u64 file_mmap_size_max(struct file *file, struct inode *inode)
|
|||||||
return MAX_LFS_FILESIZE;
|
return MAX_LFS_FILESIZE;
|
||||||
|
|
||||||
/* Special "we do even unsigned file positions" case */
|
/* Special "we do even unsigned file positions" case */
|
||||||
if (file->f_mode & FMODE_UNSIGNED_OFFSET)
|
if (file->f_op->fop_flags & FOP_UNSIGNED_OFFSET)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Yes, random drivers might want more. But I'm tired of buggy drivers */
|
/* Yes, random drivers might want more. But I'm tired of buggy drivers */
|
||||||
|
@ -26,6 +26,10 @@
|
|||||||
#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3)
|
#define F_DUPFD_QUERY (F_LINUX_SPECIFIC_BASE + 3)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef F_CREATED_QUERY
|
||||||
|
#define F_CREATED_QUERY (F_LINUX_SPECIFIC_BASE + 4)
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline int sys_close_range(unsigned int fd, unsigned int max_fd,
|
static inline int sys_close_range(unsigned int fd, unsigned int max_fd,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
@ -624,4 +628,39 @@ TEST(close_range_bitmap_corruption)
|
|||||||
EXPECT_EQ(0, WEXITSTATUS(status));
|
EXPECT_EQ(0, WEXITSTATUS(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(fcntl_created)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 101; i++) {
|
||||||
|
int fd;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
|
||||||
|
ASSERT_GE(fd, 0) {
|
||||||
|
if (errno == ENOENT)
|
||||||
|
SKIP(return,
|
||||||
|
"Skipping test since /dev/null does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We didn't create "/dev/null". */
|
||||||
|
EXPECT_EQ(fcntl(fd, F_CREATED_QUERY, 0), 0);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
sprintf(path, "aaaa_%d", i);
|
||||||
|
fd = open(path, O_CREAT | O_RDONLY | O_CLOEXEC, 0600);
|
||||||
|
ASSERT_GE(fd, 0);
|
||||||
|
|
||||||
|
/* We created "aaaa_%d". */
|
||||||
|
EXPECT_EQ(fcntl(fd, F_CREATED_QUERY, 0), 1);
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||||
|
ASSERT_GE(fd, 0);
|
||||||
|
|
||||||
|
/* We're opening it again, so no positive creation check. */
|
||||||
|
EXPECT_EQ(fcntl(fd, F_CREATED_QUERY, 0), 0);
|
||||||
|
close(fd);
|
||||||
|
unlink(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_HARNESS_MAIN
|
TEST_HARNESS_MAIN
|
||||||
|
Loading…
Reference in New Issue
Block a user