Skip to content

Commit 56e0f73

Browse files
mds/MDSAuthCaps: allow adding multiple caps via "fs auth" cmd
When multiple caps are passed to "ceph fs authorize" command, the command hangs because the MON crashes due to this command. Underlying code assumes that only one cap can be passed at a time by the user through the "ceph fs authorize" command. "ceph_assert()" checks if only one cap is passed. If not, the code crashes. Fixes: https://tracker.ceph.com/issues/64127 Signed-off-by: Rishabh Dave <[email protected]>
1 parent f33cc33 commit 56e0f73

File tree

3 files changed

+54
-7
lines changed

3 files changed

+54
-7
lines changed

qa/tasks/cephfs/test_admin.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,6 +1480,26 @@ def test_when_MDS_caps_needs_update_but_others_dont(self):
14801480
FS_AUTH_CAPS[2], self.captesters[2], self.fs2.name, self.mount_b,
14811481
keyring)
14821482

1483+
def test_adding_multiple_caps(self):
1484+
'''
1485+
Test that the command "ceph fs authorize" is successful in updating
1486+
the entity's caps when multiple caps are passed to it in one go.
1487+
1488+
Tests: https://tracker.ceph.com/issues/64127
1489+
'''
1490+
DIR = 'dir1'
1491+
self.mount_a.run_shell(f'mkdir {DIR}')
1492+
self.captesters = (CapTester(self.mount_a, '/'),
1493+
CapTester(self.mount_a, f'/{DIR}'))
1494+
self.fs.authorize(self.client_id, ('/', 'rw'))
1495+
1496+
FS_AUTH_CAPS = ('/', 'rw', 'root_squash'), (f'/{DIR}', 'rw' )
1497+
# The fact that following line passes means
1498+
# https://tracker.ceph.com/issues/64127 has been fixed
1499+
keyring = self.fs.authorize(self.client_id, FS_AUTH_CAPS)
1500+
1501+
self._remount_and_run_tests(FS_AUTH_CAPS, keyring)
1502+
14831503
def _remount_and_run_tests(self, fs_auth_caps, keyring):
14841504
'''
14851505
This method is specifically designed to meet needs of most of the

src/mds/MDSAuthCaps.cc

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -382,25 +382,28 @@ bool MDSAuthCaps::parse(string_view str, ostream *err)
382382
}
383383
}
384384

385-
bool MDSAuthCaps::merge(MDSAuthCaps newcap)
385+
/* Check if the "cap grant" is already present in this cap object. If it is,
386+
* return false. If not, add it and return true.
387+
*
388+
* ng = new grant, new mds cap grant.
389+
*/
390+
bool MDSAuthCaps::merge_one_cap_grant(MDSCapGrant ng)
386391
{
387-
ceph_assert(newcap.grants.size() == 1);
388-
auto ng = newcap.grants[0];
389-
392+
// check if "ng" is already present in this cap object.
390393
for (auto& g : grants) {
391394
if (g.match.fs_name == ng.match.fs_name && g.match.path == ng.match.path) {
392395
if (g.spec.get_caps() == ng.spec.get_caps()) {
393396
// no update required. maintaining idempotency.
394397
return false;
395398
} else {
396-
// cap for given fs name is present, let's update it.
399+
// "ng" is present but perm/spec is different. update it.
397400
g.spec.set_caps(ng.spec.get_caps());
398401
return true;
399402
}
400403
}
401404
}
402405

403-
// cap for given fs name and/or path is absent, let's add a new cap for it.
406+
// since "ng" is absent in this cap object, add it.
404407
grants.push_back(MDSCapGrant(
405408
MDSCapSpec(ng.spec.get_caps()),
406409
MDSCapMatch(ng.match.fs_name, ng.match.path, ng.match.root_squash),
@@ -409,6 +412,29 @@ bool MDSAuthCaps::merge(MDSAuthCaps newcap)
409412
return true;
410413
}
411414

415+
/* User can pass one or MDS caps that it wishes to add to entity's keyring.
416+
* Merge all of these caps one by one. Return value indicates whether or not
417+
* AuthMonitor must update the entity's keyring.
418+
*
419+
* If all caps do not merge (that is, underlying helper method returns false
420+
* after attempting merge), no update is required. Return false so that
421+
* AuthMonitor doesn't run the update procedure for caps.
422+
*
423+
* If even one cap is merged (that is, underlying method returns true even
424+
* once), an update to the entity's keyring is required. Return true so that
425+
* AuthMonitor runs the update procedure.
426+
*/
427+
bool MDSAuthCaps::merge(MDSAuthCaps newcaps)
428+
{
429+
bool were_caps_merged = false;
430+
431+
for (auto& ng : newcaps.grants) {
432+
were_caps_merged |= merge_one_cap_grant(ng);
433+
}
434+
435+
return were_caps_merged;
436+
}
437+
412438
string MDSCapMatch::to_string()
413439
{
414440
string str = "";

src/mds/MDSAuthCaps.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,8 @@ class MDSAuthCaps
259259

260260
void set_allow_all();
261261
bool parse(std::string_view str, std::ostream *err);
262-
bool merge(MDSAuthCaps newcap);
262+
bool merge_one_cap_grant(MDSCapGrant ng);
263+
bool merge(MDSAuthCaps newcaps);
263264

264265
bool allow_all() const;
265266
bool is_capable(std::string_view inode_path,

0 commit comments

Comments
 (0)