diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index c55cafa7617..0c285d980f5 100644 --- a/source3/modules/vfs_streams_xattr.c +++ b/source3/modules/vfs_streams_xattr.c @@ -35,7 +35,10 @@ struct streams_xattr_config { const char *prefix; size_t prefix_len; bool store_stream_type; + bool seed_builtin_attrs; int xattr_compat_bytes; + char *afpinfo_name; + char *afpresource_name; }; struct stream_io { @@ -46,6 +49,20 @@ struct stream_io { vfs_handle_struct *handle; }; +const char EMPTY_XATTR[64] = {0}; + +static bool is_builtin_attr_name(const char *name, + struct streams_xattr_config *config) +{ + if ((strcmp(name, SAMBA_XATTR_DOS_ATTRIB) == 0) || + (strcmp(name, config->afpinfo_name) == 0) || + (strcmp(name, config->afpresource_name) == 0)) { + return true; + } + + return false; +} + static ssize_t get_xattr_size_fsp(vfs_handle_struct *handle, struct files_struct *fsp, const char *xattr_name) @@ -957,6 +974,28 @@ static int streams_xattr_connect(vfs_handle_struct *handle, config->xattr_compat_bytes = xattr_compat ? 0 : 1; + config->afpinfo_name = talloc_asprintf(config, "%s%s%s", + prefix, "AFP_AfpInfo", config->store_stream_type ? ":$DATA" : "" + ); + if (config->afpinfo_name == NULL) { + DBG_ERR("talloc_asprintf() failed\n"); + return -1; + } + + config->afpresource_name = talloc_asprintf(config, "%s%s%s", + prefix, "AFP_Resource", config->store_stream_type ? ":$DATA" : "" + ); + + if (config->afpresource_name == NULL) { + DBG_ERR("talloc_asprintf() failed\n"); + return -1; + } + + config->seed_builtin_attrs = lp_parm_bool(SNUM(handle->conn), + "streams_xattr", + "seed_builtin_attrs", + false); + SMB_VFS_HANDLE_SET_DATA(handle, config, NULL, struct stream_xattr_config, return -1); @@ -1387,6 +1426,73 @@ static int streams_xattr_fchmod(vfs_handle_struct *handle, return 0; } +static int seed_builtin_attr(struct vfs_handle_struct *handle, + struct files_struct *fsp, + const char *name, + bool as_root) +{ + int rv; + + if (!CAN_WRITE(handle->conn)) { + errno = EPERM; + return -1; + } + + rv = SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, EMPTY_XATTR, + sizeof(EMPTY_XATTR), XATTR_CREATE); + + if ((rv == -1) && as_root && ((errno == EPERM) || (errno == EACCES))) { + become_root(); + rv = SMB_VFS_NEXT_FSETXATTR(handle, fsp, name, EMPTY_XATTR, + sizeof(EMPTY_XATTR), XATTR_CREATE); + unbecome_root(); + } + + if (rv == -1) { + DBG_INFO("%s: failed to seed builtin xattr on [%s]: %s\n", + name, fsp_str_dbg(fsp), strerror(errno)); + } + + return rv; +} + +static ssize_t parse_retrieved_xattr(struct vfs_handle_struct *handle, + struct files_struct *fsp, + const char *name, + char *value, + ssize_t xattr_get_rv, + struct streams_xattr_config *config) +{ + ssize_t rv = xattr_get_rv; + + if (!is_builtin_attr_name(name, config)) { + return rv; + } + + if (xattr_get_rv == -1) { + if ((errno == ENOATTR) && (config->seed_builtin_attrs)) { + seed_builtin_attr(handle, fsp, name, true); + } + } else if (xattr_get_rv == sizeof(EMPTY_XATTR)) { + size_t off; + bool has_data = false; + + for (off=0; off < rv; off++) { + if (value[off] != '\0') { + has_data = true; + break; + } + } + + if (!has_data) { + errno = ENOATTR; + rv = -1; + } + } + + return rv; +} + static ssize_t streams_xattr_fgetxattr(struct vfs_handle_struct *handle, struct files_struct *fsp, const char *name, @@ -1395,13 +1501,19 @@ static ssize_t streams_xattr_fgetxattr(struct vfs_handle_struct *handle, { struct stream_io *sio = (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp); + struct streams_xattr_config *config = NULL; + SMB_VFS_HANDLE_GET_DATA(handle, config, struct streams_xattr_config, + return -1); + + ssize_t rv = -1; if (sio == NULL) { - return SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, value, size); + rv = SMB_VFS_NEXT_FGETXATTR(handle, fsp, name, value, size); + return parse_retrieved_xattr(handle, fsp, name, value, rv, config); } errno = ENOTSUP; - return -1; + return rv; } static ssize_t streams_xattr_flistxattr(struct vfs_handle_struct *handle, @@ -1426,8 +1538,16 @@ static int streams_xattr_fremovexattr(struct vfs_handle_struct *handle, { struct stream_io *sio = (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp); + struct streams_xattr_config *config = NULL; + + SMB_VFS_HANDLE_GET_DATA(handle, config, struct streams_xattr_config, + return -1); if (sio == NULL) { + if (config->seed_builtin_attrs && + is_builtin_attr_name(sio->xattr_name, config)) { + return seed_builtin_attr(handle, fsp, sio->xattr_name, false); + } return SMB_VFS_NEXT_FREMOVEXATTR(handle, fsp, name); }