mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 02:36:02 +00:00
v6.4/vfs.misc
-----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQRAhzRXHqcMeLMyaSiRxhvAZXjcogUCZEJxiQAKCRCRxhvAZXjc oj4jAP4pt/SuHdZQzv9k2j+UA4hpDmwt7UUWhLxtycQrt9oEoQD/U9hCwnYY9Ksj 5TSZiK/OGyoslmF1zfAC+1R2crgr3A0= =PpBS -----END PGP SIGNATURE----- Merge tag 'v6.4/vfs.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull misc vfs updates from Christian Brauner: "This contains a pile of various smaller fixes. Most of them aren't very interesting so this just highlights things worth mentioning: - Various filesystems contained the same little helper to convert from the mode of a dentry to the DT_* type of that dentry. They have now all been switched to rely on the generic fs_umode_to_dtype() helper. All custom helpers are removed (Jeff) - Fsnotify now reports ACCESS and MODIFY events for splice (Chung-Chiang Cheng) - After converting timerfd a long time ago to rely on wait_event_interruptible_*() apis, convert eventfd as well. This removes the complex open-coded wait code (Wen Yang) - Simplify sysctl registration for devpts, avoiding the declaration of two tables. Instead, just use a prefixed path with register_sysctl() (Luis) - The setattr_should_drop_sgid() helper is now exported so NFS can use it. By switching NFS to this helper an NFS setgid inheritance bug is fixed (me)" * tag 'v6.4/vfs.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: fs: hfsplus: remove WARN_ON() from hfsplus_cat_{read,write}_inode() pnode: pass mountpoint directly eventfd: use wait_event_interruptible_locked_irq() helper splice: report related fsnotify events fs: consolidate duplicate dt_type helpers nfs: use vfs setgid helper Update relatime comments to include equality fs/buffer: Remove redundant assignment to err fs_context: drop the unused lsm_flags member fs/namespace: fnic: Switch to use %ptTd Documentation: update idmappings.rst devpts: simplify two-level sysctl registration for pty_kern_table eventpoll: align comment with nested epoll limitation
This commit is contained in:
commit
e2eff52ce5
@ -241,7 +241,7 @@ according to the filesystem's idmapping as this would give the wrong owner if
|
||||
the caller is using an idmapping.
|
||||
|
||||
So the kernel will map the id back up in the idmapping of the caller. Let's
|
||||
assume the caller has the slighly unconventional idmapping
|
||||
assume the caller has the somewhat unconventional idmapping
|
||||
``u3000:k20000:r10000`` then ``k21000`` would map back up to ``u4000``.
|
||||
Consequently the user would see that this file is owned by ``u4000``.
|
||||
|
||||
@ -320,6 +320,10 @@ and equally wrong::
|
||||
from_kuid(u20000:k0:r10000, u1000) = k21000
|
||||
~~~~~
|
||||
|
||||
Since userspace ids have type ``uid_t`` and ``gid_t`` and kernel ids have type
|
||||
``kuid_t`` and ``kgid_t`` the compiler will throw an error when they are
|
||||
conflated. So the two examples above would cause a compilation failure.
|
||||
|
||||
Idmappings when creating filesystem objects
|
||||
-------------------------------------------
|
||||
|
||||
@ -623,42 +627,105 @@ privileged users in the initial user namespace.
|
||||
However, it is perfectly possible to combine idmapped mounts with filesystems
|
||||
mountable inside user namespaces. We will touch on this further below.
|
||||
|
||||
Filesystem types vs idmapped mount types
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
With the introduction of idmapped mounts we need to distinguish between
|
||||
filesystem ownership and mount ownership of a VFS object such as an inode. The
|
||||
owner of a inode might be different when looked at from a filesystem
|
||||
perspective than when looked at from an idmapped mount. Such fundamental
|
||||
conceptual distinctions should almost always be clearly expressed in the code.
|
||||
So, to distinguish idmapped mount ownership from filesystem ownership separate
|
||||
types have been introduced.
|
||||
|
||||
If a uid or gid has been generated using the filesystem or caller's idmapping
|
||||
then we will use the ``kuid_t`` and ``kgid_t`` types. However, if a uid or gid
|
||||
has been generated using a mount idmapping then we will be using the dedicated
|
||||
``vfsuid_t`` and ``vfsgid_t`` types.
|
||||
|
||||
All VFS helpers that generate or take uids and gids as arguments use the
|
||||
``vfsuid_t`` and ``vfsgid_t`` types and we will be able to rely on the compiler
|
||||
to catch errors that originate from conflating filesystem and VFS uids and gids.
|
||||
|
||||
The ``vfsuid_t`` and ``vfsgid_t`` types are often mapped from and to ``kuid_t``
|
||||
and ``kgid_t`` types similar how ``kuid_t`` and ``kgid_t`` types are mapped
|
||||
from and to ``uid_t`` and ``gid_t`` types::
|
||||
|
||||
uid_t <--> kuid_t <--> vfsuid_t
|
||||
gid_t <--> kgid_t <--> vfsgid_t
|
||||
|
||||
Whenever we report ownership based on a ``vfsuid_t`` or ``vfsgid_t`` type,
|
||||
e.g., during ``stat()``, or store ownership information in a shared VFS object
|
||||
based on a ``vfsuid_t`` or ``vfsgid_t`` type, e.g., during ``chown()`` we can
|
||||
use the ``vfsuid_into_kuid()`` and ``vfsgid_into_kgid()`` helpers.
|
||||
|
||||
To illustrate why this helper currently exists, consider what happens when we
|
||||
change ownership of an inode from an idmapped mount. After we generated
|
||||
a ``vfsuid_t`` or ``vfsgid_t`` based on the mount idmapping we later commit to
|
||||
this ``vfsuid_t`` or ``vfsgid_t`` to become the new filesytem wide ownership.
|
||||
Thus, we are turning the ``vfsuid_t`` or ``vfsgid_t`` into a global ``kuid_t``
|
||||
or ``kgid_t``. And this can be done by using ``vfsuid_into_kuid()`` and
|
||||
``vfsgid_into_kgid()``.
|
||||
|
||||
Note, whenever a shared VFS object, e.g., a cached ``struct inode`` or a cached
|
||||
``struct posix_acl``, stores ownership information a filesystem or "global"
|
||||
``kuid_t`` and ``kgid_t`` must be used. Ownership expressed via ``vfsuid_t``
|
||||
and ``vfsgid_t`` is specific to an idmapped mount.
|
||||
|
||||
We already noted that ``vfsuid_t`` and ``vfsgid_t`` types are generated based
|
||||
on mount idmappings whereas ``kuid_t`` and ``kgid_t`` types are generated based
|
||||
on filesystem idmappings. To prevent abusing filesystem idmappings to generate
|
||||
``vfsuid_t`` or ``vfsgid_t`` types or mount idmappings to generate ``kuid_t``
|
||||
or ``kgid_t`` types filesystem idmappings and mount idmappings are different
|
||||
types as well.
|
||||
|
||||
All helpers that map to or from ``vfsuid_t`` and ``vfsgid_t`` types require
|
||||
a mount idmapping to be passed which is of type ``struct mnt_idmap``. Passing
|
||||
a filesystem or caller idmapping will cause a compilation error.
|
||||
|
||||
Similar to how we prefix all userspace ids in this document with ``u`` and all
|
||||
kernel ids with ``k`` we will prefix all VFS ids with ``v``. So a mount
|
||||
idmapping will be written as: ``u0:v10000:r10000``.
|
||||
|
||||
Remapping helpers
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Idmapping functions were added that translate between idmappings. They make use
|
||||
of the remapping algorithm we've introduced earlier. We're going to look at
|
||||
two:
|
||||
of the remapping algorithm we've introduced earlier. We're going to look at:
|
||||
|
||||
- ``i_uid_into_mnt()`` and ``i_gid_into_mnt()``
|
||||
- ``i_uid_into_vfsuid()`` and ``i_gid_into_vfsgid()``
|
||||
|
||||
The ``i_*id_into_mnt()`` functions translate filesystem's kernel ids into
|
||||
kernel ids in the mount's idmapping::
|
||||
The ``i_*id_into_vfs*id()`` functions translate filesystem's kernel ids into
|
||||
VFS ids in the mount's idmapping::
|
||||
|
||||
/* Map the filesystem's kernel id up into a userspace id in the filesystem's idmapping. */
|
||||
from_kuid(filesystem, kid) = uid
|
||||
|
||||
/* Map the filesystem's userspace id down ito a kernel id in the mount's idmapping. */
|
||||
/* Map the filesystem's userspace id down ito a VFS id in the mount's idmapping. */
|
||||
make_kuid(mount, uid) = kuid
|
||||
|
||||
- ``mapped_fsuid()`` and ``mapped_fsgid()``
|
||||
|
||||
The ``mapped_fs*id()`` functions translate the caller's kernel ids into
|
||||
kernel ids in the filesystem's idmapping. This translation is achieved by
|
||||
remapping the caller's kernel ids using the mount's idmapping::
|
||||
remapping the caller's VFS ids using the mount's idmapping::
|
||||
|
||||
/* Map the caller's kernel id up into a userspace id in the mount's idmapping. */
|
||||
/* Map the caller's VFS id up into a userspace id in the mount's idmapping. */
|
||||
from_kuid(mount, kid) = uid
|
||||
|
||||
/* Map the mount's userspace id down into a kernel id in the filesystem's idmapping. */
|
||||
make_kuid(filesystem, uid) = kuid
|
||||
|
||||
- ``vfsuid_into_kuid()`` and ``vfsgid_into_kgid()``
|
||||
|
||||
Whenever
|
||||
|
||||
Note that these two functions invert each other. Consider the following
|
||||
idmappings::
|
||||
|
||||
caller idmapping: u0:k10000:r10000
|
||||
filesystem idmapping: u0:k20000:r10000
|
||||
mount idmapping: u0:k10000:r10000
|
||||
mount idmapping: u0:v10000:r10000
|
||||
|
||||
Assume a file owned by ``u1000`` is read from disk. The filesystem maps this id
|
||||
to ``k21000`` according to its idmapping. This is what is stored in the
|
||||
@ -669,20 +736,21 @@ would usually simply use the crossmapping algorithm and map the filesystem's
|
||||
kernel id up to a userspace id in the caller's idmapping.
|
||||
|
||||
But when the caller is accessing the file on an idmapped mount the kernel will
|
||||
first call ``i_uid_into_mnt()`` thereby translating the filesystem's kernel id
|
||||
into a kernel id in the mount's idmapping::
|
||||
first call ``i_uid_into_vfsuid()`` thereby translating the filesystem's kernel
|
||||
id into a VFS id in the mount's idmapping::
|
||||
|
||||
i_uid_into_mnt(k21000):
|
||||
i_uid_into_vfsuid(k21000):
|
||||
/* Map the filesystem's kernel id up into a userspace id. */
|
||||
from_kuid(u0:k20000:r10000, k21000) = u1000
|
||||
|
||||
/* Map the filesystem's userspace id down ito a kernel id in the mount's idmapping. */
|
||||
make_kuid(u0:k10000:r10000, u1000) = k11000
|
||||
/* Map the filesystem's userspace id down into a VFS id in the mount's idmapping. */
|
||||
make_kuid(u0:v10000:r10000, u1000) = v11000
|
||||
|
||||
Finally, when the kernel reports the owner to the caller it will turn the
|
||||
kernel id in the mount's idmapping into a userspace id in the caller's
|
||||
VFS id in the mount's idmapping into a userspace id in the caller's
|
||||
idmapping::
|
||||
|
||||
k11000 = vfsuid_into_kuid(v11000)
|
||||
from_kuid(u0:k10000:r10000, k11000) = u1000
|
||||
|
||||
We can test whether this algorithm really works by verifying what happens when
|
||||
@ -696,18 +764,19 @@ fails.
|
||||
|
||||
But when the caller is accessing the file on an idmapped mount the kernel will
|
||||
first call ``mapped_fs*id()`` thereby translating the caller's kernel id into
|
||||
a kernel id according to the mount's idmapping::
|
||||
a VFS id according to the mount's idmapping::
|
||||
|
||||
mapped_fsuid(k11000):
|
||||
/* Map the caller's kernel id up into a userspace id in the mount's idmapping. */
|
||||
from_kuid(u0:k10000:r10000, k11000) = u1000
|
||||
|
||||
/* Map the mount's userspace id down into a kernel id in the filesystem's idmapping. */
|
||||
make_kuid(u0:k20000:r10000, u1000) = k21000
|
||||
make_kuid(u0:v20000:r10000, u1000) = v21000
|
||||
|
||||
When finally writing to disk the kernel will then map ``k21000`` up into a
|
||||
When finally writing to disk the kernel will then map ``v21000`` up into a
|
||||
userspace id in the filesystem's idmapping::
|
||||
|
||||
k21000 = vfsuid_into_kuid(v21000)
|
||||
from_kuid(u0:k20000:r10000, k21000) = u1000
|
||||
|
||||
As we can see, we end up with an invertible and therefore information
|
||||
@ -725,7 +794,7 @@ Example 2 reconsidered
|
||||
caller id: u1000
|
||||
caller idmapping: u0:k10000:r10000
|
||||
filesystem idmapping: u0:k20000:r10000
|
||||
mount idmapping: u0:k10000:r10000
|
||||
mount idmapping: u0:v10000:r10000
|
||||
|
||||
When the caller is using a non-initial idmapping the common case is to attach
|
||||
the same idmapping to the mount. We now perform three steps:
|
||||
@ -734,12 +803,12 @@ the same idmapping to the mount. We now perform three steps:
|
||||
|
||||
make_kuid(u0:k10000:r10000, u1000) = k11000
|
||||
|
||||
2. Translate the caller's kernel id into a kernel id in the filesystem's
|
||||
2. Translate the caller's VFS id into a kernel id in the filesystem's
|
||||
idmapping::
|
||||
|
||||
mapped_fsuid(k11000):
|
||||
/* Map the kernel id up into a userspace id in the mount's idmapping. */
|
||||
from_kuid(u0:k10000:r10000, k11000) = u1000
|
||||
mapped_fsuid(v11000):
|
||||
/* Map the VFS id up into a userspace id in the mount's idmapping. */
|
||||
from_kuid(u0:v10000:r10000, v11000) = u1000
|
||||
|
||||
/* Map the userspace id down into a kernel id in the filesystem's idmapping. */
|
||||
make_kuid(u0:k20000:r10000, u1000) = k21000
|
||||
@ -759,7 +828,7 @@ Example 3 reconsidered
|
||||
caller id: u1000
|
||||
caller idmapping: u0:k10000:r10000
|
||||
filesystem idmapping: u0:k0:r4294967295
|
||||
mount idmapping: u0:k10000:r10000
|
||||
mount idmapping: u0:v10000:r10000
|
||||
|
||||
The same translation algorithm works with the third example.
|
||||
|
||||
@ -767,12 +836,12 @@ The same translation algorithm works with the third example.
|
||||
|
||||
make_kuid(u0:k10000:r10000, u1000) = k11000
|
||||
|
||||
2. Translate the caller's kernel id into a kernel id in the filesystem's
|
||||
2. Translate the caller's VFS id into a kernel id in the filesystem's
|
||||
idmapping::
|
||||
|
||||
mapped_fsuid(k11000):
|
||||
/* Map the kernel id up into a userspace id in the mount's idmapping. */
|
||||
from_kuid(u0:k10000:r10000, k11000) = u1000
|
||||
mapped_fsuid(v11000):
|
||||
/* Map the VFS id up into a userspace id in the mount's idmapping. */
|
||||
from_kuid(u0:v10000:r10000, v11000) = u1000
|
||||
|
||||
/* Map the userspace id down into a kernel id in the filesystem's idmapping. */
|
||||
make_kuid(u0:k0:r4294967295, u1000) = k1000
|
||||
@ -792,7 +861,7 @@ Example 4 reconsidered
|
||||
file id: u1000
|
||||
caller idmapping: u0:k10000:r10000
|
||||
filesystem idmapping: u0:k0:r4294967295
|
||||
mount idmapping: u0:k10000:r10000
|
||||
mount idmapping: u0:v10000:r10000
|
||||
|
||||
In order to report ownership to userspace the kernel now does three steps using
|
||||
the translation algorithm we introduced earlier:
|
||||
@ -802,17 +871,18 @@ the translation algorithm we introduced earlier:
|
||||
|
||||
make_kuid(u0:k0:r4294967295, u1000) = k1000
|
||||
|
||||
2. Translate the kernel id into a kernel id in the mount's idmapping::
|
||||
2. Translate the kernel id into a VFS id in the mount's idmapping::
|
||||
|
||||
i_uid_into_mnt(k1000):
|
||||
i_uid_into_vfsuid(k1000):
|
||||
/* Map the kernel id up into a userspace id in the filesystem's idmapping. */
|
||||
from_kuid(u0:k0:r4294967295, k1000) = u1000
|
||||
|
||||
/* Map the userspace id down into a kernel id in the mounts's idmapping. */
|
||||
make_kuid(u0:k10000:r10000, u1000) = k11000
|
||||
/* Map the userspace id down into a VFS id in the mounts's idmapping. */
|
||||
make_kuid(u0:v10000:r10000, u1000) = v11000
|
||||
|
||||
3. Map the kernel id up into a userspace id in the caller's idmapping::
|
||||
3. Map the VFS id up into a userspace id in the caller's idmapping::
|
||||
|
||||
k11000 = vfsuid_into_kuid(v11000)
|
||||
from_kuid(u0:k10000:r10000, k11000) = u1000
|
||||
|
||||
Earlier, the caller's kernel id couldn't be crossmapped in the filesystems's
|
||||
@ -828,7 +898,7 @@ Example 5 reconsidered
|
||||
file id: u1000
|
||||
caller idmapping: u0:k10000:r10000
|
||||
filesystem idmapping: u0:k20000:r10000
|
||||
mount idmapping: u0:k10000:r10000
|
||||
mount idmapping: u0:v10000:r10000
|
||||
|
||||
Again, in order to report ownership to userspace the kernel now does three
|
||||
steps using the translation algorithm we introduced earlier:
|
||||
@ -838,17 +908,18 @@ steps using the translation algorithm we introduced earlier:
|
||||
|
||||
make_kuid(u0:k20000:r10000, u1000) = k21000
|
||||
|
||||
2. Translate the kernel id into a kernel id in the mount's idmapping::
|
||||
2. Translate the kernel id into a VFS id in the mount's idmapping::
|
||||
|
||||
i_uid_into_mnt(k21000):
|
||||
i_uid_into_vfsuid(k21000):
|
||||
/* Map the kernel id up into a userspace id in the filesystem's idmapping. */
|
||||
from_kuid(u0:k20000:r10000, k21000) = u1000
|
||||
|
||||
/* Map the userspace id down into a kernel id in the mounts's idmapping. */
|
||||
make_kuid(u0:k10000:r10000, u1000) = k11000
|
||||
/* Map the userspace id down into a VFS id in the mounts's idmapping. */
|
||||
make_kuid(u0:v10000:r10000, u1000) = v11000
|
||||
|
||||
3. Map the kernel id up into a userspace id in the caller's idmapping::
|
||||
3. Map the VFS id up into a userspace id in the caller's idmapping::
|
||||
|
||||
k11000 = vfsuid_into_kuid(v11000)
|
||||
from_kuid(u0:k10000:r10000, k11000) = u1000
|
||||
|
||||
Earlier, the file's kernel id couldn't be crossmapped in the filesystems's
|
||||
@ -899,23 +970,23 @@ from above:::
|
||||
caller id: u1125
|
||||
caller idmapping: u0:k0:r4294967295
|
||||
filesystem idmapping: u0:k0:r4294967295
|
||||
mount idmapping: u1000:k1125:r1
|
||||
mount idmapping: u1000:v1125:r1
|
||||
|
||||
1. Map the caller's userspace ids into kernel ids in the caller's idmapping::
|
||||
|
||||
make_kuid(u0:k0:r4294967295, u1125) = k1125
|
||||
|
||||
2. Translate the caller's kernel id into a kernel id in the filesystem's
|
||||
2. Translate the caller's VFS id into a kernel id in the filesystem's
|
||||
idmapping::
|
||||
|
||||
mapped_fsuid(k1125):
|
||||
/* Map the kernel id up into a userspace id in the mount's idmapping. */
|
||||
from_kuid(u1000:k1125:r1, k1125) = u1000
|
||||
mapped_fsuid(v1125):
|
||||
/* Map the VFS id up into a userspace id in the mount's idmapping. */
|
||||
from_kuid(u1000:v1125:r1, v1125) = u1000
|
||||
|
||||
/* Map the userspace id down into a kernel id in the filesystem's idmapping. */
|
||||
make_kuid(u0:k0:r4294967295, u1000) = k1000
|
||||
|
||||
2. Verify that the caller's kernel ids can be mapped to userspace ids in the
|
||||
2. Verify that the caller's filesystem ids can be mapped to userspace ids in the
|
||||
filesystem's idmapping::
|
||||
|
||||
from_kuid(u0:k0:r4294967295, k1000) = u1000
|
||||
@ -930,24 +1001,25 @@ on their work computer:
|
||||
file id: u1000
|
||||
caller idmapping: u0:k0:r4294967295
|
||||
filesystem idmapping: u0:k0:r4294967295
|
||||
mount idmapping: u1000:k1125:r1
|
||||
mount idmapping: u1000:v1125:r1
|
||||
|
||||
1. Map the userspace id on disk down into a kernel id in the filesystem's
|
||||
idmapping::
|
||||
|
||||
make_kuid(u0:k0:r4294967295, u1000) = k1000
|
||||
|
||||
2. Translate the kernel id into a kernel id in the mount's idmapping::
|
||||
2. Translate the kernel id into a VFS id in the mount's idmapping::
|
||||
|
||||
i_uid_into_mnt(k1000):
|
||||
i_uid_into_vfsuid(k1000):
|
||||
/* Map the kernel id up into a userspace id in the filesystem's idmapping. */
|
||||
from_kuid(u0:k0:r4294967295, k1000) = u1000
|
||||
|
||||
/* Map the userspace id down into a kernel id in the mounts's idmapping. */
|
||||
make_kuid(u1000:k1125:r1, u1000) = k1125
|
||||
/* Map the userspace id down into a VFS id in the mounts's idmapping. */
|
||||
make_kuid(u1000:v1125:r1, u1000) = v1125
|
||||
|
||||
3. Map the kernel id up into a userspace id in the caller's idmapping::
|
||||
3. Map the VFS id up into a userspace id in the caller's idmapping::
|
||||
|
||||
k1125 = vfsuid_into_kuid(v1125)
|
||||
from_kuid(u0:k0:r4294967295, k1125) = u1125
|
||||
|
||||
So ultimately the caller will be reported that the file belongs to ``u1125``
|
||||
|
@ -79,7 +79,6 @@ context. This is represented by the fs_context structure::
|
||||
unsigned int sb_flags;
|
||||
unsigned int sb_flags_mask;
|
||||
unsigned int s_iflags;
|
||||
unsigned int lsm_flags;
|
||||
enum fs_context_purpose purpose:8;
|
||||
...
|
||||
};
|
||||
|
@ -47,6 +47,7 @@ int setattr_should_drop_sgid(struct mnt_idmap *idmap,
|
||||
return ATTR_KILL_SGID;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(setattr_should_drop_sgid);
|
||||
|
||||
/**
|
||||
* setattr_should_drop_suidgid - determine whether the set{g,u}id bit needs to
|
||||
|
@ -2581,7 +2581,7 @@ int block_truncate_page(struct address_space *mapping,
|
||||
struct inode *inode = mapping->host;
|
||||
struct page *page;
|
||||
struct buffer_head *bh;
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
blocksize = i_blocksize(inode);
|
||||
length = offset & (blocksize - 1);
|
||||
@ -2594,9 +2594,8 @@ int block_truncate_page(struct address_space *mapping,
|
||||
iblock = (sector_t)index << (PAGE_SHIFT - inode->i_blkbits);
|
||||
|
||||
page = grab_cache_page(mapping, index);
|
||||
err = -ENOMEM;
|
||||
if (!page)
|
||||
goto out;
|
||||
return -ENOMEM;
|
||||
|
||||
if (!page_has_buffers(page))
|
||||
create_empty_buffers(page, blocksize, 0);
|
||||
@ -2610,7 +2609,6 @@ int block_truncate_page(struct address_space *mapping,
|
||||
pos += blocksize;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
if (!buffer_mapped(bh)) {
|
||||
WARN_ON(bh->b_size != blocksize);
|
||||
err = get_block(inode, iblock, bh, 0);
|
||||
@ -2634,12 +2632,11 @@ int block_truncate_page(struct address_space *mapping,
|
||||
|
||||
zero_user(page, offset, length);
|
||||
mark_buffer_dirty(bh);
|
||||
err = 0;
|
||||
|
||||
unlock:
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
out:
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(block_truncate_page);
|
||||
|
@ -1599,12 +1599,6 @@ static int configfs_dir_close(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Relationship between s_mode and the DT_xxx types */
|
||||
static inline unsigned char dt_type(struct configfs_dirent *sd)
|
||||
{
|
||||
return (sd->s_mode >> 12) & 15;
|
||||
}
|
||||
|
||||
static int configfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
{
|
||||
struct dentry *dentry = file->f_path.dentry;
|
||||
@ -1654,7 +1648,8 @@ static int configfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
name = configfs_get_name(next);
|
||||
len = strlen(name);
|
||||
|
||||
if (!dir_emit(ctx, name, len, ino, dt_type(next)))
|
||||
if (!dir_emit(ctx, name, len, ino,
|
||||
fs_umode_to_dtype(next->s_mode)))
|
||||
return 0;
|
||||
|
||||
spin_lock(&configfs_dirent_lock);
|
||||
|
@ -72,24 +72,6 @@ static struct ctl_table pty_table[] = {
|
||||
{}
|
||||
};
|
||||
|
||||
static struct ctl_table pty_kern_table[] = {
|
||||
{
|
||||
.procname = "pty",
|
||||
.mode = 0555,
|
||||
.child = pty_table,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct ctl_table pty_root_table[] = {
|
||||
{
|
||||
.procname = "kernel",
|
||||
.mode = 0555,
|
||||
.child = pty_kern_table,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
struct pts_mount_opts {
|
||||
int setuid;
|
||||
int setgid;
|
||||
@ -630,7 +612,7 @@ static int __init init_devpts_fs(void)
|
||||
{
|
||||
int err = register_filesystem(&devpts_fs_type);
|
||||
if (!err) {
|
||||
register_sysctl_table(pty_root_table);
|
||||
register_sysctl("kernel/pty", pty_table);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
41
fs/eventfd.c
41
fs/eventfd.c
@ -228,7 +228,6 @@ static ssize_t eventfd_read(struct kiocb *iocb, struct iov_iter *to)
|
||||
struct file *file = iocb->ki_filp;
|
||||
struct eventfd_ctx *ctx = file->private_data;
|
||||
__u64 ucnt = 0;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
if (iov_iter_count(to) < sizeof(ucnt))
|
||||
return -EINVAL;
|
||||
@ -239,23 +238,11 @@ static ssize_t eventfd_read(struct kiocb *iocb, struct iov_iter *to)
|
||||
spin_unlock_irq(&ctx->wqh.lock);
|
||||
return -EAGAIN;
|
||||
}
|
||||
__add_wait_queue(&ctx->wqh, &wait);
|
||||
for (;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (ctx->count)
|
||||
break;
|
||||
if (signal_pending(current)) {
|
||||
__remove_wait_queue(&ctx->wqh, &wait);
|
||||
__set_current_state(TASK_RUNNING);
|
||||
spin_unlock_irq(&ctx->wqh.lock);
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
if (wait_event_interruptible_locked_irq(ctx->wqh, ctx->count)) {
|
||||
spin_unlock_irq(&ctx->wqh.lock);
|
||||
schedule();
|
||||
spin_lock_irq(&ctx->wqh.lock);
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
__remove_wait_queue(&ctx->wqh, &wait);
|
||||
__set_current_state(TASK_RUNNING);
|
||||
}
|
||||
eventfd_ctx_do_read(ctx, &ucnt);
|
||||
current->in_eventfd = 1;
|
||||
@ -275,7 +262,6 @@ static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t c
|
||||
struct eventfd_ctx *ctx = file->private_data;
|
||||
ssize_t res;
|
||||
__u64 ucnt;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
if (count < sizeof(ucnt))
|
||||
return -EINVAL;
|
||||
@ -288,23 +274,10 @@ static ssize_t eventfd_write(struct file *file, const char __user *buf, size_t c
|
||||
if (ULLONG_MAX - ctx->count > ucnt)
|
||||
res = sizeof(ucnt);
|
||||
else if (!(file->f_flags & O_NONBLOCK)) {
|
||||
__add_wait_queue(&ctx->wqh, &wait);
|
||||
for (res = 0;;) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (ULLONG_MAX - ctx->count > ucnt) {
|
||||
res = sizeof(ucnt);
|
||||
break;
|
||||
}
|
||||
if (signal_pending(current)) {
|
||||
res = -ERESTARTSYS;
|
||||
break;
|
||||
}
|
||||
spin_unlock_irq(&ctx->wqh.lock);
|
||||
schedule();
|
||||
spin_lock_irq(&ctx->wqh.lock);
|
||||
}
|
||||
__remove_wait_queue(&ctx->wqh, &wait);
|
||||
__set_current_state(TASK_RUNNING);
|
||||
res = wait_event_interruptible_locked_irq(ctx->wqh,
|
||||
ULLONG_MAX - ctx->count > ucnt);
|
||||
if (!res)
|
||||
res = sizeof(ucnt);
|
||||
}
|
||||
if (likely(res > 0)) {
|
||||
ctx->count += ucnt;
|
||||
|
@ -483,8 +483,8 @@ static inline void ep_set_busy_poll_napi_id(struct epitem *epi)
|
||||
* (efd1) notices that it may have some event ready, so it needs to wake up
|
||||
* the waiters on its poll wait list (efd2). So it calls ep_poll_safewake()
|
||||
* that ends up in another wake_up(), after having checked about the
|
||||
* recursion constraints. That are, no more than EP_MAX_POLLWAKE_NESTS, to
|
||||
* avoid stack blasting.
|
||||
* recursion constraints. That are, no more than EP_MAX_NESTS, to avoid
|
||||
* stack blasting.
|
||||
*
|
||||
* When CONFIG_DEBUG_LOCK_ALLOC is enabled, make sure lockdep can handle
|
||||
* this special case of epoll.
|
||||
|
@ -511,7 +511,11 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
|
||||
if (type == HFSPLUS_FOLDER) {
|
||||
struct hfsplus_cat_folder *folder = &entry.folder;
|
||||
|
||||
WARN_ON(fd->entrylength < sizeof(struct hfsplus_cat_folder));
|
||||
if (fd->entrylength < sizeof(struct hfsplus_cat_folder)) {
|
||||
pr_err("bad catalog folder entry\n");
|
||||
res = -EIO;
|
||||
goto out;
|
||||
}
|
||||
hfs_bnode_read(fd->bnode, &entry, fd->entryoffset,
|
||||
sizeof(struct hfsplus_cat_folder));
|
||||
hfsplus_get_perms(inode, &folder->permissions, 1);
|
||||
@ -531,7 +535,11 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
|
||||
} else if (type == HFSPLUS_FILE) {
|
||||
struct hfsplus_cat_file *file = &entry.file;
|
||||
|
||||
WARN_ON(fd->entrylength < sizeof(struct hfsplus_cat_file));
|
||||
if (fd->entrylength < sizeof(struct hfsplus_cat_file)) {
|
||||
pr_err("bad catalog file entry\n");
|
||||
res = -EIO;
|
||||
goto out;
|
||||
}
|
||||
hfs_bnode_read(fd->bnode, &entry, fd->entryoffset,
|
||||
sizeof(struct hfsplus_cat_file));
|
||||
|
||||
@ -562,6 +570,7 @@ int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
|
||||
pr_err("bad catalog entry used to create inode\n");
|
||||
res = -EIO;
|
||||
}
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -570,6 +579,7 @@ int hfsplus_cat_write_inode(struct inode *inode)
|
||||
struct inode *main_inode = inode;
|
||||
struct hfs_find_data fd;
|
||||
hfsplus_cat_entry entry;
|
||||
int res = 0;
|
||||
|
||||
if (HFSPLUS_IS_RSRC(inode))
|
||||
main_inode = HFSPLUS_I(inode)->rsrc_inode;
|
||||
@ -588,7 +598,11 @@ int hfsplus_cat_write_inode(struct inode *inode)
|
||||
if (S_ISDIR(main_inode->i_mode)) {
|
||||
struct hfsplus_cat_folder *folder = &entry.folder;
|
||||
|
||||
WARN_ON(fd.entrylength < sizeof(struct hfsplus_cat_folder));
|
||||
if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
|
||||
pr_err("bad catalog folder entry\n");
|
||||
res = -EIO;
|
||||
goto out;
|
||||
}
|
||||
hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
|
||||
sizeof(struct hfsplus_cat_folder));
|
||||
/* simple node checks? */
|
||||
@ -613,7 +627,11 @@ int hfsplus_cat_write_inode(struct inode *inode)
|
||||
} else {
|
||||
struct hfsplus_cat_file *file = &entry.file;
|
||||
|
||||
WARN_ON(fd.entrylength < sizeof(struct hfsplus_cat_file));
|
||||
if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
|
||||
pr_err("bad catalog file entry\n");
|
||||
res = -EIO;
|
||||
goto out;
|
||||
}
|
||||
hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
|
||||
sizeof(struct hfsplus_cat_file));
|
||||
hfsplus_inode_write_fork(inode, &file->data_fork);
|
||||
@ -634,7 +652,7 @@ int hfsplus_cat_write_inode(struct inode *inode)
|
||||
set_bit(HFSPLUS_I_CAT_DIRTY, &HFSPLUS_I(inode)->flags);
|
||||
out:
|
||||
hfs_find_exit(&fd);
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
int hfsplus_fileattr_get(struct dentry *dentry, struct fileattr *fa)
|
||||
|
@ -1804,8 +1804,8 @@ EXPORT_SYMBOL(bmap);
|
||||
|
||||
/*
|
||||
* With relative atime, only update atime if the previous atime is
|
||||
* earlier than either the ctime or mtime or if at least a day has
|
||||
* passed since the last atime update.
|
||||
* earlier than or equal to either the ctime or mtime,
|
||||
* or if at least a day has passed since the last atime update.
|
||||
*/
|
||||
static int relatime_need_update(struct vfsmount *mnt, struct inode *inode,
|
||||
struct timespec64 now)
|
||||
@ -1814,12 +1814,12 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode,
|
||||
if (!(mnt->mnt_flags & MNT_RELATIME))
|
||||
return 1;
|
||||
/*
|
||||
* Is mtime younger than atime? If yes, update atime:
|
||||
* Is mtime younger than or equal to atime? If yes, update atime:
|
||||
*/
|
||||
if (timespec64_compare(&inode->i_mtime, &inode->i_atime) >= 0)
|
||||
return 1;
|
||||
/*
|
||||
* Is ctime younger than atime? If yes, update atime:
|
||||
* Is ctime younger than or equal to atime? If yes, update atime:
|
||||
*/
|
||||
if (timespec64_compare(&inode->i_ctime, &inode->i_atime) >= 0)
|
||||
return 1;
|
||||
|
@ -259,8 +259,6 @@ ssize_t __kernel_write_iter(struct file *file, struct iov_iter *from, loff_t *po
|
||||
/*
|
||||
* fs/attr.c
|
||||
*/
|
||||
int setattr_should_drop_sgid(struct mnt_idmap *idmap,
|
||||
const struct inode *inode);
|
||||
struct mnt_idmap *alloc_mnt_idmap(struct user_namespace *mnt_userns);
|
||||
struct mnt_idmap *mnt_idmap_get(struct mnt_idmap *idmap);
|
||||
void mnt_idmap_put(struct mnt_idmap *idmap);
|
||||
|
@ -1748,12 +1748,6 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Relationship between mode and the DT_xxx types */
|
||||
static inline unsigned char dt_type(struct kernfs_node *kn)
|
||||
{
|
||||
return (kn->mode >> 12) & 15;
|
||||
}
|
||||
|
||||
static int kernfs_dir_fop_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
kernfs_put(filp->private_data);
|
||||
@ -1831,7 +1825,7 @@ static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx)
|
||||
pos;
|
||||
pos = kernfs_dir_next_pos(ns, parent, ctx->pos, pos)) {
|
||||
const char *name = pos->name;
|
||||
unsigned int type = dt_type(pos);
|
||||
unsigned int type = fs_umode_to_dtype(pos->mode);
|
||||
int len = strlen(name);
|
||||
ino_t ino = kernfs_ino(pos);
|
||||
|
||||
|
@ -174,12 +174,6 @@ loff_t dcache_dir_lseek(struct file *file, loff_t offset, int whence)
|
||||
}
|
||||
EXPORT_SYMBOL(dcache_dir_lseek);
|
||||
|
||||
/* Relationship between i_mode and the DT_xxx types */
|
||||
static inline unsigned char dt_type(struct inode *inode)
|
||||
{
|
||||
return (inode->i_mode >> 12) & 15;
|
||||
}
|
||||
|
||||
/*
|
||||
* Directory is locked and all positive dentries in it are safe, since
|
||||
* for ramfs-type trees they can't go away without unlink() or rmdir(),
|
||||
@ -206,7 +200,8 @@ int dcache_readdir(struct file *file, struct dir_context *ctx)
|
||||
|
||||
while ((next = scan_positives(cursor, p, 1, next)) != NULL) {
|
||||
if (!dir_emit(ctx, next->d_name.name, next->d_name.len,
|
||||
d_inode(next)->i_ino, dt_type(d_inode(next))))
|
||||
d_inode(next)->i_ino,
|
||||
fs_umode_to_dtype(d_inode(next)->i_mode)))
|
||||
break;
|
||||
ctx->pos++;
|
||||
p = &next->d_child;
|
||||
|
@ -2617,15 +2617,12 @@ static void mnt_warn_timestamp_expiry(struct path *mountpoint, struct vfsmount *
|
||||
(ktime_get_real_seconds() + TIME_UPTIME_SEC_MAX > sb->s_time_max)) {
|
||||
char *buf = (char *)__get_free_page(GFP_KERNEL);
|
||||
char *mntpath = buf ? d_path(mountpoint, buf, PAGE_SIZE) : ERR_PTR(-ENOMEM);
|
||||
struct tm tm;
|
||||
|
||||
time64_to_tm(sb->s_time_max, 0, &tm);
|
||||
|
||||
pr_warn("%s filesystem being %s at %s supports timestamps until %04ld (0x%llx)\n",
|
||||
pr_warn("%s filesystem being %s at %s supports timestamps until %ptTd (0x%llx)\n",
|
||||
sb->s_type->name,
|
||||
is_mounted(mnt) ? "remounted" : "mounted",
|
||||
mntpath,
|
||||
tm.tm_year+1900, (unsigned long long)sb->s_time_max);
|
||||
mntpath, &sb->s_time_max,
|
||||
(unsigned long long)sb->s_time_max);
|
||||
|
||||
free_page((unsigned long)buf);
|
||||
sb->s_iflags |= SB_I_TS_EXPIRY_WARNED;
|
||||
|
@ -717,9 +717,7 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr,
|
||||
if ((attr->ia_valid & ATTR_KILL_SUID) != 0 &&
|
||||
inode->i_mode & S_ISUID)
|
||||
inode->i_mode &= ~S_ISUID;
|
||||
if ((attr->ia_valid & ATTR_KILL_SGID) != 0 &&
|
||||
(inode->i_mode & (S_ISGID | S_IXGRP)) ==
|
||||
(S_ISGID | S_IXGRP))
|
||||
if (setattr_should_drop_sgid(&nop_mnt_idmap, inode))
|
||||
inode->i_mode &= ~S_ISGID;
|
||||
if ((attr->ia_valid & ATTR_MODE) != 0) {
|
||||
int mode = attr->ia_mode & S_IALLUGO;
|
||||
|
@ -1274,9 +1274,6 @@ int nfs_get_tree_common(struct fs_context *fc)
|
||||
if (ctx->clone_data.sb->s_flags & SB_SYNCHRONOUS)
|
||||
fc->sb_flags |= SB_SYNCHRONOUS;
|
||||
|
||||
if (server->caps & NFS_CAP_SECURITY_LABEL)
|
||||
fc->lsm_flags |= SECURITY_LSM_NATIVE_LABELS;
|
||||
|
||||
/* Get a superblock - note that we may end up sharing one that already exists */
|
||||
fc->s_fs_info = server;
|
||||
s = sget_fc(fc, compare_super, nfs_set_super);
|
||||
|
12
fs/pnode.c
12
fs/pnode.c
@ -214,7 +214,6 @@ static struct mount *next_group(struct mount *m, struct mount *origin)
|
||||
|
||||
/* all accesses are serialized by namespace_sem */
|
||||
static struct mount *last_dest, *first_source, *last_source, *dest_master;
|
||||
static struct mountpoint *mp;
|
||||
static struct hlist_head *list;
|
||||
|
||||
static inline bool peers(struct mount *m1, struct mount *m2)
|
||||
@ -222,7 +221,7 @@ static inline bool peers(struct mount *m1, struct mount *m2)
|
||||
return m1->mnt_group_id == m2->mnt_group_id && m1->mnt_group_id;
|
||||
}
|
||||
|
||||
static int propagate_one(struct mount *m)
|
||||
static int propagate_one(struct mount *m, struct mountpoint *dest_mp)
|
||||
{
|
||||
struct mount *child;
|
||||
int type;
|
||||
@ -230,7 +229,7 @@ static int propagate_one(struct mount *m)
|
||||
if (IS_MNT_NEW(m))
|
||||
return 0;
|
||||
/* skip if mountpoint isn't covered by it */
|
||||
if (!is_subdir(mp->m_dentry, m->mnt.mnt_root))
|
||||
if (!is_subdir(dest_mp->m_dentry, m->mnt.mnt_root))
|
||||
return 0;
|
||||
if (peers(m, last_dest)) {
|
||||
type = CL_MAKE_SHARED;
|
||||
@ -262,7 +261,7 @@ static int propagate_one(struct mount *m)
|
||||
if (IS_ERR(child))
|
||||
return PTR_ERR(child);
|
||||
read_seqlock_excl(&mount_lock);
|
||||
mnt_set_mountpoint(m, mp, child);
|
||||
mnt_set_mountpoint(m, dest_mp, child);
|
||||
if (m->mnt_master != dest_master)
|
||||
SET_MNT_MARK(m->mnt_master);
|
||||
read_sequnlock_excl(&mount_lock);
|
||||
@ -299,13 +298,12 @@ int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp,
|
||||
last_dest = dest_mnt;
|
||||
first_source = source_mnt;
|
||||
last_source = source_mnt;
|
||||
mp = dest_mp;
|
||||
list = tree_list;
|
||||
dest_master = dest_mnt->mnt_master;
|
||||
|
||||
/* all peers of dest_mnt, except dest_mnt itself */
|
||||
for (n = next_peer(dest_mnt); n != dest_mnt; n = next_peer(n)) {
|
||||
ret = propagate_one(n);
|
||||
ret = propagate_one(n, dest_mp);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
@ -316,7 +314,7 @@ int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp,
|
||||
/* everything in that slave group */
|
||||
n = m;
|
||||
do {
|
||||
ret = propagate_one(n);
|
||||
ret = propagate_one(n, dest_mp);
|
||||
if (ret)
|
||||
goto out;
|
||||
n = next_peer(n);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/fsnotify.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/socket.h>
|
||||
@ -1165,6 +1166,9 @@ long do_splice(struct file *in, loff_t *off_in, struct file *out,
|
||||
ret = do_splice_from(ipipe, out, &offset, len, flags);
|
||||
file_end_write(out);
|
||||
|
||||
if (ret > 0)
|
||||
fsnotify_modify(out);
|
||||
|
||||
if (!off_out)
|
||||
out->f_pos = offset;
|
||||
else
|
||||
@ -1188,6 +1192,10 @@ long do_splice(struct file *in, loff_t *off_in, struct file *out,
|
||||
flags |= SPLICE_F_NONBLOCK;
|
||||
|
||||
ret = splice_file_to_pipe(in, opipe, &offset, len, flags);
|
||||
|
||||
if (ret > 0)
|
||||
fsnotify_access(in);
|
||||
|
||||
if (!off_in)
|
||||
in->f_pos = offset;
|
||||
else
|
||||
|
@ -2675,6 +2675,8 @@ extern struct inode *new_inode(struct super_block *sb);
|
||||
extern void free_inode_nonrcu(struct inode *inode);
|
||||
extern int setattr_should_drop_suidgid(struct mnt_idmap *, struct inode *);
|
||||
extern int file_remove_privs(struct file *);
|
||||
int setattr_should_drop_sgid(struct mnt_idmap *idmap,
|
||||
const struct inode *inode);
|
||||
|
||||
/*
|
||||
* This must be used for allocating filesystems specific inodes to set
|
||||
|
@ -104,7 +104,6 @@ struct fs_context {
|
||||
unsigned int sb_flags; /* Proposed superblock flags (SB_*) */
|
||||
unsigned int sb_flags_mask; /* Superblock flags that were changed */
|
||||
unsigned int s_iflags; /* OR'd with sb->s_iflags */
|
||||
unsigned int lsm_flags; /* Information flags from the fs to the LSM */
|
||||
enum fs_context_purpose purpose:8;
|
||||
enum fs_context_phase phase:8; /* The phase the context is in */
|
||||
bool need_free:1; /* Need to call ops->free() */
|
||||
|
@ -68,7 +68,7 @@ struct watch_notification;
|
||||
/* If capable is being called by a setid function */
|
||||
#define CAP_OPT_INSETID BIT(2)
|
||||
|
||||
/* LSM Agnostic defines for fs_context::lsm_flags */
|
||||
/* LSM Agnostic defines for security_sb_set_mnt_opts() flags */
|
||||
#define SECURITY_LSM_NATIVE_LABELS 1
|
||||
|
||||
struct ctl_table;
|
||||
|
Loading…
Reference in New Issue
Block a user