summaryrefslogtreecommitdiffstats
path: root/src/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/client')
-rw-r--r--src/client/Client.cc365
-rw-r--r--src/client/Client.h21
-rw-r--r--src/client/Dentry.h16
-rw-r--r--src/client/Inode.cc16
-rw-r--r--src/client/Inode.h4
-rw-r--r--src/client/fuse_ll.cc9
6 files changed, 304 insertions, 127 deletions
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<MClientSession>& 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<MClientCaps>(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<uint64_t> 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<MCommandReply>& 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<MClientSession>(CEPH_SESSION_REQUEST_RENEWCAPS, seq));
+ auto m = make_message<MClientSession>(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<uint8_t>* 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<dentry> 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<std::string, std::string> &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<void(dir_result_t*, MetaRequest*, InodeRef&, frag_t)> 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<uint8_t>* 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<MDSCapAuth> 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
@@ -120,6 +120,22 @@ void Inode::make_short_path(filepath& p)
}
/*
+ * 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
* - if we are snapped, make filepath relative to first non-snapped parent.
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);