Viewing: llite_foreign.c
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 Intel Corporation.
*/
#define DEBUG_SUBSYSTEM S_LLITE
#include "llite_internal.h"
static void ll_manage_foreign_file(struct inode *inode,
struct lov_foreign_md *lfm)
{
struct ll_sb_info *sbi = ll_i2sbi(inode);
if (le32_to_cpu(lfm->lfm_type) == LU_FOREIGN_TYPE_SYMLINK) {
CDEBUG(D_INFO,
"%s: inode %p of fid "DFID": Foreign file of type symlink, faking a symlink\n",
sbi->ll_fsname, inode, PFID(ll_inode2fid(inode)));
/* change inode_operations to add symlink methods, and clear
* IOP_NOFOLLOW to ensure file will be treated as a symlink
* by Kernel (see in * d_flags_for_inode()).
*/
inode->i_op = &ll_foreign_file_symlink_inode_operations;
inode->i_opflags &= ~IOP_NOFOLLOW;
} else {
CDEBUG(D_INFO,
"%s: inode %p of fid "DFID": Foreign file of type %ux, nothing special to do\n",
sbi->ll_fsname, inode, PFID(ll_inode2fid(inode)),
le32_to_cpu(lfm->lfm_type));
}
}
static void ll_manage_foreign_dir(struct inode *inode,
struct lmv_foreign_md *lfm)
{
struct ll_sb_info *sbi = ll_i2sbi(inode);
if (lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK) {
CDEBUG(D_INFO,
"%s: inode %p of fid "DFID": Foreign dir of type symlink, faking a symlink\n",
sbi->ll_fsname, inode, PFID(ll_inode2fid(inode)));
/* change inode_operations to add symlink methods
* IOP_NOFOLLOW should not be set for dirs
*/
inode->i_op = &ll_foreign_dir_symlink_inode_operations;
} else {
CDEBUG(D_INFO,
"%s: inode %p of fid "DFID": Foreign dir of type %ux, nothing special to do\n",
sbi->ll_fsname, inode, PFID(ll_inode2fid(inode)),
le32_to_cpu(lfm->lfm_type));
}
}
int ll_manage_foreign(struct inode *inode, struct lustre_md *lmd)
{
int rc = 0;
ENTRY;
/* apply any foreign file/dir policy */
if (S_ISREG((inode)->i_mode)) {
struct ll_inode_info *lli = ll_i2info(inode);
struct cl_object *obj = lli->lli_clob;
if (lmd->layout.lb_buf != NULL && lmd->layout.lb_len != 0) {
struct lov_foreign_md *lfm = lmd->layout.lb_buf;
if (lfm->lfm_magic == LOV_MAGIC_FOREIGN)
ll_manage_foreign_file(inode, lfm);
GOTO(out, rc);
}
if (obj) {
struct lov_foreign_md lfm = {
.lfm_magic = LOV_MAGIC,
};
struct cl_layout cl = {
.cl_buf.lb_buf = &lfm,
.cl_buf.lb_len = sizeof(lfm),
};
struct lu_env *env;
u16 refcheck;
env = cl_env_get(&refcheck);
if (IS_ERR(env))
GOTO(out, rc = PTR_ERR(env));
rc = cl_object_layout_get(env, obj, &cl);
/* error is likely to be -ERANGE because of the small
* buffer we use, only the content is significant here
*/
if (rc < 0 && rc != -ERANGE) {
cl_env_put(env, &refcheck);
GOTO(out, rc);
}
if (lfm.lfm_magic == LOV_MAGIC_FOREIGN)
ll_manage_foreign_file(inode, &lfm);
cl_env_put(env, &refcheck);
}
} else if (S_ISDIR((inode)->i_mode)) {
if (lmv_dir_foreign(lmd->lsm_obj)) {
ll_manage_foreign_dir(inode, &lmd->lsm_obj->lso_lfm);
} else {
struct ll_inode_info *lli = ll_i2info(inode);
struct lmv_stripe_object *lsm_obj;
down_read(&lli->lli_lsm_sem);
lsm_obj = lli->lli_lsm_obj;
if (lmv_dir_foreign(lsm_obj))
ll_manage_foreign_dir(inode, &lsm_obj->lso_lfm);
up_read(&lli->lli_lsm_sem);
}
}
out:
RETURN(rc);
}
/* dentry must be spliced to inode (dentry->d_inode != NULL) !!! */
bool ll_foreign_is_openable(struct dentry *dentry, unsigned int flags)
{
/* check for faked symlink here as they should not be opened (unless
* O_NOFOLLOW!) and thus wants ll_atomic_open() to return 1 from
* finish_no_open() in order to get follow_link() to be called in both
* path_lookupat() and path_openupat().
* This will not break regular symlink handling as they have
* been treated/filtered upstream.
*/
if (d_is_symlink(dentry) && !S_ISLNK(dentry->d_inode->i_mode) &&
!(flags & O_NOFOLLOW))
return false;
return true;
}
static bool should_preserve_foreign_file(struct lov_foreign_md *lfm,
struct ll_inode_info *lli, bool unset)
{
/* for now, only avoid foreign fake symlink file removal */
if (unset)
if (lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK) {
set_bit(LLIF_FOREIGN_REMOVABLE, &lli->lli_flags);
return true;
} else {
return false;
}
else
return lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK &&
!test_bit(LLIF_FOREIGN_REMOVABLE, &lli->lli_flags);
}
static bool should_preserve_foreign_dir(struct lmv_foreign_md *lfm,
struct ll_inode_info *lli, bool unset)
{
/* for now, only avoid foreign fake symlink dir removal */
if (unset)
if (lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK) {
set_bit(LLIF_FOREIGN_REMOVABLE, &lli->lli_flags);
return true;
} else {
return false;
}
else
return lfm->lfm_type == LU_FOREIGN_TYPE_SYMLINK &&
!test_bit(LLIF_FOREIGN_REMOVABLE, &lli->lli_flags);
}
/* XXX
* instead of fetching type from foreign LOV/LMV, we may simply
* check (d_is_symlink(dentry) && !S_ISLNK(dentry->d_inode->i_mode))
* to identify a fake symlink
*/
bool ll_foreign_is_removable(struct dentry *dentry, bool unset)
{
struct inode *inode = dentry->d_inode;
const struct qstr *name = &dentry->d_name;
bool preserve_foreign = false;
int rc = 0;
ENTRY;
if (inode == NULL)
return 0;
/* some foreign types may not be allowed to be unlinked in order to
* keep references with external objects
*/
if (S_ISREG(inode->i_mode)) {
struct ll_inode_info *lli = ll_i2info(inode);
struct cl_object *obj = lli->lli_clob;
if (obj) {
struct lov_foreign_md lfm = {
.lfm_magic = LOV_MAGIC,
};
struct cl_layout cl = {
.cl_buf.lb_buf = &lfm,
.cl_buf.lb_len = sizeof(lfm),
};
struct lu_env *env;
u16 refcheck;
env = cl_env_get(&refcheck);
if (IS_ERR(env))
GOTO(out, rc = PTR_ERR(env));
rc = cl_object_layout_get(env, obj, &cl);
/* error is likely to be -ERANGE because of the small
* buffer we use, only the content is significant here
*/
if (rc < 0 && rc != -ERANGE) {
cl_env_put(env, &refcheck);
goto out;
} else {
rc = 0;
}
if (lfm.lfm_magic == LOV_MAGIC_FOREIGN)
preserve_foreign =
should_preserve_foreign_file(&lfm, lli,
unset);
cl_env_put(env, &refcheck);
if (preserve_foreign) {
CDEBUG(D_INFO,
"%s unlink of foreign file (%.*s, "DFID")\n",
unset ? "allow" : "prevent",
name->len, name->name,
PFID(ll_inode2fid(inode)));
RETURN(false);
}
} else {
CDEBUG(D_INFO,
"unable to check if file (%.*s, "DFID") is foreign...\n",
name->len, name->name,
PFID(ll_inode2fid(inode)));
/* XXX should we prevent removal ?? */
}
} else if (S_ISDIR(inode->i_mode)) {
struct ll_inode_info *lli = ll_i2info(inode);
struct lmv_stripe_object *lsm_obj;
down_read(&lli->lli_lsm_sem);
lsm_obj = lli->lli_lsm_obj;
if (!lsm_obj)
CDEBUG(D_INFO,
"unable to check if dir (%.*s, "DFID") is foreign...\n",
name->len, name->name,
PFID(ll_inode2fid(inode)));
else if (lmv_dir_foreign(lsm_obj))
preserve_foreign =
should_preserve_foreign_dir(&lsm_obj->lso_lfm,
lli, unset);
up_read(&lli->lli_lsm_sem);
if (preserve_foreign) {
CDEBUG(D_INFO,
"%s unlink of foreign dir (%.*s, "DFID")\n",
unset ? "allow" : "prevent",
name->len, name->name,
PFID(ll_inode2fid(inode)));
RETURN(false);
}
}
out:
RETURN(true);
}