From a1f6808db01f808c360157cc1b1e7ede3a5a9edf Mon Sep 17 00:00:00 2001 From: Vinit Agnihotri Date: Thu, 25 Jun 2026 14:52:51 +0530 Subject: [PATCH 1/3] vfs_ceph_rgw: add flistxattrs method BUG: https://bugzilla.samba.org/show_bug.cgi?id=16218 Signed-off-by: Vinit Agnihotri Reviewed-by: Shachar Sharon Reviewed-by: Anoop C S Reviewed-by: Guenther Deschner (cherry picked from commit 9867d4d99c9337a3e820b07f3e0f7d9b756524f6) --- source3/modules/vfs_ceph_rgw.c | 118 ++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/source3/modules/vfs_ceph_rgw.c b/source3/modules/vfs_ceph_rgw.c index 8a7ca204100..2ed239412e4 100644 --- a/source3/modules/vfs_ceph_rgw.c +++ b/source3/modules/vfs_ceph_rgw.c @@ -92,6 +92,12 @@ struct vfs_ceph_rgw_getxattr_arg { size_t size; }; +struct vfs_ceph_rgw_lsxattr_arg { + int rc; + char *list; + size_t rem_size; +}; + /* * Note, librgw's return code model is to return -errno. Thus we have to * convert to what Samba expects: set errno to non-negative value and return @@ -2098,6 +2104,116 @@ out: return status_code(rc); } +/* + * Callback for rgw_lsxattrs() to build list of attribute names. + * + * @param attrs: List of attributes from RGW + * @param arg: Pointer to vfs_ceph_rgw_lsxattr_arg structure + * @param flags: Callback flags (unused) + * @return: 0 to continue iteration, RGW_LSXATTR_FLAG_STOP to stop + * + * Output format: null-separated attribute names (e.g., "user.foo\0user.bar\0") + * + */ +static int ceph_rgw_lsxattr_cb(rgw_xattrlist *attrs, void *arg, uint32_t flags) +{ + uint32_t i; + struct vfs_ceph_rgw_lsxattr_arg *cb_arg = arg; + + for (i = 0; i < attrs->xattr_cnt; i++) { + const rgw_xattr *attr = &attrs->xattrs[i]; + uint32_t len = attr->key.len + 1; + + if (len > cb_arg->rem_size) { + DBG_DEBUG("[CEPH_RGW] Not enough space for more " + "names\n"); + cb_arg->rc = -ERANGE; + return RGW_LSXATTR_FLAG_STOP; + } + + memcpy(cb_arg->list, attr->key.val, attr->key.len); + cb_arg->list[attr->key.len] = '\0'; + DBG_DEBUG("[CEPH_RGW] xattr-name:%s\n", cb_arg->list); + + cb_arg->list += len; + cb_arg->rem_size -= len; + } + return 0; +} + +static ssize_t vfs_ceph_rgw_flistxattr(vfs_handle_struct *handle, + struct files_struct *fsp, + char *list, + size_t size) +{ + ssize_t ret = -ENOMEM; + struct vfs_ceph_rgw_config *config = NULL; + struct vfs_ceph_rgw_fh *fh = NULL; + struct vfs_ceph_rgw_lsxattr_arg cb_arg = {}; + + START_PROFILE_X(SNUM(handle->conn), syscall_flistxattr); + + SMB_VFS_HANDLE_GET_DATA(handle, + config, + struct vfs_ceph_rgw_config, + goto out); + + DBG_DEBUG("[CEPH_RGW] flistxattr: fsp_name=%s list=%p size=%zu\n", + fsp_str_dbg(fsp), + list, + size); + + /* + * We do not support posix like handling to get buffer size + * from backend in case of size is mentioned as 0. + */ + if (size == 0) { + DBG_DEBUG("[CEPH_RGW] No support to get size of the list of " + "xattrs\n"); + ret = -ENOTSUP; + goto out; + } + + if (is_special_name(fsp->fsp_name->base_name)) { + DBG_DEBUG( + "[CEPH_RGW] Cannot perform op on special name [%s]\n", + fsp_str_dbg(fsp)); + ret = -EINVAL; + goto out; + } + + ret = vfs_ceph_rgw_fetch_fh(handle, fsp, &fh); + if (ret != 0) { + DBG_ERR("[CEPH_RGW] Unable to fetch handle\n"); + goto out; + } + + /* prepare argument for callback */ + cb_arg.list = list; + cb_arg.rem_size = size; + + ret = rgw_lsxattrs(config->rgw_root_fs, + fh->rgw_fh, + NULL, /* filter_prefix unimplemented, pass NULL */ + ceph_rgw_lsxattr_cb, + &cb_arg, + RGW_LSXATTR_FLAG_NONE); + if (ret < 0 || cb_arg.rc < 0) { + DBG_ERR("[CEPH_RGW] Unable to list xattrs. rc=%zd cb_rc=%d\n", + ret, + cb_arg.rc); + if (cb_arg.rc < 0) { + ret = cb_arg.rc; + } + goto out; + } + ret = size - cb_arg.rem_size; +out: + DBG_DEBUG("[CEPH_RGW] flistxattr done: ret=%zd\n", ret); + END_PROFILE_X(syscall_flistxattr); + return lstatus_code(ret); +} + static bool vfs_ceph_rgw_mount_bucket(struct vfs_ceph_rgw_config *config) { int rc = 0; @@ -2451,7 +2567,7 @@ static struct vfs_fn_pointers ceph_rgw_fns = { .getxattrat_send_fn = vfs_not_implemented_getxattrat_send, .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv, .fgetxattr_fn = vfs_ceph_rgw_fgetxattr, - .flistxattr_fn = vfs_not_implemented_flistxattr, + .flistxattr_fn = vfs_ceph_rgw_flistxattr, .fremovexattr_fn = vfs_ceph_rgw_fremovexattr, .fsetxattr_fn = vfs_ceph_rgw_fsetxattr, -- 2.55.0 From e15384b3d499123bc0fb36f5f14d9dd478f8d5b7 Mon Sep 17 00:00:00 2001 From: Vinit Agnihotri Date: Tue, 14 Jul 2026 09:58:31 +0530 Subject: [PATCH 2/3] vfs_ceph_rgw: Add fs_capabilities method BUG: https://bugzilla.samba.org/show_bug.cgi?id=16218 Signed-off-by: Vinit Agnihotri Reviewed-by: Shachar Sharon Reviewed-by: Anoop C S Reviewed-by: Guenther Deschner (cherry picked from commit 3796c788ac879bae0a66e3d7f44b94e68f251b02) --- source3/modules/vfs_ceph_rgw.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/source3/modules/vfs_ceph_rgw.c b/source3/modules/vfs_ceph_rgw.c index 2ed239412e4..d6b1837d394 100644 --- a/source3/modules/vfs_ceph_rgw.c +++ b/source3/modules/vfs_ceph_rgw.c @@ -1893,8 +1893,7 @@ static int vfs_ceph_rgw_fstatvfs(struct vfs_handle_struct *handle, */ statbuf->FsIdentifier = (unsigned long int)-1; - /* We do not have any capabilities defined, returning 0 */ - statbuf->FsCapabilities = 0; + statbuf->FsCapabilities = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES; DBG_DEBUG("[CEPH_RGW] fstatvfs: name=%s f_bsize=%" PRIu64 " f_blocks=%" PRIu64 " f_bfree=%" PRIu64 " f_bavail=%" PRIu64 @@ -2214,6 +2213,15 @@ out: return lstatus_code(ret); } +static uint32_t vfs_ceph_rgw_fs_capabilities( + struct vfs_handle_struct *handle, + enum timestamp_set_resolution *p_ts_res) +{ + uint32_t caps = vfs_get_fs_capabilities(handle->conn, p_ts_res); + + return caps; +} + static bool vfs_ceph_rgw_mount_bucket(struct vfs_ceph_rgw_config *config) { int rc = 0; @@ -2508,7 +2516,7 @@ static struct vfs_fn_pointers ceph_rgw_fns = { .get_quota_fn = vfs_not_implemented_get_quota, .set_quota_fn = vfs_not_implemented_set_quota, .fstatvfs_fn = vfs_ceph_rgw_fstatvfs, - .fs_capabilities_fn = vfs_not_implemented_fs_capabilities, + .fs_capabilities_fn = vfs_ceph_rgw_fs_capabilities, /* Directory operations */ -- 2.55.0 From c36a5b5ef646fe4892c00f0f366f0bacebc770e4 Mon Sep 17 00:00:00 2001 From: Vinit Agnihotri Date: Tue, 14 Jul 2026 16:03:37 +0530 Subject: [PATCH 3/3] vfs_ceph_rgw: Return ENOATTR for non-existing xattr In case of existing xattr, rgw_getxattrs() API executes callback and returns 0 on success. We then determine number of bytes to return from callback. In case of non-existing xattr, rgw_getxattrs() API does not execute callback but returns 0. Upper layer expect NT_STATUS_OBJECT_NAME_NOT_FOUND in case of non-existing xattr. Since we were returning 0, upper layer treats it as missing data and flags as error. This fix allows module to handle this situation by returning -ENOATTR for non-existing xattr, thereby providing correct information for upper layer. Additional changes: - rgw_getxattrs() do not set errno, thus relevant handling is removed. - Added error handling for special names to get/set/rm xattr calls. BUG: https://bugzilla.samba.org/show_bug.cgi?id=16218 Signed-off-by: Vinit Agnihotri Reviewed-by: Shachar Sharon Reviewed-by: Anoop C S Reviewed-by: Guenther Deschner Autobuild-User(master): Anoop C S Autobuild-Date(master): Tue Aug 11 06:06:14 UTC 2026 on atb-devel-224 (cherry picked from commit e39f85d383ba15dffc0b8fd109b361cfb89afee7) --- source3/modules/vfs_ceph_rgw.c | 85 +++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 16 deletions(-) diff --git a/source3/modules/vfs_ceph_rgw.c b/source3/modules/vfs_ceph_rgw.c index d6b1837d394..fbbec98e3d4 100644 --- a/source3/modules/vfs_ceph_rgw.c +++ b/source3/modules/vfs_ceph_rgw.c @@ -90,6 +90,7 @@ struct vfs_ceph_rgw_getxattr_arg { int rc; char *val; size_t size; + bool cb_called; }; struct vfs_ceph_rgw_lsxattr_arg { @@ -902,6 +903,17 @@ static void prepare_xattr_list(rgw_xattrlist *attr_list, } } +/* + * Callback for rgw_getxattrs() to get value for given attribute names. + * + * @param attrs: List of attributes received from RGW + * @param arg: Pointer to vfs_ceph_rgw_getxattr_arg structure + * @param flags: Callback flags (unused) + * @return: 0 to mark completion for callback. + * + * Output: value for attribute and size in bytes via callback argument. + * + */ static int ceph_rgw_getxattr_cb(rgw_xattrlist *attr_list, void *arg, uint32_t flags) @@ -910,7 +922,8 @@ static int ceph_rgw_getxattr_cb(rgw_xattrlist *attr_list, *cb_arg = (struct vfs_ceph_rgw_getxattr_arg *)arg; rgw_xattr *xattr = attr_list->xattrs; - if ((cb_arg->size != 0) && (cb_arg->size < xattr->val.len)) { + cb_arg->cb_called = true; + if (cb_arg->size < xattr->val.len) { cb_arg->rc = -ERANGE; return 0; } @@ -943,6 +956,24 @@ static ssize_t vfs_ceph_rgw_fgetxattr(struct vfs_handle_struct *handle, DBG_DEBUG("[CEPH_RGW] fgetxattr: [%s] %s\n", fsp_str_dbg(fsp), name); + /* + * We do not support posix like handling to get current size of + * extended attribute from backend in case of size is mentioned as 0. + */ + if (size == 0) { + DBG_DEBUG("[CEPH_RGW] No support to get size xattr\n"); + rc = -ENOTSUP; + goto out; + } + + if (is_special_name(fsp->fsp_name->base_name)) { + DBG_DEBUG( + "[CEPH_RGW] Cannot perform op on special name [%s]\n", + fsp_str_dbg(fsp)); + rc = -EINVAL; + goto out; + } + rc = vfs_ceph_rgw_fetch_fh(handle, fsp, &fh); if (rc != 0) { DBG_ERR("[CEPH_RGW] Unable to fetch handle\n"); @@ -951,7 +982,6 @@ static ssize_t vfs_ceph_rgw_fgetxattr(struct vfs_handle_struct *handle, prepare_xattr_list(&attr_list, discard_const(name), NULL, 0); - cb_arg.rc = 0; cb_arg.val = value; cb_arg.size = size; @@ -962,23 +992,30 @@ static ssize_t vfs_ceph_rgw_fgetxattr(struct vfs_handle_struct *handle, &cb_arg, RGW_GETXATTR_FLAG_NONE); - if (rc < 0) { - int err = errno; - if (err == 0) { - rc = -ENODATA; - } else { - DBG_ERR("[CEPH_RGW] Error getting xattr. " - "rc = %d errno = %d cbErr = %d\n", - rc, - err, - cb_arg.rc); - rc = -err; - } + if (rc < 0 || cb_arg.rc < 0) { + DBG_ERR("[CEPH_RGW] Error getting xattr. " + "rc = %d cbErr = %d\n", + rc, + cb_arg.rc); + rc = (rc < 0) ? rc : cb_arg.rc; goto out; } + + /* + * API returns 0 on success and callback serves to indicate + * number of bytes to return. + */ rc = cb_arg.rc; - if (rc < 0) { - DBG_ERR("[CEPH_RGW] Error getting xattr. Err=%d\n", cb_arg.rc); + + /* + * In case of requested xattr is not found, + * API returns 0, and callback is not called. + * Therefore we treat this case as -ENOATTR. + */ + if (rc == 0 && !cb_arg.cb_called) { + DBG_DEBUG("[CEPH_RGW] xattr %s not found.\n", name); + rc = -ENOATTR; + /* fall through */ } out: DBG_DEBUG("[CEPH_RGW] fgetxattr(...) = %d\n", rc); @@ -1008,6 +1045,14 @@ static int vfs_ceph_rgw_fsetxattr(struct vfs_handle_struct *handle, DBG_DEBUG("[CEPH_RGW] fsetxattr [%s] %s\n", fsp_str_dbg(fsp), name); + if (is_special_name(fsp->fsp_name->base_name)) { + DBG_DEBUG( + "[CEPH_RGW] Cannot perform op on special name [%s]\n", + fsp_str_dbg(fsp)); + rc = -EINVAL; + goto out; + } + rc = vfs_ceph_rgw_fetch_fh(handle, fsp, &fh); if (rc != 0) { DBG_ERR("[CEPH_RGW] Unable to fetch handle\n"); @@ -2040,6 +2085,14 @@ static int vfs_ceph_rgw_fremovexattr(struct vfs_handle_struct *handle, struct vfs_ceph_rgw_config, goto out); + if (is_special_name(fsp->fsp_name->base_name)) { + DBG_DEBUG( + "[CEPH_RGW] Cannot perform op on special name [%s]\n", + fsp_str_dbg(fsp)); + rc = -EINVAL; + goto out; + } + rc = vfs_ceph_rgw_fetch_fh(handle, fsp, &fh); if (rc != 0) { DBG_ERR("[CEPH_RGW] Unable to fetch handle\n"); -- 2.55.0