From 17d6a993fc17d533460c5f40f3908c708e057c18 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 23 May 2024 18:45:17 +0200 Subject: Merging upstream version 18.2.3. Signed-off-by: Daniel Baumann --- src/client/Client.cc | 365 ++++++++++++++++++++++++++++++++++---------------- src/client/Client.h | 21 ++- src/client/Dentry.h | 16 +++ src/client/Inode.cc | 16 +++ src/client/Inode.h | 4 +- src/client/fuse_ll.cc | 9 ++ 6 files changed, 304 insertions(+), 127 deletions(-) (limited to 'src/client') diff --git a/src/client/Client.cc b/src/client/Client.cc index 2b7db5a89..ba41e9dd0 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -257,10 +257,10 @@ int Client::get_fd_inode(int fd, InodeRef *in) { return r; } -dir_result_t::dir_result_t(Inode *in, const UserPerm& perms) +dir_result_t::dir_result_t(Inode *in, const UserPerm& perms, int fd) : inode(in), offset(0), next_offset(2), release_count(0), ordered_count(0), cache_index(0), start_shared_gen(0), - perms(perms) + perms(perms), fd(fd) { } void Client::_reset_faked_inos() @@ -467,15 +467,20 @@ void Client::tear_down_cache() ceph_assert(inode_map.empty()); } -inodeno_t Client::get_root_ino() +inodeno_t Client::_get_root_ino(bool fake) { - std::scoped_lock l(client_lock); - if (use_faked_inos()) + if (fake && use_faked_inos()) return root->faked_ino; else return root->ino; } +inodeno_t Client::get_root_ino() +{ + std::scoped_lock l(client_lock); + return _get_root_ino(true); +} + Inode *Client::get_root() { std::scoped_lock l(client_lock); @@ -2420,6 +2425,7 @@ void Client::handle_client_session(const MConstRef& m) session->seq = m->get_seq(); reinit_mds_features(session.get(), m); + cap_auths = std::move(m->cap_auths); renew_caps(session.get()); session->state = MetaSession::STATE_OPEN; @@ -3810,8 +3816,17 @@ void Client::send_cap(Inode *in, MetaSession *session, Cap *cap, flush, cap->mseq, cap_epoch_barrier); - m->caller_uid = in->cap_dirtier_uid; - m->caller_gid = in->cap_dirtier_gid; + /* + * Since the setattr will check the cephx mds auth access before + * buffering the changes, so it makes no sense any more to let + * the cap update to check the access in MDS again. + * + * For new clients with old MDSs that doesn't support + * CEPHFS_FEATURE_MDS_AUTH_CAPS_CHECK we will force the session + * to be readonly if root_squash is enabled as a workaround. + */ + m->caller_uid = -1; + m->caller_gid = -1; m->head.issue_seq = cap->issue_seq; m->set_tid(flush_tid); @@ -4086,8 +4101,6 @@ void Client::queue_cap_snap(Inode *in, SnapContext& old_snapc) capsnap.btime = in->btime; capsnap.xattrs = in->xattrs; capsnap.xattr_version = in->xattr_version; - capsnap.cap_dirtier_uid = in->cap_dirtier_uid; - capsnap.cap_dirtier_gid = in->cap_dirtier_gid; if (used & CEPH_CAP_FILE_WR) { ldout(cct, 10) << __func__ << " WR used on " << *in << dendl; @@ -4111,12 +4124,6 @@ void Client::finish_cap_snap(Inode *in, CapSnap &capsnap, int used) capsnap.change_attr = in->change_attr; capsnap.dirty |= in->caps_dirty(); - /* Only reset it if it wasn't set before */ - if (capsnap.cap_dirtier_uid == -1) { - capsnap.cap_dirtier_uid = in->cap_dirtier_uid; - capsnap.cap_dirtier_gid = in->cap_dirtier_gid; - } - if (capsnap.dirty & CEPH_CAP_FILE_WR) { capsnap.inline_data = in->inline_data; capsnap.inline_version = in->inline_version; @@ -4140,8 +4147,13 @@ void Client::send_flush_snap(Inode *in, MetaSession *session, auto m = make_message(CEPH_CAP_OP_FLUSHSNAP, in->ino, in->snaprealm->ino, 0, in->auth_cap->mseq, cap_epoch_barrier); - m->caller_uid = capsnap.cap_dirtier_uid; - m->caller_gid = capsnap.cap_dirtier_gid; + /* + * Since the setattr will check the cephx mds auth access before + * buffering the changes, so it makes no sense any more to let + * the cap update to check the access in MDS again. + */ + m->caller_uid = -1; + m->caller_gid = -1; m->set_client_tid(capsnap.flush_tid); m->head.snap_follows = follows; @@ -4773,6 +4785,9 @@ void Client::trim_caps(MetaSession *s, uint64_t max) // is deleted inside remove_cap ++p; + if (in->dirty_caps || in->cap_snaps.size()) + cap_delay_requeue(in.get()); + if (in->caps.size() > 1 && cap != in->auth_cap) { int mine = cap->issued | cap->implemented; int oissued = in->auth_cap ? in->auth_cap->issued : 0; @@ -4810,7 +4825,8 @@ void Client::trim_caps(MetaSession *s, uint64_t max) } if (all && in->ino != CEPH_INO_ROOT) { ldout(cct, 20) << __func__ << " counting as trimmed: " << *in << dendl; - trimmed++; + if (!in->dirty_caps && !in->cap_snaps.size()) + trimmed++; } } } @@ -5559,11 +5575,6 @@ void Client::handle_cap_flush_ack(MetaSession *session, Inode *in, Cap *cap, con sync_cond.notify_all(); } - if (!dirty) { - in->cap_dirtier_uid = -1; - in->cap_dirtier_gid = -1; - } - if (!cleaned) { ldout(cct, 10) << " tid " << m->get_client_tid() << " != any cap bit tids" << dendl; } else { @@ -5875,13 +5886,73 @@ void Client::handle_cap_grant(MetaSession *session, Inode *in, Cap *cap, const M _try_to_trim_inode(in, true); } +int Client::mds_check_access(std::string& path, const UserPerm& perms, int mask) +{ + const gid_t *gids; + int count = perms.get_gids(&gids); + std::vector gid_list; + bool root_squash_perms = true; + MDSCapAuth *rw_perms_s = nullptr; + + ldout(cct, 25) << __func__ << " path " << path << ", uid " << perms.uid() + << ", gid " << perms.gid() << ", mask " << mask << dendl; + + if (count) { + gid_list.reserve(count); + for (int i = 0; i < count; ++i) { + gid_list.push_back(gids[i]); + } + } + + for (auto& s: cap_auths) { + ldout(cct, 20) << __func__ << " auth match path " << s.match.path << " r: " << s.readable + << " w: " << s.writeable << dendl; + ldout(cct, 20) << " match.uid " << s.match.uid << dendl; + if (s.match.match(path, perms.uid(), perms.gid(), &gid_list)) { + ldout(cct, 20) << " is matched" << dendl; + // always follow the last auth caps' permision + root_squash_perms = true; + rw_perms_s = nullptr; + + if ((mask & MAY_WRITE) && s.writeable && + s.match.root_squash && ((perms.uid() == 0) || (perms.gid() == 0))) { + root_squash_perms = false; + } + + if (((mask & MAY_WRITE) && !s.writeable) || + ((mask & MAY_READ) && !s.readable)) { + rw_perms_s = &s; + } + } else { + ldout(cct, 20) << " is mismatched" << dendl; + } + } + + if (root_squash_perms && rw_perms_s == nullptr) { + return 0; + } + + if (!root_squash_perms) { + ldout(cct, 10) << __func__ << " permission denied, root_squash is enabled and user" + << " (uid " << perms.uid() << ", gid " << perms.gid() + << ") isn't allowed to write" << dendl; + } + if (rw_perms_s) { + ldout(cct, 10) << __func__ << " permission denied, mds auth caps readable/writeable:" + << rw_perms_s->readable << "/" << rw_perms_s->writeable << ", request r/w:" + << !!(mask & MAY_READ) << "/" << !!(mask & MAY_WRITE) << dendl; + } + + return -CEPHFS_EACCES; +} + int Client::inode_permission(Inode *in, const UserPerm& perms, unsigned want) { if (perms.uid() == 0) { // For directories, DACs are overridable. // For files, Read/write DACs are always overridable but executable DACs are // overridable when there is at least one exec bit set - if(!S_ISDIR(in->mode) && (want & MAY_EXEC) && !(in->mode & S_IXUGO)) + if(!S_ISDIR(in->mode) && (want & CLIENT_MAY_EXEC) && !(in->mode & S_IXUGO)) return -CEPHFS_EACCES; return 0; } @@ -5907,7 +5978,7 @@ int Client::xattr_permission(Inode *in, const char *name, unsigned want, r = 0; if (strncmp(name, "system.", 7) == 0) { - if ((want & MAY_WRITE) && (perms.uid() != 0 && perms.uid() != in->uid)) + if ((want & CLIENT_MAY_WRITE) && (perms.uid() != 0 && perms.uid() != in->uid)) r = -CEPHFS_EPERM; } else { r = inode_permission(in, perms, want); @@ -5932,7 +6003,7 @@ int Client::may_setattr(Inode *in, struct ceph_statx *stx, int mask, goto out; if (mask & CEPH_SETATTR_SIZE) { - r = inode_permission(in, perms, MAY_WRITE); + r = inode_permission(in, perms, CLIENT_MAY_WRITE); if (r < 0) goto out; } @@ -5979,7 +6050,7 @@ int Client::may_setattr(Inode *in, struct ceph_statx *stx, int mask, if (check_mask & mask) { goto out; } else { - r = inode_permission(in, perms, MAY_WRITE); + r = inode_permission(in, perms, CLIENT_MAY_WRITE); if (r < 0) goto out; } @@ -5997,13 +6068,13 @@ int Client::may_open(Inode *in, int flags, const UserPerm& perms) unsigned want = 0; if ((flags & O_ACCMODE) == O_WRONLY) - want = MAY_WRITE; + want = CLIENT_MAY_WRITE; else if ((flags & O_ACCMODE) == O_RDWR) - want = MAY_READ | MAY_WRITE; + want = CLIENT_MAY_READ | CLIENT_MAY_WRITE; else if ((flags & O_ACCMODE) == O_RDONLY) - want = MAY_READ; + want = CLIENT_MAY_READ; if (flags & O_TRUNC) - want |= MAY_WRITE; + want |= CLIENT_MAY_WRITE; int r = 0; switch (in->mode & S_IFMT) { @@ -6011,7 +6082,7 @@ int Client::may_open(Inode *in, int flags, const UserPerm& perms) r = -CEPHFS_ELOOP; goto out; case S_IFDIR: - if (want & MAY_WRITE) { + if (want & CLIENT_MAY_WRITE) { r = -CEPHFS_EISDIR; goto out; } @@ -6035,7 +6106,7 @@ int Client::may_lookup(Inode *dir, const UserPerm& perms) if (r < 0) goto out; - r = inode_permission(dir, perms, MAY_EXEC); + r = inode_permission(dir, perms, CLIENT_MAY_EXEC); out: ldout(cct, 3) << __func__ << " " << dir << " = " << r << dendl; return r; @@ -6048,7 +6119,7 @@ int Client::may_create(Inode *dir, const UserPerm& perms) if (r < 0) goto out; - r = inode_permission(dir, perms, MAY_EXEC | MAY_WRITE); + r = inode_permission(dir, perms, CLIENT_MAY_EXEC | CLIENT_MAY_WRITE); out: ldout(cct, 3) << __func__ << " " << dir << " = " << r << dendl; return r; @@ -6061,7 +6132,7 @@ int Client::may_delete(Inode *dir, const char *name, const UserPerm& perms) if (r < 0) goto out; - r = inode_permission(dir, perms, MAY_EXEC | MAY_WRITE); + r = inode_permission(dir, perms, CLIENT_MAY_EXEC | CLIENT_MAY_WRITE); if (r < 0) goto out; @@ -6126,7 +6197,7 @@ int Client::may_hardlink(Inode *in, const UserPerm& perms) if ((in->mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) goto out; - r = inode_permission(in, perms, MAY_READ | MAY_WRITE); + r = inode_permission(in, perms, CLIENT_MAY_READ | CLIENT_MAY_WRITE); out: ldout(cct, 3) << __func__ << " " << in << " = " << r << dendl; return r; @@ -6354,6 +6425,12 @@ int Client::mds_command( populate_metadata(""); } + ceph_tid_t multi_target_id = 0; + if (non_laggy.size() > 1) { + std::scoped_lock cmd_lock(command_lock); + multi_target_id = command_table.get_new_multi_target_id(); + } + // Send commands to targets C_GatherBuilder gather(cct, onfinish); for (const auto& target_gid : non_laggy) { @@ -6366,7 +6443,7 @@ int Client::mds_command( { std::scoped_lock cmd_lock(command_lock); // Generate MDSCommandOp state - auto &op = command_table.start_command(); + auto &op = command_table.start_command(multi_target_id); op.on_finish = gather.new_sub(); op.cmd = cmd; @@ -6377,7 +6454,7 @@ int Client::mds_command( op.con = conn; ldout(cct, 4) << __func__ << ": new command op to " << target_gid - << " tid=" << op.tid << cmd << dendl; + << " tid=" << op.tid << " multi_id=" << op.multi_target_id << " "<< cmd << dendl; // Construct and send MCommand MessageRef m = op.get_message(monclient->get_fsid()); @@ -6403,8 +6480,33 @@ void Client::handle_command_reply(const MConstRef& m) } auto &op = command_table.get_command(tid); + ceph_tid_t multi_id = op.multi_target_id; + if (op.outbl) { - *op.outbl = m->get_data(); + if (multi_id != 0 && m->r == 0) { + string prefix; + string mds_name = fmt::format("mds.{}", fsmap->get_info_gid(op.mds_gid).name); + + if (op.outbl->length() == 0) { // very first command result + prefix = fmt::format("[{{\"{}\":", mds_name); + } + else { + prefix = fmt::format(",{{\"{}\":", mds_name); + } + op.outbl->append(prefix); + op.outbl->append(m->get_data()); + op.outbl->append("}"); + } + + if (multi_id == 0) { + *op.outbl = m->get_data(); + } + else { + // when this command is the last one + if (command_table.count_multi_commands(multi_id) == 1) { + op.outbl->append("]"); + } + } } if (op.outs) { *op.outs = m->rs; @@ -7121,7 +7223,9 @@ void Client::renew_caps(MetaSession *session) ldout(cct, 10) << "renew_caps mds." << session->mds_num << dendl; session->last_cap_renew_request = ceph_clock_now(); uint64_t seq = ++session->cap_renew_seq; - session->con->send_message2(make_message(CEPH_SESSION_REQUEST_RENEWCAPS, seq)); + auto m = make_message(CEPH_SESSION_REQUEST_RENEWCAPS, seq); + m->oldest_client_tid = oldest_tid; + session->con->send_message2(std::move(m)); } @@ -7883,6 +7987,20 @@ int Client::_getvxattr( return res; } +bool Client::make_absolute_path_string(Inode *in, std::string& path) +{ + if (!metadata.count("root") || !in) + return false; + + path = metadata["root"].data(); + if (!in->make_path_string(path)) { + path.clear(); + return false; + } + + return true; +} + int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, const UserPerm& perms, InodeRef *inp, std::vector* aux) @@ -7892,12 +8010,14 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, bool kill_sguid = false; int inode_drop = 0; size_t auxsize = 0; + filepath path; + MetaRequest *req; if (aux) auxsize = aux->size(); ldout(cct, 10) << __func__ << " mask " << mask << " issued " << - ccap_string(issued) << " aux size " << auxsize << dendl; + ccap_string(issued) << " aux size " << auxsize << " perms " << perms << dendl; if (in->snapid != CEPH_NOSNAP) { return -CEPHFS_EROFS; @@ -7919,32 +8039,25 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, memset(&args, 0, sizeof(args)); - // make the change locally? - if ((in->cap_dirtier_uid >= 0 && perms.uid() != in->cap_dirtier_uid) || - (in->cap_dirtier_gid >= 0 && perms.gid() != in->cap_dirtier_gid)) { - ldout(cct, 10) << __func__ << " caller " << perms.uid() << ":" << perms.gid() - << " != cap dirtier " << in->cap_dirtier_uid << ":" - << in->cap_dirtier_gid << ", forcing sync setattr" - << dendl; - /* - * This works because we implicitly flush the caps as part of the - * request, so the cap update check will happen with the writeback - * cap context, and then the setattr check will happen with the - * caller's context. - * - * In reality this pattern is likely pretty rare (different users - * setattr'ing the same file). If that turns out not to be the - * case later, we can build a more complex pipelined cap writeback - * infrastructure... - */ - mask |= CEPH_SETATTR_CTIME; + bool do_sync = true; + int res; + { + std::string path; + if (make_absolute_path_string(in, path)) { + ldout(cct, 20) << " absolute path: " << path << dendl; + if (path.length()) + path = path.substr(1); // drop leading / + res = mds_check_access(path, perms, MAY_WRITE); + if (res) { + goto out; + } + do_sync = false; + } } if (!mask) { // caller just needs us to bump the ctime in->ctime = ceph_clock_now(); - in->cap_dirtier_uid = perms.uid(); - in->cap_dirtier_gid = perms.gid(); if (issued & CEPH_CAP_AUTH_EXCL) in->mark_caps_dirty(CEPH_CAP_AUTH_EXCL); else if (issued & CEPH_CAP_FILE_EXCL) @@ -7962,10 +8075,8 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, if (mask & CEPH_SETATTR_UID) { ldout(cct,10) << "changing uid to " << stx->stx_uid << dendl; - if (in->caps_issued_mask(CEPH_CAP_AUTH_EXCL)) { + if (!do_sync && 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->uid = stx->stx_uid; in->mark_caps_dirty(CEPH_CAP_AUTH_EXCL); mask &= ~CEPH_SETATTR_UID; @@ -7982,10 +8093,8 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, if (mask & CEPH_SETATTR_GID) { ldout(cct,10) << "changing gid to " << stx->stx_gid << dendl; - if (in->caps_issued_mask(CEPH_CAP_AUTH_EXCL)) { + if (!do_sync && 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->gid = stx->stx_gid; in->mark_caps_dirty(CEPH_CAP_AUTH_EXCL); mask &= ~CEPH_SETATTR_GID; @@ -8002,10 +8111,8 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, if (mask & CEPH_SETATTR_MODE) { ldout(cct,10) << "changing mode to " << stx->stx_mode << dendl; - if (in->caps_issued_mask(CEPH_CAP_AUTH_EXCL)) { + if (!do_sync && 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->mode = (in->mode & ~07777) | (stx->stx_mode & 07777); in->mark_caps_dirty(CEPH_CAP_AUTH_EXCL); mask &= ~CEPH_SETATTR_MODE; @@ -8016,7 +8123,7 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, } else { mask &= ~CEPH_SETATTR_MODE; } - } else if (in->caps_issued_mask(CEPH_CAP_AUTH_EXCL) && S_ISREG(in->mode)) { + } else if (!do_sync && in->caps_issued_mask(CEPH_CAP_AUTH_EXCL) && S_ISREG(in->mode)) { if (kill_sguid && (in->mode & (S_IXUSR|S_IXGRP|S_IXOTH))) { in->mode &= ~(S_ISUID|S_ISGID); } else { @@ -8034,10 +8141,8 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, if (mask & CEPH_SETATTR_BTIME) { ldout(cct,10) << "changing btime to " << in->btime << dendl; - if (in->caps_issued_mask(CEPH_CAP_AUTH_EXCL)) { + if (!do_sync && 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->btime = utime_t(stx->stx_btime); in->mark_caps_dirty(CEPH_CAP_AUTH_EXCL); mask &= ~CEPH_SETATTR_BTIME; @@ -8054,10 +8159,8 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, ldout(cct,10) << "resetting cached fscrypt_auth field. size now " << in->fscrypt_auth.size() << dendl; - if (in->caps_issued_mask(CEPH_CAP_AUTH_EXCL)) { + if (!do_sync && 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; @@ -8077,13 +8180,11 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, } ldout(cct,10) << "changing size to " << stx->stx_size << dendl; - if (in->caps_issued_mask(CEPH_CAP_FILE_EXCL) && + if (!do_sync && in->caps_issued_mask(CEPH_CAP_FILE_EXCL) && !(mask & CEPH_SETATTR_KILL_SGUID) && stx->stx_size >= in->size) { if (stx->stx_size > in->size) { in->size = in->reported_size = stx->stx_size; - in->cap_dirtier_uid = perms.uid(); - in->cap_dirtier_gid = perms.gid(); in->mark_caps_dirty(CEPH_CAP_FILE_EXCL); mask &= ~(CEPH_SETATTR_SIZE); mask |= CEPH_SETATTR_MTIME; @@ -8102,10 +8203,8 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, ldout(cct,10) << "resetting cached fscrypt_file field. size now " << in->fscrypt_file.size() << dendl; - if (in->caps_issued_mask(CEPH_CAP_FILE_EXCL)) { + if (!do_sync && 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; @@ -8118,20 +8217,16 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, } if (mask & CEPH_SETATTR_MTIME) { - if (in->caps_issued_mask(CEPH_CAP_FILE_EXCL)) { + if (!do_sync && in->caps_issued_mask(CEPH_CAP_FILE_EXCL)) { in->mtime = utime_t(stx->stx_mtime); in->ctime = ceph_clock_now(); - in->cap_dirtier_uid = perms.uid(); - in->cap_dirtier_gid = perms.gid(); in->time_warp_seq++; in->mark_caps_dirty(CEPH_CAP_FILE_EXCL); mask &= ~CEPH_SETATTR_MTIME; - } else if (in->caps_issued_mask(CEPH_CAP_FILE_WR) && + } else if (!do_sync && in->caps_issued_mask(CEPH_CAP_FILE_WR) && utime_t(stx->stx_mtime) > in->mtime) { in->mtime = utime_t(stx->stx_mtime); in->ctime = ceph_clock_now(); - in->cap_dirtier_uid = perms.uid(); - in->cap_dirtier_gid = perms.gid(); in->mark_caps_dirty(CEPH_CAP_FILE_WR); mask &= ~CEPH_SETATTR_MTIME; } else if (!in->caps_issued_mask(CEPH_CAP_FILE_SHARED) || @@ -8145,20 +8240,16 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, } if (mask & CEPH_SETATTR_ATIME) { - if (in->caps_issued_mask(CEPH_CAP_FILE_EXCL)) { + if (!do_sync && in->caps_issued_mask(CEPH_CAP_FILE_EXCL)) { in->atime = utime_t(stx->stx_atime); in->ctime = ceph_clock_now(); - in->cap_dirtier_uid = perms.uid(); - in->cap_dirtier_gid = perms.gid(); in->time_warp_seq++; in->mark_caps_dirty(CEPH_CAP_FILE_EXCL); mask &= ~CEPH_SETATTR_ATIME; - } else if (in->caps_issued_mask(CEPH_CAP_FILE_WR) && + } else if (!do_sync && in->caps_issued_mask(CEPH_CAP_FILE_WR) && utime_t(stx->stx_atime) > in->atime) { in->atime = utime_t(stx->stx_atime); in->ctime = ceph_clock_now(); - in->cap_dirtier_uid = perms.uid(); - in->cap_dirtier_gid = perms.gid(); in->mark_caps_dirty(CEPH_CAP_FILE_WR); mask &= ~CEPH_SETATTR_ATIME; } else if (!in->caps_issued_mask(CEPH_CAP_FILE_SHARED) || @@ -8182,9 +8273,7 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, return 0; } - MetaRequest *req = new MetaRequest(CEPH_MDS_OP_SETATTR); - - filepath path; + req = new MetaRequest(CEPH_MDS_OP_SETATTR); in->make_nosnap_relative_path(path); req->set_filepath(path); @@ -8200,7 +8289,9 @@ int Client::_do_setattr(Inode *in, struct ceph_statx *stx, int mask, req->head.args.setattr.mask = mask; req->regetattr_mask = mask; - int res = make_request(req, perms, inp); + res = make_request(req, perms, inp); + +out: ldout(cct, 10) << "_setattr result=" << res << dendl; return res; } @@ -9010,7 +9101,9 @@ int Client::fdopendir(int dirfd, dir_result_t **dirpp, const UserPerm &perms) { return r; } } - r = _opendir(dirinode.get(), dirpp, perms); + // Posix says that closedir will also close the file descriptor passed to fdopendir, so we associate + // dirfd to the new dir_result_t so that it can be closed later. + r = _opendir(dirinode.get(), dirpp, perms, dirfd); /* if ENOTDIR, dirpp will be an uninitialized point and it's very dangerous to access its value */ if (r != -CEPHFS_ENOTDIR) { tout(cct) << (uintptr_t)*dirpp << std::endl; @@ -9018,11 +9111,11 @@ int Client::fdopendir(int dirfd, dir_result_t **dirpp, const UserPerm &perms) { return r; } -int Client::_opendir(Inode *in, dir_result_t **dirpp, const UserPerm& perms) +int Client::_opendir(Inode *in, dir_result_t **dirpp, const UserPerm& perms, int fd) { if (!in->is_dir()) return -CEPHFS_ENOTDIR; - *dirpp = new dir_result_t(in, perms); + *dirpp = new dir_result_t(in, perms, fd); opened_dirs.insert(*dirpp); ldout(cct, 8) << __func__ << "(" << in->ino << ") = " << 0 << " (" << *dirpp << ")" << dendl; return 0; @@ -9050,6 +9143,12 @@ void Client::_closedir(dir_result_t *dirp) } _readdir_drop_dirp_buffer(dirp); opened_dirs.erase(dirp); + + /* Close the associated fd if this dir_result_t comes from an fdopendir request. */ + if (dirp->fd >= 0) { + _close(dirp->fd); + } + delete dirp; } @@ -9269,7 +9368,7 @@ int Client::_readdir_cache_cb(dir_result_t *dirp, add_dirent_cb_t cb, void *p, } int idx = pd - dir->readdir_cache.begin(); - if (dn->inode->is_dir()) { + if (dn->inode->is_dir() && cct->_conf->client_dirsize_rbytes) { mask |= CEPH_STAT_RSTAT; } int r = _getattr(dn->inode, mask, dirp->perms); @@ -9377,6 +9476,7 @@ int Client::_readdir_r_cb(int op, bool bypass_cache) { int caps = statx_to_mask(flags, want); + int rstat_on_dir = cct->_conf->client_dirsize_rbytes ? CEPH_STAT_RSTAT : 0; RWRef_t mref_reader(mount_state, CLIENT_MOUNTING); if (!mref_reader.is_state_satisfied()) @@ -9406,7 +9506,7 @@ int Client::_readdir_r_cb(int op, uint64_t next_off = 1; int r; - r = _getattr(diri, caps | CEPH_STAT_RSTAT, dirp->perms); + r = _getattr(diri, caps | rstat_on_dir, dirp->perms); if (r < 0) return r; @@ -9439,7 +9539,7 @@ int Client::_readdir_r_cb(int op, in = diri->get_first_parent()->dir->parent_inode; int r; - r = _getattr(in, caps | CEPH_STAT_RSTAT, dirp->perms); + r = _getattr(in, caps | rstat_on_dir, dirp->perms); if (r < 0) return r; @@ -9510,7 +9610,7 @@ int Client::_readdir_r_cb(int op, if (check_caps) { int mask = caps; if(entry.inode->is_dir()){ - mask |= CEPH_STAT_RSTAT; + mask |= rstat_on_dir; } r = _getattr(entry.inode, mask, dirp->perms); if (r < 0) @@ -10181,11 +10281,30 @@ int Client::_open(Inode *in, int flags, mode_t mode, Fh **fhp, in->get_open_ref(cmode); // make note of pending open, since it effects _wanted_ caps. + int do_sync = true; if ((flags & O_TRUNC) == 0 && in->caps_issued_mask(want)) { - // update wanted? - check_caps(in, CHECK_CAPS_NODELAY); - } else { + int mask = MAY_READ; + if (cmode & CEPH_FILE_MODE_WR) { + mask |= MAY_WRITE; + } + + std::string path; + if (make_absolute_path_string(in, path)) { + ldout(cct, 20) << __func__ << " absolute path: " << path << dendl; + if (path.length()) + path = path.substr(1); // drop leading / + result = mds_check_access(path, perms, mask); + if (result) { + return result; + } + // update wanted? + check_caps(in, CHECK_CAPS_NODELAY); + do_sync = false; + } + } + + if (do_sync) { MetaRequest *req = new MetaRequest(CEPH_MDS_OP_OPEN); filepath path; in->make_nosnap_relative_path(path); @@ -12966,7 +13085,9 @@ int Client::_getxattr(Inode *in, const char *name, void *value, size_t size, if (!strncmp(name, "ceph.", 5)) { r = _getvxattr(in, perms, name, size, value, MDS_RANK_NONE); - goto out; + if (r != -ENODATA) { + goto out; + } } if (acl_type == NO_ACL && !strncmp(name, "system.", 7)) { @@ -12997,7 +13118,7 @@ int Client::_getxattr(InodeRef &in, const char *name, void *value, size_t size, const UserPerm& perms) { if (cct->_conf->client_permissions) { - int r = xattr_permission(in.get(), name, MAY_READ, perms); + int r = xattr_permission(in.get(), name, CLIENT_MAY_READ, perms); if (r < 0) return r; } @@ -13020,7 +13141,7 @@ int Client::ll_getxattr(Inode *in, const char *name, void *value, std::scoped_lock lock(client_lock); if (!fuse_default_permissions) { - int r = xattr_permission(in, name, MAY_READ, perms); + int r = xattr_permission(in, name, CLIENT_MAY_READ, perms); if (r < 0) return r; } @@ -13200,7 +13321,7 @@ int Client::_setxattr(InodeRef &in, const char *name, const void *value, size_t size, int flags, const UserPerm& perms) { if (cct->_conf->client_permissions) { - int r = xattr_permission(in.get(), name, MAY_WRITE, perms); + int r = xattr_permission(in.get(), name, CLIENT_MAY_WRITE, perms); if (r < 0) return r; } @@ -13288,7 +13409,7 @@ int Client::ll_setxattr(Inode *in, const char *name, const void *value, std::scoped_lock lock(client_lock); if (!fuse_default_permissions) { - int r = xattr_permission(in, name, MAY_WRITE, perms); + int r = xattr_permission(in, name, CLIENT_MAY_WRITE, perms); if (r < 0) return r; } @@ -13303,10 +13424,11 @@ int Client::_removexattr(Inode *in, const char *name, const UserPerm& perms) // same xattrs supported by kernel client if (strncmp(name, "user.", 5) && - strncmp(name, "system.", 7) && strncmp(name, "security.", 9) && strncmp(name, "trusted.", 8) && - strncmp(name, "ceph.", 5)) + strncmp(name, "ceph.", 5) && + strcmp(name, ACL_EA_ACCESS) && + strcmp(name, ACL_EA_DEFAULT)) return -CEPHFS_EOPNOTSUPP; const VXattr *vxattr = _match_vxattr(in, name); @@ -13322,6 +13444,11 @@ int Client::_removexattr(Inode *in, const char *name, const UserPerm& perms) int res = make_request(req, perms); + if ((!strcmp(name, ACL_EA_ACCESS) || + !strcmp(name, ACL_EA_DEFAULT)) && + res == -CEPHFS_ENODATA) + res = 0; + trim_cache(); ldout(cct, 8) << "_removexattr(" << in->ino << ", \"" << name << "\") = " << res << dendl; return res; @@ -13330,7 +13457,7 @@ int Client::_removexattr(Inode *in, const char *name, const UserPerm& perms) int Client::_removexattr(InodeRef &in, const char *name, const UserPerm& perms) { if (cct->_conf->client_permissions) { - int r = xattr_permission(in.get(), name, MAY_WRITE, perms); + int r = xattr_permission(in.get(), name, CLIENT_MAY_WRITE, perms); if (r < 0) return r; } @@ -13352,7 +13479,7 @@ int Client::ll_removexattr(Inode *in, const char *name, const UserPerm& perms) std::scoped_lock lock(client_lock); if (!fuse_default_permissions) { - int r = xattr_permission(in, name, MAY_WRITE, perms); + int r = xattr_permission(in, name, CLIENT_MAY_WRITE, perms); if (r < 0) return r; } diff --git a/src/client/Client.h b/src/client/Client.h index 911a8b460..beeef0803 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -32,6 +32,7 @@ #include "include/unordered_set.h" #include "include/cephfs/metrics/Types.h" #include "mds/mdstypes.h" +#include "mds/MDSAuthCaps.h" #include "include/cephfs/types.h" #include "msg/Dispatcher.h" #include "msg/MessageRef.h" @@ -97,6 +98,7 @@ class MDSCommandOp : public CommandOp mds_gid_t mds_gid; explicit MDSCommandOp(ceph_tid_t t) : CommandOp(t) {} + explicit MDSCommandOp(ceph_tid_t t, ceph_tid_t multi_id) : CommandOp(t, multi_id) {} }; /* error code for ceph_fuse */ @@ -161,7 +163,7 @@ struct dir_result_t { }; - explicit dir_result_t(Inode *in, const UserPerm& perms); + explicit dir_result_t(Inode *in, const UserPerm& perms, int fd); static uint64_t make_fpos(unsigned h, unsigned l, bool hash) { @@ -238,6 +240,8 @@ struct dir_result_t { std::vector buffer; struct dirent de; + + int fd; // fd attached using fdopendir (-1 if none) }; class Client : public Dispatcher, public md_config_obs_t { @@ -550,6 +554,9 @@ public: mode_t mode=0, const std::map &metadata={}); int rmsnap(const char *path, const char *name, const UserPerm& perm, bool check_perms=false); + // cephx mds auth caps checking + int mds_check_access(std::string& path, const UserPerm& perms, int mask); + // Inode permission checking int inode_permission(Inode *in, const UserPerm& perms, unsigned want); @@ -695,6 +702,7 @@ public: client_t get_nodeid() { return whoami; } + inodeno_t _get_root_ino(bool fake=true); inodeno_t get_root_ino(); Inode *get_root(); @@ -1274,9 +1282,9 @@ private: }; enum { - MAY_EXEC = 1, - MAY_WRITE = 2, - MAY_READ = 4, + CLIENT_MAY_EXEC = 1, + CLIENT_MAY_WRITE = 2, + CLIENT_MAY_READ = 4, }; typedef std::function fill_readdir_args_cb_t; @@ -1296,7 +1304,7 @@ private: void fill_dirent(struct dirent *de, const char *name, int type, uint64_t ino, loff_t next_off); - int _opendir(Inode *in, dir_result_t **dirpp, const UserPerm& perms); + int _opendir(Inode *in, dir_result_t **dirpp, const UserPerm& perms, int fd = -1); void _readdir_drop_dirp_buffer(dir_result_t *dirp); bool _readdir_have_frag(dir_result_t *dirp); void _readdir_next_frag(dir_result_t *dirp); @@ -1356,6 +1364,7 @@ private: const UserPerm& perms, std::string alternate_name, InodeRef *inp = 0); int _mknod(Inode *dir, const char *name, mode_t mode, dev_t rdev, const UserPerm& perms, InodeRef *inp = 0); + bool make_absolute_path_string(Inode *in, std::string& path); int _do_setattr(Inode *in, struct ceph_statx *stx, int mask, const UserPerm& perms, InodeRef *inp, std::vector* aux=nullptr); @@ -1628,6 +1637,8 @@ private: uint64_t nr_metadata_request = 0; uint64_t nr_read_request = 0; uint64_t nr_write_request = 0; + + std::vector cap_auths; }; /** diff --git a/src/client/Dentry.h b/src/client/Dentry.h index 8003dfed3..47d320ecb 100644 --- a/src/client/Dentry.h +++ b/src/client/Dentry.h @@ -77,6 +77,22 @@ public: dir = nullptr; } + bool make_path_string(std::string& s) + { + bool ret = false; + + if (dir) { + ret = dir->parent_inode->make_path_string(s); + } else { + // Couldn't link all the way to our mount point + return false; + } + s += "/"; + s.append(name.data(), name.length()); + + return ret; + } + void dump(Formatter *f) const; friend std::ostream &operator<<(std::ostream &oss, const Dentry &Dentry); diff --git a/src/client/Inode.cc b/src/client/Inode.cc index 0c19bef5e..cb30d4940 100644 --- a/src/client/Inode.cc +++ b/src/client/Inode.cc @@ -119,6 +119,22 @@ void Inode::make_short_path(filepath& p) p = filepath(ino); } +/* + * make a filepath suitable for mds auth access check: + */ +bool Inode::make_path_string(std::string& s) +{ + if (client->_get_root_ino(false) == ino) { + return true; + } else if (!dentries.empty()) { + Dentry *dn = get_first_parent(); + ceph_assert(dn->dir && dn->dir->parent_inode); + return dn->make_path_string(s); + } + + return false; +} + /* * make a filepath suitable for an mds request: * - if we are non-snapped/live, the ino is sufficient, e.g. #1234 diff --git a/src/client/Inode.h b/src/client/Inode.h index d28ff2e08..a2f6c15be 100644 --- a/src/client/Inode.h +++ b/src/client/Inode.h @@ -97,9 +97,6 @@ struct CapSnap { bool writing = false, dirty_data = false; uint64_t flush_tid = 0; - int64_t cap_dirtier_uid = -1; - int64_t cap_dirtier_gid = -1; - explicit CapSnap(Inode *i) : in(i) {} @@ -252,6 +249,7 @@ struct Inode : RefCountedObject { void make_long_path(filepath& p); void make_short_path(filepath& p); + bool make_path_string(std::string& s); void make_nosnap_relative_path(filepath& p); // The ref count. 1 for each dentry, fh, inode_map, diff --git a/src/client/fuse_ll.cc b/src/client/fuse_ll.cc index 7f92dd668..0e2e20064 100644 --- a/src/client/fuse_ll.cc +++ b/src/client/fuse_ll.cc @@ -753,6 +753,15 @@ static void fuse_ll_rename(fuse_req_t req, fuse_ino_t parent, const char *name, #endif ) { +#if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) + // cephfs does not support renameat2 flavors; follow same logic as done in + // kclient's ceph_rename() + if (flags) { + fuse_reply_err(req, get_sys_errno(CEPHFS_EINVAL)); + return; + } +#endif + CephFuse::Handle *cfuse = fuse_ll_req_prepare(req); const struct fuse_ctx *ctx = fuse_req_ctx(req); UserPerm perm(ctx->uid, ctx->gid); -- cgit v1.2.3