Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mds: add truncate size handling support for fscrypt #43588

Merged
merged 18 commits into from Feb 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f0d2e1e
common: add encode and decode routines for opaque vectors of bytes
jtlayton May 4, 2021
1a593e1
mds: convert fscrypt flag to two opaque fields
jtlayton Apr 29, 2021
798bfa6
mds: add fscrypt opaque field to inode_t encoding
jtlayton Apr 15, 2021
7204cf1
mds: encode fscrypt_auth and fscrypt_file in InodeStat
jtlayton Jun 21, 2021
ec892d3
mds: add fscrypt_auth and fscrypt_file fields to MClientCaps
jtlayton Apr 14, 2021
9e40efc
client: send fscrypt_auth and fscrypt_file in MClientRequest
jtlayton Apr 15, 2021
33381ff
mds: populate fscrypt_auth/_file on inode creation
jtlayton May 5, 2021
a354393
mds: allow setattr to change fscrypt_auth/file
jtlayton May 5, 2021
b52dd9d
client: add support for fscrypt_auth and fscrypt_file fields
jtlayton May 11, 2021
5d09332
test: add fscrypt attribute testcases
jtlayton May 10, 2021
62d2d7f
mds: encode fscrypt_auth and fscrypt_file in appropriate mds locks
jtlayton Jun 21, 2021
c9db274
mds: don't allow changing layout on encrypted files/directories
luis-henrix Aug 12, 2021
090810b
mds: send the fscrypt_auth/fscrypt_file to clients in MClientCaps
lxbsz Nov 24, 2021
9dff0ba
mds: add mds_fscrypt_last_block_max_size option support
lxbsz Nov 25, 2021
e1a980b
mds: move the class MDCacheIOContext to MDCache.h
lxbsz Oct 14, 2021
5119882
client: trust the fscrypt_file as the inode size and truncate caches
lxbsz Nov 26, 2021
b73a048
client: userland clients are only allowed to read if fscrypt is enabled
lxbsz Nov 26, 2021
3b5a0ea
mds: add truncate size handling support for fscrypt
lxbsz Oct 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
161 changes: 151 additions & 10 deletions src/client/Client.cc
Expand Up @@ -802,7 +802,7 @@ void Client::update_inode_file_size(Inode *in, int issued, uint64_t size,

// truncate cached file data
if (prior_size > size) {
_invalidate_inode_cache(in, truncate_size, prior_size - truncate_size);
_invalidate_inode_cache(in, size, prior_size - size);
}
}

Expand Down Expand Up @@ -954,6 +954,7 @@ Inode * Client::add_update_inode(InodeStat *st, utime_t from,
in->btime = st->btime;
in->snap_btime = st->snap_btime;
in->snap_metadata = st->snap_metadata;
in->fscrypt_auth = st->fscrypt_auth;
}

if ((new_version || (new_issued & CEPH_CAP_LINK_SHARED)) &&
Expand All @@ -969,6 +970,7 @@ Inode * Client::add_update_inode(InodeStat *st, utime_t from,
if (new_version ||
(new_issued & (CEPH_CAP_ANY_FILE_RD | CEPH_CAP_ANY_FILE_WR))) {
in->layout = st->layout;
in->fscrypt_file = st->fscrypt_file;
update_inode_file_size(in, issued, st->size, st->truncate_seq, st->truncate_size);
}

Expand Down Expand Up @@ -1050,7 +1052,6 @@ Inode * Client::add_update_inode(InodeStat *st, utime_t from,
in->snap_caps |= st->cap.caps;
}

in->fscrypt = st->fscrypt;
return in;
}

Expand Down Expand Up @@ -2415,6 +2416,8 @@ ref_t<MClientRequest> Client::build_client_request(MetaRequest *request)
req->set_filepath2(request->get_filepath2());
req->set_alternate_name(request->alternate_name);
req->set_data(request->data);
req->fscrypt_auth = request->fscrypt_auth;
req->fscrypt_file = request->fscrypt_file;
req->set_retry_attempt(request->retry_attempt++);
req->head.num_fwd = request->num_fwd;
const gid_t *_gids;
Expand Down Expand Up @@ -3495,8 +3498,10 @@ int Client::get_caps(Fh *fh, int need, int want, int *phave, loff_t endoff)
waitfor_caps = true;
}

