| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- // SPDX-License-Identifier: GPL-2.0-only
- #include <linux/fs.h>
- #include <linux/xattr.h>
- #include "overlayfs.h"
- static bool ovl_is_escaped_xattr(struct super_block *sb, const char *name)
- {
- struct ovl_fs *ofs = sb->s_fs_info;
- if (ofs->config.userxattr)
- return strncmp(name, OVL_XATTR_ESCAPE_USER_PREFIX,
- OVL_XATTR_ESCAPE_USER_PREFIX_LEN) == 0;
- else
- return strncmp(name, OVL_XATTR_ESCAPE_TRUSTED_PREFIX,
- OVL_XATTR_ESCAPE_TRUSTED_PREFIX_LEN - 1) == 0;
- }
- static bool ovl_is_own_xattr(struct super_block *sb, const char *name)
- {
- struct ovl_fs *ofs = OVL_FS(sb);
- if (ofs->config.userxattr)
- return strncmp(name, OVL_XATTR_USER_PREFIX,
- OVL_XATTR_USER_PREFIX_LEN) == 0;
- else
- return strncmp(name, OVL_XATTR_TRUSTED_PREFIX,
- OVL_XATTR_TRUSTED_PREFIX_LEN) == 0;
- }
- bool ovl_is_private_xattr(struct super_block *sb, const char *name)
- {
- return ovl_is_own_xattr(sb, name) && !ovl_is_escaped_xattr(sb, name);
- }
- static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
- const void *value, size_t size, int flags)
- {
- int err;
- struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
- struct dentry *upperdentry = ovl_i_dentry_upper(inode);
- struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
- struct path realpath;
- const struct cred *old_cred;
- if (!value && !upperdentry) {
- ovl_path_lower(dentry, &realpath);
- old_cred = ovl_override_creds(dentry->d_sb);
- err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
- revert_creds(old_cred);
- if (err < 0)
- goto out;
- }
- if (!upperdentry) {
- err = ovl_copy_up(dentry);
- if (err)
- goto out;
- realdentry = ovl_dentry_upper(dentry);
- }
- err = ovl_want_write(dentry);
- if (err)
- goto out;
- old_cred = ovl_override_creds(dentry->d_sb);
- if (value) {
- err = ovl_do_setxattr(ofs, realdentry, name, value, size,
- flags);
- } else {
- WARN_ON(flags != XATTR_REPLACE);
- err = ovl_do_removexattr(ofs, realdentry, name);
- }
- revert_creds(old_cred);
- ovl_drop_write(dentry);
- /* copy c/mtime */
- ovl_copyattr(inode);
- out:
- return err;
- }
- static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
- void *value, size_t size)
- {
- ssize_t res;
- const struct cred *old_cred;
- struct path realpath;
- ovl_i_path_real(inode, &realpath);
- old_cred = ovl_override_creds(dentry->d_sb);
- res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
- revert_creds(old_cred);
- return res;
- }
- static bool ovl_can_list(struct super_block *sb, const char *s)
- {
- /* Never list private (.overlay) */
- if (ovl_is_private_xattr(sb, s))
- return false;
- /* List all non-trusted xattrs */
- if (strncmp(s, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) != 0)
- return true;
- /* list other trusted for superuser only */
- return ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN);
- }
- ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
- {
- struct dentry *realdentry = ovl_dentry_real(dentry);
- struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
- ssize_t res;
- size_t len;
- char *s;
- const struct cred *old_cred;
- size_t prefix_len, name_len;
- old_cred = ovl_override_creds(dentry->d_sb);
- res = vfs_listxattr(realdentry, list, size);
- revert_creds(old_cred);
- if (res <= 0 || size == 0)
- return res;
- prefix_len = ofs->config.userxattr ?
- OVL_XATTR_USER_PREFIX_LEN : OVL_XATTR_TRUSTED_PREFIX_LEN;
- /* filter out private xattrs */
- for (s = list, len = res; len;) {
- size_t slen = strnlen(s, len) + 1;
- /* underlying fs providing us with an broken xattr list? */
- if (WARN_ON(slen > len))
- return -EIO;
- len -= slen;
- if (!ovl_can_list(dentry->d_sb, s)) {
- res -= slen;
- memmove(s, s + slen, len);
- } else if (ovl_is_escaped_xattr(dentry->d_sb, s)) {
- res -= OVL_XATTR_ESCAPE_PREFIX_LEN;
- name_len = slen - prefix_len - OVL_XATTR_ESCAPE_PREFIX_LEN;
- s += prefix_len;
- memmove(s, s + OVL_XATTR_ESCAPE_PREFIX_LEN, name_len + len);
- s += name_len;
- } else {
- s += slen;
- }
- }
- return res;
- }
- static char *ovl_xattr_escape_name(const char *prefix, const char *name)
- {
- size_t prefix_len = strlen(prefix);
- size_t name_len = strlen(name);
- size_t escaped_len;
- char *escaped, *s;
- escaped_len = prefix_len + OVL_XATTR_ESCAPE_PREFIX_LEN + name_len;
- if (escaped_len > XATTR_NAME_MAX)
- return ERR_PTR(-EOPNOTSUPP);
- escaped = kmalloc(escaped_len + 1, GFP_KERNEL);
- if (escaped == NULL)
- return ERR_PTR(-ENOMEM);
- s = escaped;
- memcpy(s, prefix, prefix_len);
- s += prefix_len;
- memcpy(s, OVL_XATTR_ESCAPE_PREFIX, OVL_XATTR_ESCAPE_PREFIX_LEN);
- s += OVL_XATTR_ESCAPE_PREFIX_LEN;
- memcpy(s, name, name_len + 1);
- return escaped;
- }
- static int ovl_own_xattr_get(const struct xattr_handler *handler,
- struct dentry *dentry, struct inode *inode,
- const char *name, void *buffer, size_t size)
- {
- char *escaped;
- int r;
- escaped = ovl_xattr_escape_name(handler->prefix, name);
- if (IS_ERR(escaped))
- return PTR_ERR(escaped);
- r = ovl_xattr_get(dentry, inode, escaped, buffer, size);
- kfree(escaped);
- return r;
- }
- static int ovl_own_xattr_set(const struct xattr_handler *handler,
- struct mnt_idmap *idmap,
- struct dentry *dentry, struct inode *inode,
- const char *name, const void *value,
- size_t size, int flags)
- {
- char *escaped;
- int r;
- escaped = ovl_xattr_escape_name(handler->prefix, name);
- if (IS_ERR(escaped))
- return PTR_ERR(escaped);
- r = ovl_xattr_set(dentry, inode, escaped, value, size, flags);
- kfree(escaped);
- return r;
- }
- static int ovl_other_xattr_get(const struct xattr_handler *handler,
- struct dentry *dentry, struct inode *inode,
- const char *name, void *buffer, size_t size)
- {
- return ovl_xattr_get(dentry, inode, name, buffer, size);
- }
- static int ovl_other_xattr_set(const struct xattr_handler *handler,
- struct mnt_idmap *idmap,
- struct dentry *dentry, struct inode *inode,
- const char *name, const void *value,
- size_t size, int flags)
- {
- return ovl_xattr_set(dentry, inode, name, value, size, flags);
- }
- static const struct xattr_handler ovl_own_trusted_xattr_handler = {
- .prefix = OVL_XATTR_TRUSTED_PREFIX,
- .get = ovl_own_xattr_get,
- .set = ovl_own_xattr_set,
- };
- static const struct xattr_handler ovl_own_user_xattr_handler = {
- .prefix = OVL_XATTR_USER_PREFIX,
- .get = ovl_own_xattr_get,
- .set = ovl_own_xattr_set,
- };
- static const struct xattr_handler ovl_other_xattr_handler = {
- .prefix = "", /* catch all */
- .get = ovl_other_xattr_get,
- .set = ovl_other_xattr_set,
- };
- static const struct xattr_handler * const ovl_trusted_xattr_handlers[] = {
- &ovl_own_trusted_xattr_handler,
- &ovl_other_xattr_handler,
- NULL
- };
- static const struct xattr_handler * const ovl_user_xattr_handlers[] = {
- &ovl_own_user_xattr_handler,
- &ovl_other_xattr_handler,
- NULL
- };
- const struct xattr_handler * const *ovl_xattr_handlers(struct ovl_fs *ofs)
- {
- return ofs->config.userxattr ? ovl_user_xattr_handlers :
- ovl_trusted_xattr_handlers;
- }
|