#include "../libcli/smb/smb2_create_ctx.h"
#include "lib/util/tevent_ntstatus.h"
#include "lib/util/tevent_unix.h"
+#include "lib/util/util_file.h"
#include "offload_token.h"
#include "string_replace.h"
#include "hash_inode.h"
#include "lib/adouble.h"
#include "lib/util_macstreams.h"
+#include "source3/smbd/dir.h"
/*
* Enhanced OS X and Netatalk compatibility
const char *model;
bool time_machine;
off_t time_machine_max_size;
+ bool convert_adouble;
bool wipe_intentionally_left_blank_rfork;
bool delete_empty_adfiles;
+ bool validate_afpinfo;
/*
* Additional options, all enabled by default,
static int init_fruit_config(vfs_handle_struct *handle)
{
struct fruit_config_data *config;
- int enumval;
+ int enumval = -1;
const char *tm_size_str = NULL;
config = talloc_zero(handle->conn, struct fruit_config_data);
return -1;
}
- /*
- * Versions up to Samba 4.5.x had a spelling bug in the
- * fruit:resource option calling lp_parm_enum with
- * "res*s*ource" (ie two s).
- *
- * In Samba 4.6 we accept both the wrong and the correct
- * spelling, in Samba 4.7 the bad spelling will be removed.
- */
enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
- "ressource", fruit_rsrc, FRUIT_RSRC_ADFILE);
- if (enumval == -1) {
- DEBUG(1, ("value for %s: resource type unknown\n",
- FRUIT_PARAM_TYPE_NAME));
- return -1;
- }
- config->rsrc = (enum fruit_rsrc)enumval;
-
- enumval = lp_parm_enum(SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
- "resource", fruit_rsrc, enumval);
+ "resource", fruit_rsrc, FRUIT_RSRC_ADFILE);
if (enumval == -1) {
DEBUG(1, ("value for %s: resource type unknown\n",
FRUIT_PARAM_TYPE_NAME));
config->time_machine_max_size = conv_str_size(tm_size_str);
}
+ config->convert_adouble = lp_parm_bool(
+ SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+ "convert_adouble", true);
+
config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
"wipe_intentionally_left_blank_rfork", false);
SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
"delete_empty_adfiles", false);
+ config->validate_afpinfo = lp_parm_bool(
+ SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+ "validate_afpinfo", true);
+
SMB_VFS_HANDLE_SET_DATA(handle, config,
NULL, struct fruit_config_data,
return -1);
if (req_bitmap & SMB2_CRTCTX_AAPL_SERVER_CAPS) {
if ((client_caps & SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) &&
- (handle->conn->tcon->compat->fs_capabilities & FILE_NAMED_STREAMS)) {
+ (handle->conn->fs_capabilities & FILE_NAMED_STREAMS)) {
server_caps |= SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR;
config->readdir_attr_enabled = true;
}
}
if (req_bitmap & SMB2_CRTCTX_AAPL_VOLUME_CAPS) {
- int val = lp_case_sensitive(SNUM(handle->conn->tcon->compat));
+ int val = lp_case_sensitive(SNUM(handle->conn));
uint64_t caps = 0;
switch (val) {
{
struct fruit_config_data *config = NULL;
struct fio *fio = NULL;
- int open_flags = flags & ~O_CREAT;
+ struct vfs_open_how how = {
+ .flags = flags & ~O_CREAT,
+ .mode = mode,
+ };
int fd;
DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
dirfsp,
smb_fname,
fsp,
- open_flags,
- mode);
+ &how);
if (fd != -1) {
return fd;
}
S_ISDIR(fsp->base_fsp->fsp_name->st.st_ex_mode))
{
/* sorry, but directories don't have a resource fork */
- errno = EISDIR;
+ errno = ENOENT;
rc = -1;
goto exit;
}
fio->config = config;
switch (config->rsrc) {
- case FRUIT_RSRC_STREAM:
+ case FRUIT_RSRC_STREAM: {
+ struct vfs_open_how how = {
+ .flags = flags, .mode = mode,
+ };
fd = SMB_VFS_NEXT_OPENAT(handle,
dirfsp,
smb_fname,
fsp,
- flags,
- mode);
+ &how);
break;
+ }
case FRUIT_RSRC_ADFILE:
fd = fruit_open_rsrc_adouble(handle, dirfsp, smb_fname,
const struct files_struct *dirfsp,
const struct smb_filename *smb_fname,
files_struct *fsp,
- int flags,
- mode_t mode)
+ const struct vfs_open_how *how)
{
int fd;
dirfsp,
smb_fname,
fsp,
- flags,
- mode);
+ how);
+ }
+
+ if (how->resolve != 0) {
+ errno = ENOSYS;
+ return -1;
}
SMB_ASSERT(fsp_is_alternate_stream(fsp));
dirfsp,
smb_fname,
fsp,
- flags,
- mode);
+ how->flags,
+ how->mode);
} else if (is_afpresource_stream(smb_fname->stream_name)) {
fd = fruit_open_rsrc(handle,
dirfsp,
smb_fname,
fsp,
- flags,
- mode);
+ how->flags,
+ how->mode);
} else {
fd = SMB_VFS_NEXT_OPENAT(handle,
dirfsp,
smb_fname,
fsp,
- flags,
- mode);
+ how);
}
DBG_DEBUG("Path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
adp_smb_fname,
0);
TALLOC_FREE(adp_smb_fname);
- if ((rc != 0) && (errno == ENOENT) && force_unlink) {
+ if ((rc != 0) && (errno == ENOENT || errno == ENAMETOOLONG) && force_unlink) {
rc = 0;
}
}
if (fio == NULL) {
- DBG_ERR("Failed to fetch fsp extension");
+ DBG_ERR("Failed to fetch fsp extension\n");
return -1;
}
{
struct fruit_pread_state *state = tevent_req_data(
req, struct fruit_pread_state);
+ ssize_t retval = -1;
if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+ tevent_req_received(req);
return -1;
}
*vfs_aio_state = state->vfs_aio_state;
- return state->nread;
+ retval = state->nread;
+ tevent_req_received(req);
+ return retval;
}
static ssize_t fruit_pwrite_meta_stream(vfs_handle_struct *handle,
- files_struct *fsp, const void *data,
+ files_struct *fsp, const void *indata,
size_t n, off_t offset)
{
struct fio *fio = fruit_get_complete_fio(handle, fsp);
+ const void *data = indata;
+ char afpinfo_buf[AFP_INFO_SIZE];
AfpInfo *ai = NULL;
size_t nwritten;
int ret;
}
if (fio->fake_fd) {
+ struct vfs_open_how how = {
+ .flags = fio->flags, .mode = fio->mode,
+ };
int fd = fsp_get_pathref_fd(fsp);
ret = vfs_fake_fd_close(fd);
}
fd = SMB_VFS_NEXT_OPENAT(handle,
- fsp->conn->cwd_fsp,
+ NULL, /* opening a stream */
fsp->fsp_name,
fsp,
- fio->flags,
- fio->mode);
+ &how);
if (fd == -1) {
DBG_ERR("On-demand create [%s] in write failed: %s\n",
fsp_str_dbg(fsp), strerror(errno));
fio->fake_fd = false;
}
- ai = afpinfo_unpack(talloc_tos(), data);
+ ai = afpinfo_unpack(talloc_tos(), data, fio->config->validate_afpinfo);
if (ai == NULL) {
return -1;
}
return n;
}
+ if (!fio->config->validate_afpinfo) {
+ /*
+ * Ensure the buffer contains a valid header, so marshall
+ * the data from the afpinfo struck back into a buffer
+ * and write that instead of the possibly malformed data
+ * we got from the client.
+ */
+ nwritten = afpinfo_pack(ai, afpinfo_buf);
+ if (nwritten != AFP_INFO_SIZE) {
+ errno = EINVAL;
+ return -1;
+ }
+ data = afpinfo_buf;
+ }
+
nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
if (nwritten != n) {
return -1;
files_struct *fsp, const void *data,
size_t n, off_t offset)
{
+ struct fruit_config_data *config = NULL;
struct adouble *ad = NULL;
AfpInfo *ai = NULL;
char *p = NULL;
int ret;
bool ok;
- ai = afpinfo_unpack(talloc_tos(), data);
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct fruit_config_data, return -1);
+
+ ai = afpinfo_unpack(talloc_tos(), data, config->validate_afpinfo);
if (ai == NULL) {
return -1;
}
int cmp;
if (fio == NULL) {
- DBG_ERR("Failed to fetch fsp extension");
+ DBG_ERR("Failed to fetch fsp extension\n");
return -1;
}
return -1;
}
- cmp = memcmp(data, "AFP", 3);
- if (cmp != 0) {
- errno = EINVAL;
- return -1;
+ if (fio->config->validate_afpinfo) {
+ cmp = memcmp(data, "AFP", 3);
+ if (cmp != 0) {
+ errno = EINVAL;
+ return -1;
+ }
}
if (n <= AFP_OFF_FinderInfo) {
ssize_t nwritten;
if (fio == NULL) {
- DBG_ERR("Failed to fetch fsp extension");
+ DBG_ERR("Failed to fetch fsp extension\n");
return -1;
}
{
struct fruit_pwrite_state *state = tevent_req_data(
req, struct fruit_pwrite_state);
+ ssize_t retval = -1;
+
+ if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+ tevent_req_received(req);
+ return -1;
+ }
+
+ *vfs_aio_state = state->vfs_aio_state;
+ retval = state->nwritten;
+ tevent_req_received(req);
+ return retval;
+}
+
+struct fruit_fsync_state {
+ int ret;
+ struct vfs_aio_state vfs_aio_state;
+};
+
+static void fruit_fsync_done(struct tevent_req *subreq);
+
+static struct tevent_req *fruit_fsync_send(
+ struct vfs_handle_struct *handle,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct files_struct *fsp)
+{
+ struct tevent_req *req = NULL;
+ struct tevent_req *subreq = NULL;
+ struct fruit_fsync_state *state = NULL;
+ struct fio *fio = fruit_get_complete_fio(handle, fsp);
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct fruit_fsync_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ if (fruit_must_handle_aio_stream(fio)) {
+ struct adouble *ad = NULL;
+
+ if (fio->type == ADOUBLE_META) {
+ /*
+ * We must never pass a fake_fd
+ * to lower level fsync calls.
+ * Everything is already done
+ * synchronously, so just return
+ * true.
+ */
+ SMB_ASSERT(fio->fake_fd);
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+ }
+
+ /*
+ * We know the following must be true,
+ * as it's the condition for fruit_must_handle_aio_stream()
+ * to return true if fio->type == ADOUBLE_RSRC.
+ */
+ SMB_ASSERT(fio->config->rsrc == FRUIT_RSRC_ADFILE);
+ if (fio->ad_fsp == NULL) {
+ tevent_req_error(req, EBADF);
+ return tevent_req_post(req, ev);
+ }
+ ad = ad_fget(talloc_tos(), handle, fio->ad_fsp, ADOUBLE_RSRC);
+ if (ad == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return tevent_req_post(req, ev);
+ }
+ fsp = fio->ad_fsp;
+ }
+
+ subreq = SMB_VFS_NEXT_FSYNC_SEND(state, ev, handle, fsp);
+ if (tevent_req_nomem(req, subreq)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, fruit_fsync_done, req);
+ return req;
+}
+
+static void fruit_fsync_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct fruit_fsync_state *state = tevent_req_data(
+ req, struct fruit_fsync_state);
+
+ state->ret = SMB_VFS_FSYNC_RECV(subreq, &state->vfs_aio_state);
+ TALLOC_FREE(subreq);
+ if (state->ret != 0) {
+ tevent_req_error(req, errno);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+static int fruit_fsync_recv(struct tevent_req *req,
+ struct vfs_aio_state *vfs_aio_state)
+{
+ struct fruit_fsync_state *state = tevent_req_data(
+ req, struct fruit_fsync_state);
+ int retval = -1;
if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) {
+ tevent_req_received(req);
return -1;
}
*vfs_aio_state = state->vfs_aio_state;
- return state->nwritten;
+ retval = state->ret;
+ tevent_req_received(req);
+ return retval;
}
/**
struct fruit_config_data *config = NULL;
NTSTATUS status;
+ if (S_ISDIR(smb_fname->st.st_ex_mode)) {
+ return NT_STATUS_OK;
+ }
+
SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
return NT_STATUS_INTERNAL_ERROR);
return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
}
- DBG_DEBUG("set btime for %s to %s\n", fsp_str_dbg(fsp),
+ DBG_DEBUG("set btime for %s to %s", fsp_str_dbg(fsp),
time_to_asc(convert_timespec_to_time_t(ft->create_time)));
ad = ad_fget(talloc_tos(), handle, fsp, ADOUBLE_META);
int ret;
if (fio == NULL) {
- DBG_ERR("Failed to fetch fsp extension");
+ DBG_ERR("Failed to fetch fsp extension\n");
return -1;
}
off_t offset)
{
if (offset > 60) {
- DBG_WARNING("ftruncate %s to %jd",
+ DBG_WARNING("ftruncate %s to %jd\n",
fsp_str_dbg(fsp), (intmax_t)offset);
/* OS X returns NT_STATUS_ALLOTTED_SPACE_EXCEEDED */
errno = EOVERFLOW;
SMB_VFS_HANDLE_GET_DATA(handle, config, struct fruit_config_data,
return NT_STATUS_UNSUCCESSFUL);
- if (is_apple_stream(smb_fname->stream_name) && !internal_open) {
+ if (is_apple_stream(smb_fname->stream_name) &&
+ !internal_open &&
+ config->convert_adouble)
+ {
uint32_t conv_flags = 0;
if (config->wipe_intentionally_left_blank_rfork) {
macos_string_replace_map,
conv_flags);
if (ret != 0) {
- DBG_ERR("ad_convert() failed\n");
- return NT_STATUS_UNSUCCESSFUL;
+ DBG_ERR("ad_convert(\"%s\") failed\n",
+ smb_fname_str_dbg(smb_fname));
}
}
DBG_DEBUG("Path [%s]\n", fsp_str_dbg(fsp));
- if (config->wipe_intentionally_left_blank_rfork) {
- conv_flags |= AD_CONV_WIPE_BLANK;
- }
- if (config->delete_empty_adfiles) {
- conv_flags |= AD_CONV_DELETE;
- }
+ if (config->convert_adouble) {
+ if (config->wipe_intentionally_left_blank_rfork) {
+ conv_flags |= AD_CONV_WIPE_BLANK;
+ }
+ if (config->delete_empty_adfiles) {
+ conv_flags |= AD_CONV_DELETE;
+ }
- ret = ad_convert(handle,
- fsp->fsp_name,
- macos_string_replace_map,
- conv_flags);
- if (ret != 0) {
- DBG_ERR("ad_convert() failed\n");
- return NT_STATUS_UNSUCCESSFUL;
+ ret = ad_convert(handle,
+ fsp->fsp_name,
+ macos_string_replace_map,
+ conv_flags);
+ if (ret != 0) {
+ DBG_ERR("ad_convert(\"%s\") failed\n",
+ fsp_str_dbg(fsp));
+ }
}
*pattr_data = talloc_zero(mem_ctx, struct readdir_attr_data);
state->handle->conn,
src_fname_tmp,
dst_fname_tmp,
- OPENX_FILE_CREATE_IF_NOT_EXIST);
+ FILE_CREATE);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1, ("%s: copy %s to %s failed: %s\n", __func__,
smb_fname_str_dbg(src_fname_tmp),
struct smb_Dir *dir_hnd = NULL;
const char *dname = NULL;
char *talloced = NULL;
- long offset = 0;
size_t nbands;
NTSTATUS status;
nbands = 0;
- while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
- != NULL)
- {
+ while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
if (ISDOT(dname) || ISDOTDOT(dname)) {
continue;
}
struct smb_Dir *dir_hnd = NULL;
const char *dname = NULL;
char *talloced = NULL;
- long offset = 0;
uint64_t dfree;
uint64_t dsize;
bool ok;
return UINT64_MAX;
}
- while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
- != NULL)
- {
+ while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
ok = fruit_tmsize_do_dirent(handle, &state, dname);
if (!ok) {
TALLOC_FREE(talloced);
.pread_recv_fn = fruit_pread_recv,
.pwrite_send_fn = fruit_pwrite_send,
.pwrite_recv_fn = fruit_pwrite_recv,
+ .fsync_send_fn = fruit_fsync_send,
+ .fsync_recv_fn = fruit_fsync_recv,
.stat_fn = fruit_stat,
.lstat_fn = fruit_lstat,
.fstat_fn = fruit_fstat,