if ((need & CEPH_CAP_FILE_WR) && in->auth_cap &&
in->auth_cap->session->readonly)
if ((need & CEPH_CAP_FILE_WR) &&
((in->auth_cap && in->auth_cap->session->readonly) ||
// userland clients are only allowed to read if fscrypt enabled
in->is_fscrypt_enabled()))
return -CEPHFS_EROFS;

if (in->flags & I_CAP_DROPPED) {
Expand Down Expand Up @@ -3621,6 +3626,8 @@ void Client::send_cap(Inode *in, MetaSession *session, Cap *cap,
m->btime = in->btime;
m->time_warp_seq = in->time_warp_seq;
m->change_attr = in->change_attr;
m->fscrypt_auth = in->fscrypt_auth;
m->fscrypt_file = in->fscrypt_file;

if (!(flags & MClientCaps::FLAG_PENDING_CAPSNAP) &&
!in->cap_snaps.empty() &&
Expand Down Expand Up @@ -5209,15 +5216,20 @@ void Client::handle_cap_trunc(MetaSession *session, Inode *in, const MConstRef<M
mds_rank_t mds = session->mds_num;
ceph_assert(in->caps.count(mds));

uint64_t size = m->get_size();
if (in->is_fscrypt_enabled()) {
size = std::stoll(std::string(std::rbegin(m->fscrypt_file),
std::rend(m->fscrypt_file)));
}
ldout(cct, 10) << __func__ << " on ino " << *in
<< " size " << in->size << " -> " << m->get_size()
<< dendl;

int issued;
in->caps_issued(&issued);
issued |= in->caps_dirty();
update_inode_file_size(in, issued, m->get_size(),
m->get_truncate_seq(), m->get_truncate_size());
update_inode_file_size(in, issued, size, m->get_truncate_seq(),
m->get_truncate_size());
}

void Client::handle_cap_flush_ack(MetaSession *session, Inode *in, Cap *cap, const MConstRef<MClientCaps>& m)
Expand Down Expand Up @@ -7443,15 +7455,20 @@ int Client::_getattr(Inode *in, int mask, const UserPerm& perms, bool force)
}

int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask,
const UserPerm& perms, InodeRef *inp)
const UserPerm& perms, InodeRef *inp,
std::vector<uint8_t>* aux)
{
int issued = in->caps_issued();
union ceph_mds_request_args args;
bool kill_sguid = false;
int inode_drop = 0;
size_t auxsize = 0;

if (aux)
auxsize = aux->size();

ldout(cct, 10) << __func__ << " mask " << mask << " issued " <<
ccap_string(issued) << dendl;
ccap_string(issued) << " aux size " << auxsize << dendl;

if (in->snapid != CEPH_NOSNAP) {
return -CEPHFS_EROFS;
Expand All @@ -7463,6 +7480,14 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask,
return -CEPHFS_EDQUOT;
}

// Can't set fscrypt_auth and file at the same time!
if ((mask & (CEPH_SETATTR_FSCRYPT_AUTH|CEPH_SETATTR_FSCRYPT_FILE)) ==
(CEPH_SETATTR_FSCRYPT_AUTH|CEPH_SETATTR_FSCRYPT_FILE))
return -CEPHFS_EINVAL;

if (!aux && (mask & (CEPH_SETATTR_FSCRYPT_AUTH|CEPH_SETATTR_FSCRYPT_FILE)))
return -CEPHFS_EINVAL;

memset(&args, 0, sizeof(args));

// make the change locally?
Expand Down Expand Up @@ -7595,6 +7620,25 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask,
}
}

