The CephFS kernel client has regression starting from 6.18-rc1. We have issue in ceph_mds_auth_match() if fs_name == NULL: const char fs_name = mdsc->fsc->mount_options->mds_namespace; ... if (auth->match.fs_name && strcmp(auth->match.fs_name, fs_name)) { / fsname mismatch, try next one */ return 0; } Patrick Donnelly suggested that: In summary, we should definitely start decoding `fs_name` from the MDSMap and do strict authorizations checks against it. Note that the `-o mds_namespace=foo` should only be used for selecting the file system to mount and nothing else. It's possible no mds_namespace is specified but the kernel will mount the only file system that exists which may have name "foo". This patch reworks ceph_mdsmap_decode() and namespace_equals() with the goal of supporting the suggested concept. Now struct ceph_mdsmap contains m_fs_name field that receives copy of extracted FS name by ceph_extract_encoded_string(). For the case of "old" CephFS file systems, it is used "cephfs" name. [ idryomov: replace redundant %*pE with %s in ceph_mdsmap_decode(), get rid of a series of strlen() calls in ceph_namespace_match(), drop changes to namespace_equals() body to avoid treating empty mds_namespace as equal, drop changes to ceph_mdsc_handle_fsmap() as namespace_equals() isn't an equivalent substitution there ]
Unified · Split
| @@ -5671,7 +5671,7 @@ static int ceph_mds_auth_match(struct ceph_mds_client *mdsc, · 5670 unmodified lines | |||
| 5671 | u32 caller_uid = from_kuid(&init_user_ns, cred->fsuid); | 5671 | u32 caller_uid = from_kuid(&init_user_ns, cred->fsuid); |
| 5672 | u32 caller_gid = from_kgid(&init_user_ns, cred->fsgid); | 5672 | u32 caller_gid = from_kgid(&init_user_ns, cred->fsgid); |
| 5673 | struct ceph_client *cl = mdsc->fsc->client; | 5673 | struct ceph_client *cl = mdsc->fsc->client; |
| 5674 | const char *fs_name = mdsc->fsc->mount_options->mds_namespace; | 5674 | const char *fs_name = mdsc->mdsmap->m_fs_name; |
| 5675 | const char *spath = mdsc->fsc->mount_options->server_path; | 5675 | const char *spath = mdsc->fsc->mount_options->server_path; |
| 5676 | bool gid_matched = false; | 5676 | bool gid_matched = false; |
| 5677 | u32 gid, tlen, len; | 5677 | u32 gid, tlen, len; |
| @@ -5679,7 +5679,8 @@ static int ceph_mds_auth_match(struct ceph_mds_client *mdsc, · 1 unmodified line | |||
| 5679 | 5679 | ||
| 5680 | doutc(cl, "fsname check fs_name=%s match.fs_name=%s\n", | 5680 | doutc(cl, "fsname check fs_name=%s match.fs_name=%s\n", |
| 5681 | fs_name, auth->match.fs_name ? auth->match.fs_name : ""); | 5681 | fs_name, auth->match.fs_name ? auth->match.fs_name : ""); |
| 5682 | if (auth->match.fs_name && strcmp(auth->match.fs_name, fs_name)) { | 5682 | |
| 5683 | if (!ceph_namespace_match(auth->match.fs_name, fs_name)) { | ||
| 5683 | /* fsname mismatch, try next one */ | 5684 | /* fsname mismatch, try next one */ |
| 5684 | return 0; | 5685 | return 0; |
| 5685 | } | 5686 | } |
| @@ -353,22 +353,33 @@ struct ceph_mdsmap *ceph_mdsmap_decode(struct ceph_mds_client *mdsc, void **p, · 352 unmodified lines | |||
| 353 | __decode_and_drop_type(p, end, u8, bad_ext); | 353 | __decode_and_drop_type(p, end, u8, bad_ext); |
| 354 | } | 354 | } |
| 355 | if (mdsmap_ev >= 8) { | 355 | if (mdsmap_ev >= 8) { |
| 356 | u32 fsname_len; | 356 | size_t fsname_len; |
| 357 | |||
| 357 | /* enabled */ | 358 | /* enabled */ |
| 358 | ceph_decode_8_safe(p, end, m->m_enabled, bad_ext); | 359 | ceph_decode_8_safe(p, end, m->m_enabled, bad_ext); |
| 360 | |||
| 359 | /* fs_name */ | 361 | /* fs_name */ |
| 360 | ceph_decode_32_safe(p, end, fsname_len, bad_ext); | 362 | m->m_fs_name = ceph_extract_encoded_string(p, end, |
| 363 | &fsname_len, | ||
| 364 | GFP_NOFS); | ||
| 365 | if (IS_ERR(m->m_fs_name)) { | ||
| 366 | m->m_fs_name = NULL; | ||
| 367 | goto nomem; | ||
| 368 | } | ||
| 361 | 369 | ||
| 362 | /* validate fsname against mds_namespace */ | 370 | /* validate fsname against mds_namespace */ |
| 363 | if (!namespace_equals(mdsc->fsc->mount_options, *p, | 371 | if (!namespace_equals(mdsc->fsc->mount_options, m->m_fs_name, |
| 364 | fsname_len)) { | 372 | fsname_len)) { |
| 365 | pr_warn_client(cl, "fsname %*pE doesn't match mds_namespace %s\n", | 373 | pr_warn_client(cl, "fsname %s doesn't match mds_namespace %s\n", |
| 366 | (int)fsname_len, (char *)*p, | 374 | m->m_fs_name, |
| 367 | mdsc->fsc->mount_options->mds_namespace); | 375 | mdsc->fsc->mount_options->mds_namespace); |
| 368 | goto bad; | 376 | goto bad; |
| 369 | } | 377 | } |
| 370 | /* skip fsname after validation */ | 378 | } else { |
| 371 | ceph_decode_skip_n(p, end, fsname_len, bad); | 379 | m->m_enabled = false; |
| 380 | m->m_fs_name = kstrdup(CEPH_OLD_FS_NAME, GFP_NOFS); | ||
| 381 | if (!m->m_fs_name) | ||
| 382 | goto nomem; | ||
| 372 | } | 383 | } |
| 373 | /* damaged */ | 384 | /* damaged */ |
| 374 | if (mdsmap_ev >= 9) { | 385 | if (mdsmap_ev >= 9) { |
| @@ -430,6 +441,7 @@ void ceph_mdsmap_destroy(struct ceph_mdsmap *m) · 55 unmodified lines | |||
| 430 | kfree(m->m_info); | 441 | kfree(m->m_info); |
| 431 | } | 442 | } |
| 432 | kfree(m->m_data_pg_pools); | 443 | kfree(m->m_data_pg_pools); |
| 444 | kfree(m->m_fs_name); | ||
| 433 | kfree(m); | 445 | kfree(m); |
| 434 | } | 446 | } |
| 435 | 447 | ||
| @@ -45,6 +45,7 @@ struct ceph_mdsmap { · 44 unmodified lines | |||
| 45 | bool m_enabled; | 45 | bool m_enabled; |
| 46 | bool m_damaged; | 46 | bool m_damaged; |
| 47 | int m_num_laggy; | 47 | int m_num_laggy; |
| 48 | char *m_fs_name; | ||
| 48 | }; | 49 | }; |
| 49 | 50 | ||
| 50 | static inline struct ceph_entity_addr * | 51 | static inline struct ceph_entity_addr * |
| @@ -104,14 +104,26 @@ struct ceph_mount_options { · 103 unmodified lines | |||
| 104 | struct fscrypt_dummy_policy dummy_enc_policy; | 104 | struct fscrypt_dummy_policy dummy_enc_policy; |
| 105 | }; | 105 | }; |
| 106 | 106 | ||
| 107 | #define CEPH_NAMESPACE_WILDCARD "*" | ||
| 108 | |||
| 109 | static inline bool ceph_namespace_match(const char *pattern, | ||
| 110 | const char *target) | ||
| 111 | { | ||
| 112 | if (!pattern || !pattern[0] || | ||
| 113 | !strcmp(pattern, CEPH_NAMESPACE_WILDCARD)) | ||
| 114 | return true; | ||
| 115 | |||
| 116 | return !strcmp(pattern, target); | ||
| 117 | } | ||
| 118 | |||
| 107 | /* | 119 | /* |
| 108 | * Check if the mds namespace in ceph_mount_options matches | 120 | * Check if the mds namespace in ceph_mount_options matches |
| 109 | * the passed in namespace string. First time match (when | 121 | * the passed in namespace string. First time match (when |
| 110 | * ->mds_namespace is NULL) is treated specially, since | 122 | * ->mds_namespace is NULL) is treated specially, since |
| 111 | * ->mds_namespace needs to be initialized by the caller. | 123 | * ->mds_namespace needs to be initialized by the caller. |
| 112 | */ | 124 | */ |
| 113 | static inline int namespace_equals(struct ceph_mount_options *fsopt, | 125 | static inline bool namespace_equals(struct ceph_mount_options *fsopt, |
| 114 | const char *namespace, size_t len) | 126 | const char *namespace, size_t len) |
| 115 | { | 127 | { |
| 116 | return !(fsopt->mds_namespace && | 128 | return !(fsopt->mds_namespace && |
| 117 | (strlen(fsopt->mds_namespace) != len || | 129 | (strlen(fsopt->mds_namespace) != len || |
| @@ -31,6 +31,12 @@ · 30 unmodified lines | |||
| 31 | #define CEPH_INO_CEPH 2 /* hidden .ceph dir */ | 31 | #define CEPH_INO_CEPH 2 /* hidden .ceph dir */ |
| 32 | #define CEPH_INO_GLOBAL_SNAPREALM 3 /* global dummy snaprealm */ | 32 | #define CEPH_INO_GLOBAL_SNAPREALM 3 /* global dummy snaprealm */ |
| 33 | 33 | ||
| 34 | /* | ||
| 35 | * name for "old" CephFS file systems, | ||
| 36 | * see ceph.git e2b151d009640114b2565c901d6f41f6cd5ec652 | ||
| 37 | */ | ||
| 38 | #define CEPH_OLD_FS_NAME "cephfs" | ||
| 39 | |||
| 34 | /* arbitrary limit on max # of monitors (cluster of 3 is typical) */ | 40 | /* arbitrary limit on max # of monitors (cluster of 3 is typical) */ |
| 35 | #define CEPH_MAX_MON 31 | 41 | #define CEPH_MAX_MON 31 |
| 36 | 42 | ||