if (mask & CEPH_SETATTR_FSCRYPT_AUTH) {
ldout(cct,10) << "resetting cached fscrypt_auth field. size now "
<< in->fscrypt_auth.size() << dendl;

if (in->caps_issued_mask(CEPH_CAP_AUTH_EXCL)) {
in->ctime = ceph_clock_now();
in->cap_dirtier_uid = perms.uid();
in->cap_dirtier_gid = perms.gid();
in->fscrypt_auth = *aux;
in->mark_caps_dirty(CEPH_CAP_AUTH_EXCL);
mask &= ~CEPH_SETATTR_FSCRYPT_AUTH;
} else if (!in->caps_issued_mask(CEPH_CAP_AUTH_SHARED) ||
in->fscrypt_auth != *aux) {
inode_drop |= CEPH_CAP_AUTH_SHARED;
} else {
mask &= ~CEPH_SETATTR_FSCRYPT_AUTH;
}
}

if (mask & CEPH_SETATTR_SIZE) {
if ((uint64_t)stx->stx_size >= mdsmap->get_max_filesize()) {
//too big!
Expand Down Expand Up @@ -7624,6 +7668,25 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask,
}
}

if (mask & CEPH_SETATTR_FSCRYPT_FILE) {
ldout(cct,10) << "resetting cached fscrypt_file field. size now "
<< in->fscrypt_file.size() << dendl;

if (in->caps_issued_mask(CEPH_CAP_FILE_EXCL)) {
in->ctime = ceph_clock_now();
in->cap_dirtier_uid = perms.uid();
in->cap_dirtier_gid = perms.gid();
in->fscrypt_file = *aux;
in->mark_caps_dirty(CEPH_CAP_FILE_EXCL);
mask &= ~CEPH_SETATTR_FSCRYPT_FILE;
} else if (!in->caps_issued_mask(CEPH_CAP_FILE_SHARED) ||
in->fscrypt_file != *aux) {
inode_drop |= CEPH_CAP_FILE_SHARED | CEPH_CAP_FILE_RD | CEPH_CAP_FILE_WR;
} else {
mask &= ~CEPH_SETATTR_FSCRYPT_FILE;
}
}

if (mask & CEPH_SETATTR_MTIME) {
if (in->caps_issued_mask(CEPH_CAP_FILE_EXCL)) {
in->mtime = utime_t(stx->stx_mtime);
Expand Down Expand Up @@ -7693,6 +7756,11 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask,

req->head.args = args;
req->inode_drop = inode_drop;
if (mask & CEPH_SETATTR_FSCRYPT_AUTH) {
req->fscrypt_auth = *aux;
} else if (mask & CEPH_SETATTR_FSCRYPT_FILE) {
req->fscrypt_file = *aux;
}
req->head.args.setattr.mask = mask;
req->regetattr_mask = mask;

Expand Down Expand Up @@ -12499,7 +12567,7 @@ int Client::_setxattr(Inode *in, const char *name, const void *value,
if (new_mode != in->mode) {
struct ceph_statx stx;
stx.stx_mode = new_mode;
ret = _do_setattr(in, &stx, CEPH_SETATTR_MODE, perms, NULL);
ret = _do_setattr(in, &stx, CEPH_SETATTR_MODE, perms, nullptr);
if (ret < 0)
return ret;
}
Expand All @@ -12524,6 +12592,8 @@ int Client::_setxattr(Inode *in, const char *name, const void *value,
if (vxattr) {
if (vxattr->readonly)
return -CEPHFS_EOPNOTSUPP;
if (vxattr->setxattr_cb)
return (this->*(vxattr->setxattr_cb))(in, value, size, perms);
if (vxattr->name.compare(0, 10, "ceph.quota") == 0 && value)
check_realm = true;
}
Expand Down Expand Up @@ -12704,6 +12774,61 @@ int Client::ll_removexattr(Inode *in, const char *name, const UserPerm& perms)
return _removexattr(in, name, perms);
}

bool Client::_vxattrcb_fscrypt_auth_exists(Inode *in)
{
bool exists = !in->fscrypt_auth.empty();

ldout(cct, 10) << "fscrypt_auth exists " << exists << dendl;
return exists;
}

size_t Client::_vxattrcb_fscrypt_auth(Inode *in, char *val, size_t size)
{
size_t count = in->fscrypt_auth.size();

if (count <= size)
memcpy(val, in->fscrypt_auth.data(), count);
return count;
}

int Client::_vxattrcb_fscrypt_auth_set(Inode *in, const void *val, size_t size,
const UserPerm& perms)
{
struct ceph_statx stx = { 0 };
std::vector<uint8_t> aux;

aux.resize(size);
memcpy(aux.data(), val, size);

return _do_setattr(in, &stx, CEPH_SETATTR_FSCRYPT_AUTH, perms, nullptr, &aux);
}

bool Client::_vxattrcb_fscrypt_file_exists(Inode *in)
{
return !in->fscrypt_file.empty();
}

size_t Client::_vxattrcb_fscrypt_file(Inode *in, char *val, size_t size)
{
size_t count = in->fscrypt_file.size();

if (count <= size)
memcpy(val, in->fscrypt_file.data(), count);
return count;
}

int Client::_vxattrcb_fscrypt_file_set(Inode *in, const void *val, size_t size,
const UserPerm& perms)
{
struct ceph_statx stx = { 0 };
std::vector<uint8_t> aux;

aux.resize(size);
memcpy(aux.data(), val, size);

return _do_setattr(in, &stx, CEPH_SETATTR_FSCRYPT_FILE, perms, nullptr, &aux);
}

bool Client::_vxattrcb_quota_exists(Inode *in)
{
return in->quota.is_enable() &&
Expand Down Expand Up @@ -13005,6 +13130,22 @@ const Client::VXattr Client::_common_vxattrs[] = {
exists_cb: nullptr,
flags: 0,
},
{
name: "ceph.fscrypt.auth",
getxattr_cb: &Client::_vxattrcb_fscrypt_auth,
setxattr_cb: &Client::_vxattrcb_fscrypt_auth_set,
readonly: false,
exists_cb: &Client::_vxattrcb_fscrypt_auth_exists,
flags: 0,
},
{
name: "ceph.fscrypt.file",
getxattr_cb: &Client::_vxattrcb_fscrypt_file,
setxattr_cb: &Client::_vxattrcb_fscrypt_file_set,
readonly: false,
exists_cb: &Client::_vxattrcb_fscrypt_file_exists,
flags: 0,
},
{ name: "" } /* Required table terminator */
};

Expand Down
11 changes: 10 additions & 1 deletion src/client/Client.h
Expand Up @@ -1238,6 +1238,8 @@ class Client : public Dispatcher, public md_config_obs_t {
struct VXattr {
const std::string name;
size_t (Client::*getxattr_cb)(Inode *in, char *val, size_t size);
int (Client::*setxattr_cb)(Inode *in, const void *val, size_t size,
const UserPerm& perms);
bool readonly;
bool (Client::*exists_cb)(Inode *in);
unsigned int flags;
Expand Down Expand Up @@ -1318,7 +1320,8 @@ class Client : public Dispatcher, public md_config_obs_t {
int _mknod(Inode *dir, const char *name, mode_t mode, dev_t rdev,
const UserPerm& perms, InodeRef *inp = 0);
int _do_setattr(Inode *in, struct ceph_statx *stx, int mask,
const UserPerm& perms, InodeRef *inp);
const UserPerm& perms, InodeRef *inp,
std::vector<uint8_t>* aux=nullptr);
void stat_to_statx(struct stat *st, struct ceph_statx *stx);
int __setattrx(Inode *in, struct ceph_statx *stx, int mask,
const UserPerm& perms, InodeRef *inp = 0);
Expand Down Expand Up @@ -1392,6 +1395,12 @@ class Client : public Dispatcher, public md_config_obs_t {

vinodeno_t _get_vino(Inode *in);

bool _vxattrcb_fscrypt_auth_exists(Inode *in);
size_t _vxattrcb_fscrypt_auth(Inode *in, char *val, size_t size);
int _vxattrcb_fscrypt_auth_set(Inode *in, const void *val, size_t size, const UserPerm& perms);
bool _vxattrcb_fscrypt_file_exists(Inode *in);
size_t _vxattrcb_fscrypt_file(Inode *in, char *val, size_t size);
int _vxattrcb_fscrypt_file_set(Inode *in, const void *val, size_t size, const UserPerm& perms);
bool _vxattrcb_quota_exists(Inode *in);
size_t _vxattrcb_quota(Inode *in, char *val, size_t size);
size_t _vxattrcb_quota_max_bytes(Inode *in, char *val, size_t size);
Expand Down
6 changes: 5 additions & 1 deletion src/client/Inode.h
Expand Up @@ -163,7 +163,11 @@ struct Inode : RefCountedObject {
version_t inline_version = 0;
bufferlist inline_data;

bool fscrypt = false; // fscrypt enabled ?
std::vector<uint8_t> fscrypt_auth;
std::vector<uint8_t> fscrypt_file;
bool is_fscrypt_enabled() {
return !!fscrypt_auth.size();
}

bool is_root() const { return ino == CEPH_INO_ROOT; }
bool is_symlink() const { return (mode & S_IFMT) == S_IFLNK; }
Expand Down
2 changes: 2 additions & 0 deletions src/client/MetaRequest.h
Expand Up @@ -30,6 +30,8 @@ struct MetaRequest {
ceph_mds_request_head head;
filepath path, path2;
std::string alternate_name;
std::vector<uint8_t> fscrypt_auth;
std::vector<uint8_t> fscrypt_file;
bufferlist data;
int inode_drop = 0; //the inode caps this operation will drop
int inode_unless = 0; //unless we have these caps already
Expand Down
10 changes: 10 additions & 0 deletions src/common/options/mds.yaml.in
Expand Up @@ -11,6 +11,16 @@ options:
- mds
flags:
- runtime
- name: mds_fscrypt_last_block_max_size
type: size
level: advanced
desc: maximum size of the last block without the header along with a truncate
request when the fscrypt is enabled.
default: 4_K
services:
- mds
flags:
- runtime
- name: mds_valgrind_exit
type: bool
level: dev
Expand Down
25 changes: 14 additions & 11 deletions src/include/ceph_fs.h
Expand Up @@ -426,19 +426,22 @@ enum {

extern const char *ceph_mds_op_name(int op);

// setattr mask is an int
#ifndef CEPH_SETATTR_MODE
#define CEPH_SETATTR_MODE (1 << 0)
#define CEPH_SETATTR_UID (1 << 1)
#define CEPH_SETATTR_GID (1 << 2)
#define CEPH_SETATTR_MTIME (1 << 3)
#define CEPH_SETATTR_ATIME (1 << 4)
#define CEPH_SETATTR_SIZE (1 << 5)
#define CEPH_SETATTR_CTIME (1 << 6)
#define CEPH_SETATTR_MTIME_NOW (1 << 7)
#define CEPH_SETATTR_ATIME_NOW (1 << 8)
#define CEPH_SETATTR_BTIME (1 << 9)
#define CEPH_SETATTR_MODE (1 << 0)
#define CEPH_SETATTR_UID (1 << 1)
#define CEPH_SETATTR_GID (1 << 2)
#define CEPH_SETATTR_MTIME (1 << 3)
#define CEPH_SETATTR_ATIME (1 << 4)
#define CEPH_SETATTR_SIZE (1 << 5)
#define CEPH_SETATTR_CTIME (1 << 6)
#define CEPH_SETATTR_MTIME_NOW (1 << 7)
#define CEPH_SETATTR_ATIME_NOW (1 << 8)
#define CEPH_SETATTR_BTIME (1 << 9)
#define CEPH_SETATTR_KILL_SGUID (1 << 10)
#define CEPH_SETATTR_FSCRYPT_AUTH (1 << 11)
#define CEPH_SETATTR_FSCRYPT_FILE (1 << 12)
#endif
#define CEPH_SETATTR_KILL_SGUID (1 << 10)

/*
* open request flags
Expand Down