const char *model;
bool time_machine;
off_t time_machine_max_size;
+ bool wipe_intentionally_left_blank_rfork;
+ bool delete_empty_adfiles;
/*
* Additional options, all enabled by default,
AD_DEV, AD_INO, AD_SYN, AD_ID
};
+static char empty_resourcefork[] = {
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x72, 0x65, 0x73,
+ 0x6F, 0x75, 0x72, 0x63, 0x65, 0x20, 0x66, 0x6F,
+ 0x72, 0x6B, 0x20, 0x69, 0x6E, 0x74, 0x65, 0x6E,
+ 0x74, 0x69, 0x6F, 0x6E, 0x61, 0x6C, 0x6C, 0x79,
+ 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x62, 0x6C,
+ 0x61, 0x6E, 0x6B, 0x20, 0x20, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x1C, 0x00, 0x1E, 0xFF, 0xFF
+};
+
struct fio {
/* tcon config handle */
struct fruit_config_data *config;
/* Denote stream type, meta or rsrc */
adouble_type_t type;
+
+ /* Whether the create created the stream */
+ bool created;
+
+ /*
+ * AFP_AfpInfo stream created, but not written yet, thus still a fake
+ * pipe fd. This is set to true in fruit_open_meta if there was no
+ * exisiting stream but the caller requested O_CREAT. It is later set to
+ * false when we get a write on the stream that then does open and
+ * create the stream.
+ */
+ bool fake_fd;
+ int flags;
+ int mode;
};
/*
return true;
}
+static bool ad_convert_move_reso(struct adouble *ad,
+ const struct smb_filename *smb_fname)
+{
+ char *map = MAP_FAILED;
+ size_t maplen;
+ ssize_t len;
+ int rc;
+ bool ok;
+
+ if (ad_getentrylen(ad, ADEID_RFORK) == 0) {
+ return true;
+ }
+
+ maplen = ad_getentryoff(ad, ADEID_RFORK) +
+ ad_getentrylen(ad, ADEID_RFORK);
+
+ /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
+ map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
+ ad->ad_fd, 0);
+ if (map == MAP_FAILED) {
+ DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
+ return false;
+ }
+
+
+ memmove(map + ADEDOFF_RFORK_DOT_UND,
+ map + ad_getentryoff(ad, ADEID_RFORK),
+ ad_getentrylen(ad, ADEID_RFORK));
+
+ rc = munmap(map, maplen);
+ if (rc != 0) {
+ DBG_ERR("munmap failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ ad_setentryoff(ad, ADEID_RFORK, ADEDOFF_RFORK_DOT_UND);
+
+ ok = ad_pack(ad);
+ if (!ok) {
+ DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
+ return false;
+ }
+
+ len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
+ if (len != AD_DATASZ_DOT_UND) {
+ DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
+ return false;
+ }
+
+ return true;
+}
+
static bool ad_convert_xattr(struct adouble *ad,
const struct smb_filename *smb_fname,
- char *map)
+ bool *converted_xattr)
{
static struct char_mappings **string_replace_cmaps = NULL;
+ char *map = MAP_FAILED;
+ size_t maplen;
uint16_t i;
+ ssize_t len;
int saved_errno = 0;
NTSTATUS status;
+ int rc;
+ bool ok;
+
+ *converted_xattr = false;
- if (ad->adx_header.adx_num_attrs == 0) {
+ if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
return true;
}
TALLOC_FREE(mappings);
}
+ maplen = ad_getentryoff(ad, ADEID_RFORK) +
+ ad_getentrylen(ad, ADEID_RFORK);
+
+ /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
+ map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
+ ad->ad_fd, 0);
+ if (map == MAP_FAILED) {
+ DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
+ return false;
+ }
+
for (i = 0; i < ad->adx_header.adx_num_attrs; i++) {
struct ad_xattr_entry *e = &ad->adx_entries[i];
char *mapped_name = NULL;
!NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
{
DBG_ERR("string_replace_allocate failed\n");
- return -1;
+ ok = false;
+ goto fail;
}
tmp = mapped_name;
mapped_name = talloc_asprintf(talloc_tos(), ":%s", tmp);
TALLOC_FREE(tmp);
if (mapped_name == NULL) {
- return -1;
+ ok = false;
+ goto fail;
}
stream_name = synthetic_smb_fname(talloc_tos(),
TALLOC_FREE(mapped_name);
if (stream_name == NULL) {
DBG_ERR("synthetic_smb_fname failed\n");
- return -1;
+ ok = false;
+ goto fail;
}
DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
TALLOC_FREE(stream_name);
if (!NT_STATUS_IS_OK(status)) {
DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
- return -1;
+ ok = false;
+ goto fail;
}
nwritten = SMB_VFS_PWRITE(fsp,
saved_errno = errno;
close_file(NULL, fsp, ERROR_CLOSE);
errno = saved_errno;
- return -1;
+ ok = false;
+ goto fail;
}
status = close_file(NULL, fsp, NORMAL_CLOSE);
if (!NT_STATUS_IS_OK(status)) {
- return -1;
+ ok = false;
+ goto fail;
}
fsp = NULL;
}
+ ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
+
+ ok = ad_pack(ad);
+ if (!ok) {
+ DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
+ goto fail;
+ }
+
+ len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
+ if (len != AD_DATASZ_DOT_UND) {
+ DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
+ ok = false;
+ goto fail;
+ }
+
+ ok = ad_convert_move_reso(ad, smb_fname);
+ if (!ok) {
+ goto fail;
+ }
+
+ *converted_xattr = true;
+ ok = true;
+
+fail:
+ rc = munmap(map, maplen);
+ if (rc != 0) {
+ DBG_ERR("munmap failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ return ok;
+}
+
+static bool ad_convert_finderinfo(struct adouble *ad,
+ const struct smb_filename *smb_fname)
+{
+ char *p_ad = NULL;
+ AfpInfo *ai = NULL;
+ DATA_BLOB aiblob;
+ struct smb_filename *stream_name = NULL;
+ files_struct *fsp = NULL;
+ size_t size;
+ ssize_t nwritten;
+ NTSTATUS status;
+ int saved_errno = 0;
+ int cmp;
+
+ cmp = memcmp(ad->ad_filler, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);
+ if (cmp != 0) {
+ return true;
+ }
+
+ p_ad = ad_get_entry(ad, ADEID_FINDERI);
+ if (p_ad == NULL) {
+ return false;
+ }
+
+ ai = afpinfo_new(talloc_tos());
+ if (ai == NULL) {
+ return false;
+ }
+
+ memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
+
+ aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
+ if (aiblob.data == NULL) {
+ TALLOC_FREE(ai);
+ return false;
+ }
+
+ size = afpinfo_pack(ai, (char *)aiblob.data);
+ TALLOC_FREE(ai);
+ if (size != AFP_INFO_SIZE) {
+ return false;
+ }
+
+ stream_name = synthetic_smb_fname(talloc_tos(),
+ smb_fname->base_name,
+ AFPINFO_STREAM,
+ NULL,
+ smb_fname->flags);
+ if (stream_name == NULL) {
+ data_blob_free(&aiblob);
+ DBG_ERR("synthetic_smb_fname failed\n");
+ return false;
+ }
+
+ DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
+
+ status = SMB_VFS_CREATE_FILE(
+ ad->ad_handle->conn, /* conn */
+ NULL, /* req */
+ 0, /* root_dir_fid */
+ stream_name, /* fname */
+ FILE_GENERIC_WRITE, /* access_mask */
+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
+ FILE_OPEN_IF, /* create_disposition */
+ 0, /* create_options */
+ 0, /* file_attributes */
+ INTERNAL_OPEN_ONLY, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp, /* result */
+ NULL, /* psbuf */
+ NULL, NULL); /* create context */
+ TALLOC_FREE(stream_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
+ return false;
+ }
+
+ nwritten = SMB_VFS_PWRITE(fsp,
+ aiblob.data,
+ aiblob.length,
+ 0);
+ if (nwritten == -1) {
+ DBG_ERR("SMB_VFS_PWRITE failed\n");
+ saved_errno = errno;
+ close_file(NULL, fsp, ERROR_CLOSE);
+ errno = saved_errno;
+ return false;
+ }
+
+ status = close_file(NULL, fsp, NORMAL_CLOSE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+ fsp = NULL;
+
+ return true;
+}
+
+static bool ad_convert_truncate(struct adouble *ad,
+ const struct smb_filename *smb_fname)
+{
+ int rc;
+
+ /*
+ * FIXME: direct ftruncate(), but we don't have a fsp for the
+ * VFS call
+ */
+ rc = ftruncate(ad->ad_fd, ADEDOFF_RFORK_DOT_UND +
+ ad_getentrylen(ad, ADEID_RFORK));
+ if (rc != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool ad_convert_blank_rfork(struct adouble *ad,
+ bool *blank)
+{
+ struct fruit_config_data *config = NULL;
+ uint8_t *map = MAP_FAILED;
+ size_t maplen;
+ int cmp;
+ ssize_t len;
+ int rc;
+ bool ok;
+
+ *blank = false;
+
+ SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
+ struct fruit_config_data, return false);
+
+ if (!config->wipe_intentionally_left_blank_rfork) {
+ return true;
+ }
+
+ if (ad_getentrylen(ad, ADEID_RFORK) != sizeof(empty_resourcefork)) {
+ return true;
+ }
+
+ maplen = ad_getentryoff(ad, ADEID_RFORK) +
+ ad_getentrylen(ad, ADEID_RFORK);
+
+ /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
+ map = mmap(NULL, maplen, PROT_READ|PROT_WRITE, MAP_SHARED,
+ ad->ad_fd, 0);
+ if (map == MAP_FAILED) {
+ DBG_ERR("mmap AppleDouble: %s\n", strerror(errno));
+ return false;
+ }
+
+ cmp = memcmp(map + ADEDOFF_RFORK_DOT_UND,
+ empty_resourcefork,
+ sizeof(empty_resourcefork));
+ rc = munmap(map, maplen);
+ if (rc != 0) {
+ DBG_ERR("munmap failed: %s\n", strerror(errno));
+ return false;
+ }
+
+ if (cmp != 0) {
+ return true;
+ }
+
+ ad_setentrylen(ad, ADEID_RFORK, 0);
+
+ ok = ad_pack(ad);
+ if (!ok) {
+ return false;
+ }
+
+ len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
+ if (len != AD_DATASZ_DOT_UND) {
+ return false;
+ }
+
+ *blank = true;
+ return true;
+}
+
+static bool ad_convert_delete_adfile(struct adouble *ad,
+ const struct smb_filename *smb_fname)
+{
+ struct fruit_config_data *config = NULL;
+ struct smb_filename *ad_name = NULL;
+ int rc;
+
+ if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
+ return true;
+ }
+
+ SMB_VFS_HANDLE_GET_DATA(ad->ad_handle, config,
+ struct fruit_config_data, return false);
+
+ if (!config->delete_empty_adfiles) {
+ return true;
+ }
+
+ rc = adouble_path(talloc_tos(), smb_fname, &ad_name);
+ if (rc != 0) {
+ return false;
+ }
+
+ rc = SMB_VFS_NEXT_UNLINK(ad->ad_handle, ad_name);
+ if (rc != 0) {
+ DBG_ERR("Unlinking [%s] failed: %s\n",
+ smb_fname_str_dbg(ad_name), strerror(errno));
+ TALLOC_FREE(ad_name);
+ return false;
+ }
+
+ DBG_WARNING("Unlinked [%s] after conversion\n", smb_fname_str_dbg(ad_name));
+ TALLOC_FREE(ad_name);
+
return true;
}
* otherwise
**/
static int ad_convert(struct adouble *ad,
- const struct smb_filename *smb_fname,
- int fd)
+ const struct smb_filename *smb_fname)
{
- int rc = 0;
- char *map = MAP_FAILED;
- size_t origlen;
bool ok;
+ bool converted_xattr = false;
+ bool blank;
- origlen = ad_getentryoff(ad, ADEID_RFORK) +
- ad_getentrylen(ad, ADEID_RFORK);
-
- /* FIXME: direct use of mmap(), vfs_aio_fork does it too */
- map = mmap(NULL, origlen, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
- if (map == MAP_FAILED) {
- DEBUG(2, ("mmap AppleDouble: %s\n", strerror(errno)));
- rc = -1;
- goto exit;
+ ok = ad_convert_xattr(ad, smb_fname, &converted_xattr);
+ if (!ok) {
+ return -1;
}
- ok = ad_convert_xattr(ad, smb_fname, map);
+ ok = ad_convert_blank_rfork(ad, &blank);
if (!ok) {
- munmap(map, origlen);
return -1;
}
- if (ad_getentrylen(ad, ADEID_RFORK) > 0) {
- memmove(map + ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI,
- map + ad_getentryoff(ad, ADEID_RFORK),
- ad_getentrylen(ad, ADEID_RFORK));
+ if (converted_xattr || blank) {
+ ok = ad_convert_truncate(ad, smb_fname);
+ if (!ok) {
+ return -1;
+ }
}
- ad_setentrylen(ad, ADEID_FINDERI, ADEDLEN_FINDERI);
- ad_setentryoff(ad, ADEID_RFORK,
- ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI);
-
- /*
- * FIXME: direct ftruncate(), but we don't have a fsp for the
- * VFS call
- */
- rc = ftruncate(fd, ad_getentryoff(ad, ADEID_RFORK)
- + ad_getentrylen(ad, ADEID_RFORK));
+ ok = ad_convert_finderinfo(ad, smb_fname);
+ if (!ok) {
+ DBG_ERR("Failed to convert [%s]\n",
+ smb_fname_str_dbg(smb_fname));
+ return -1;
+ }
-exit:
- if (map != MAP_FAILED) {
- munmap(map, origlen);
+ ok = ad_convert_delete_adfile(ad, smb_fname);
+ if (!ok) {
+ return -1;
}
- return rc;
+
+ return 0;
}
/**
{
SMB_STRUCT_STAT sbuf;
char *p_ad = NULL;
- AfpInfo *ai = NULL;
- DATA_BLOB aiblob;
- struct smb_filename *stream_name = NULL;
- files_struct *fsp = NULL;
- ssize_t len;
size_t size;
- ssize_t nwritten;
- NTSTATUS status;
- int saved_errno = 0;
+ ssize_t len;
int ret;
bool ok;
return -1;
}
- if (ad_getentrylen(ad, ADEID_FINDERI) == ADEDLEN_FINDERI) {
- return len;
- }
-
/*
* Try to fixup AppleDouble files created by OS X with xattrs
* appended to the ADEID_FINDERI entry.
*/
- ret = ad_convert(ad, smb_fname, ad->ad_fd);
+ ret = ad_convert(ad, smb_fname);
if (ret != 0) {
DBG_WARNING("Failed to convert [%s]\n", smb_fname->base_name);
return len;
}
- ok = ad_pack(ad);
- if (!ok) {
- DBG_WARNING("ad_pack [%s] failed\n", smb_fname->base_name);
- return -1;
- }
-
- len = sys_pwrite(ad->ad_fd, ad->ad_data, AD_DATASZ_DOT_UND, 0);
- if (len != AD_DATASZ_DOT_UND) {
- DBG_ERR("%s: bad size: %zd\n", smb_fname->base_name, len);
- return -1;
- }
-
- p_ad = ad_get_entry(ad, ADEID_FINDERI);
- if (p_ad == NULL) {
- return -1;
- }
-
- ai = afpinfo_new(talloc_tos());
- if (ai == NULL) {
- return -1;
- }
-
- memcpy(ai->afpi_FinderInfo, p_ad, ADEDLEN_FINDERI);
-
- aiblob = data_blob_talloc(talloc_tos(), NULL, AFP_INFO_SIZE);
- if (aiblob.data == NULL) {
- TALLOC_FREE(ai);
- return -1;
- }
-
- size = afpinfo_pack(ai, (char *)aiblob.data);
- TALLOC_FREE(ai);
- if (size != AFP_INFO_SIZE) {
- return -1;
- }
-
- stream_name = synthetic_smb_fname(talloc_tos(),
- smb_fname->base_name,
- AFPINFO_STREAM,
- NULL,
- smb_fname->flags);
- if (stream_name == NULL) {
- data_blob_free(&aiblob);
- DBG_ERR("synthetic_smb_fname failed\n");
- return -1;
- }
-
- DBG_DEBUG("stream_name: %s\n", smb_fname_str_dbg(stream_name));
-
- status = SMB_VFS_CREATE_FILE(
- ad->ad_handle->conn, /* conn */
- NULL, /* req */
- 0, /* root_dir_fid */
- stream_name, /* fname */
- FILE_GENERIC_WRITE, /* access_mask */
- FILE_SHARE_READ | FILE_SHARE_WRITE, /* share_access */
- FILE_OPEN_IF, /* create_disposition */
- 0, /* create_options */
- 0, /* file_attributes */
- INTERNAL_OPEN_ONLY, /* oplock_request */
- NULL, /* lease */
- 0, /* allocation_size */
- 0, /* private_flags */
- NULL, /* sd */
- NULL, /* ea_list */
- &fsp, /* result */
- NULL, /* psbuf */
- NULL, NULL); /* create context */
- TALLOC_FREE(stream_name);
- if (!NT_STATUS_IS_OK(status)) {
- DBG_ERR("SMB_VFS_CREATE_FILE failed\n");
- return -1;
- }
-
- nwritten = SMB_VFS_PWRITE(fsp,
- aiblob.data,
- aiblob.length,
- 0);
- if (nwritten == -1) {
- DBG_ERR("SMB_VFS_PWRITE failed\n");
- saved_errno = errno;
- close_file(NULL, fsp, ERROR_CLOSE);
- errno = saved_errno;
- return -1;
- }
-
- status = close_file(NULL, fsp, NORMAL_CLOSE);
- if (!NT_STATUS_IS_OK(status)) {
- return -1;
- }
- fsp = NULL;
-
return len;
}
config->time_machine_max_size = conv_str_size(tm_size_str);
}
+ config->wipe_intentionally_left_blank_rfork = lp_parm_bool(
+ SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+ "wipe_intentionally_left_blank_rfork", false);
+
+ config->delete_empty_adfiles = lp_parm_bool(
+ SNUM(handle->conn), FRUIT_PARAM_TYPE_NAME,
+ "delete_empty_adfiles", false);
+
SMB_VFS_HANDLE_SET_DATA(handle, config,
NULL, struct fruit_config_data,
return -1);
SMB_INO_T result;
char *upper_sname;
+ DBG_DEBUG("fruit_inode called for %ju/%ju [%s]\n",
+ (uintmax_t)sbuf->st_ex_dev,
+ (uintmax_t)sbuf->st_ex_ino, sname);
+
upper_sname = talloc_strdup_upper(talloc_tos(), sname);
SMB_ASSERT(upper_sname != NULL);
/* Hopefully all the variation is in the lower 4 (or 8) bytes! */
memcpy(&result, hash, sizeof(result));
- DEBUG(10, ("fruit_inode \"%s\": ino=0x%llu\n",
- sname, (unsigned long long)result));
+ DBG_DEBUG("fruit_inode \"%s\": ino=%ju\n",
+ sname, (uintmax_t)result);
return result;
}
return rc;
}
+static int fruit_fake_fd(void)
+{
+ int pipe_fds[2];
+ int fd;
+ int ret;
+
+ /*
+ * Return a valid fd, but ensure any attempt to use it returns
+ * an error (EPIPE). Once we get a write on the handle, we open
+ * the real fd.
+ */
+ ret = pipe(pipe_fds);
+ if (ret != 0) {
+ return -1;
+ }
+ fd = pipe_fds[0];
+ close(pipe_fds[1]);
+
+ return fd;
+}
+
static int fruit_open_meta_stream(vfs_handle_struct *handle,
struct smb_filename *smb_fname,
files_struct *fsp,
int flags,
mode_t mode)
{
- AfpInfo *ai = NULL;
- char afpinfo_buf[AFP_INFO_SIZE];
- ssize_t len, written;
- int hostfd = -1;
- int rc = -1;
+ struct fruit_config_data *config = NULL;
+ struct fio *fio = NULL;
+ int open_flags = flags & ~O_CREAT;
+ int fd;
- hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
- if (hostfd == -1) {
- return -1;
- }
+ DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
- if (!(flags & (O_CREAT | O_TRUNC))) {
- return hostfd;
- }
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct fruit_config_data, return -1);
- ai = afpinfo_new(talloc_tos());
- if (ai == NULL) {
- rc = -1;
- goto fail;
- }
+ fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
+ fio->type = ADOUBLE_META;
+ fio->config = config;
- len = afpinfo_pack(ai, afpinfo_buf);
- if (len != AFP_INFO_SIZE) {
- rc = -1;
- goto fail;
+ fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, open_flags, mode);
+ if (fd != -1) {
+ return fd;
}
- /* Set fd, needed in SMB_VFS_NEXT_PWRITE() */
- fsp->fh->fd = hostfd;
-
- written = SMB_VFS_NEXT_PWRITE(handle, fsp, afpinfo_buf,
- AFP_INFO_SIZE, 0);
- fsp->fh->fd = -1;
- if (written != AFP_INFO_SIZE) {
- DBG_ERR("bad write [%zd/%d]\n", written, AFP_INFO_SIZE);
- rc = -1;
- goto fail;
+ if (!(flags & O_CREAT)) {
+ VFS_REMOVE_FSP_EXTENSION(handle, fsp);
+ return -1;
}
- rc = 0;
+ fd = fruit_fake_fd();
+ if (fd == -1) {
+ VFS_REMOVE_FSP_EXTENSION(handle, fsp);
+ return -1;
+ }
-fail:
- DBG_DEBUG("rc=%d, fd=%d\n", rc, hostfd);
+ fio->fake_fd = true;
+ fio->flags = flags;
+ fio->mode = mode;
- if (rc != 0) {
- int saved_errno = errno;
- if (hostfd >= 0) {
- fsp->fh->fd = hostfd;
- SMB_VFS_NEXT_CLOSE(handle, fsp);
- }
- hostfd = -1;
- errno = saved_errno;
- }
- return hostfd;
+ return fd;
}
static int fruit_open_meta_netatalk(vfs_handle_struct *handle,
int flags,
mode_t mode)
{
- int rc;
- int fakefd = -1;
+ struct fruit_config_data *config = NULL;
+ struct fio *fio = NULL;
struct adouble *ad = NULL;
- int fds[2];
+ bool meta_exists = false;
+ int fd;
DBG_DEBUG("Path [%s]\n", smb_fname_str_dbg(smb_fname));
- /*
- * Return a valid fd, but ensure any attempt to use it returns an error
- * (EPIPE). All operations on the smb_fname or the fsp will use path
- * based syscalls.
- */
- rc = pipe(fds);
- if (rc != 0) {
- goto exit;
+ ad = ad_get(talloc_tos(), handle, smb_fname, ADOUBLE_META);
+ if (ad != NULL) {
+ meta_exists = true;
}
- fakefd = fds[0];
- close(fds[1]);
-
- if (flags & (O_CREAT | O_TRUNC)) {
- /*
- * The attribute does not exist or needs to be truncated,
- * create an AppleDouble EA
- */
- ad = ad_init(fsp, handle, ADOUBLE_META);
- if (ad == NULL) {
- rc = -1;
- goto exit;
- }
- rc = ad_set(ad, fsp->fsp_name);
- if (rc != 0) {
- rc = -1;
- goto exit;
- }
+ TALLOC_FREE(ad);
- TALLOC_FREE(ad);
+ if (!meta_exists && !(flags & O_CREAT)) {
+ errno = ENOENT;
+ return -1;
}
-exit:
- DEBUG(10, ("fruit_open meta rc=%d, fd=%d\n", rc, fakefd));
- if (rc != 0) {
- int saved_errno = errno;
- if (fakefd >= 0) {
- close(fakefd);
- }
- fakefd = -1;
- errno = saved_errno;
+ fd = fruit_fake_fd();
+ if (fd == -1) {
+ return -1;
}
- return fakefd;
+
+ SMB_VFS_HANDLE_GET_DATA(handle, config,
+ struct fruit_config_data, return -1);
+
+ fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
+ fio->type = ADOUBLE_META;
+ fio->config = config;
+ fio->fake_fd = true;
+ fio->flags = flags;
+ fio->mode = mode;
+
+ return fd;
}
static int fruit_open_meta(vfs_handle_struct *handle,
{
int fd;
struct fruit_config_data *config = NULL;
- struct fio *fio = NULL;
DBG_DEBUG("path [%s]\n", smb_fname_str_dbg(smb_fname));
DBG_DEBUG("path [%s] fd [%d]\n", smb_fname_str_dbg(smb_fname), fd);
- if (fd == -1) {
- return -1;
- }
-
- fio = VFS_ADD_FSP_EXTENSION(handle, fsp, struct fio, NULL);
- fio->type = ADOUBLE_META;
- fio->config = config;
-
return fd;
}
goto exit;
}
- /* Sanitize flags */
- if (flags & O_WRONLY) {
- /* We always need read access for the metadata header too */
- flags &= ~O_WRONLY;
- flags |= O_RDWR;
- }
+ /* We always need read/write access for the metadata header too */
+ flags &= ~(O_RDONLY | O_WRONLY);
+ flags |= O_RDWR;
hostfd = SMB_VFS_NEXT_OPEN(handle, smb_fname_base, fsp,
flags, mode);
SMB_VFS_HANDLE_GET_DATA(handle, config,
struct fruit_config_data, return -1);
- if (((flags & O_ACCMODE) == O_RDONLY)
- && (flags & O_CREAT)
- && !VALID_STAT(fsp->fsp_name->st))
- {
- /*
- * This means the stream doesn't exist. macOS SMB server fails
- * this with NT_STATUS_OBJECT_NAME_NOT_FOUND, so must we. Cf bug
- * 12565 and the test for this combination in
- * test_rfork_create().
- */
- errno = ENOENT;
- return -1;
- }
-
switch (config->rsrc) {
case FRUIT_RSRC_STREAM:
fd = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
int ret;
nread = SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
-
- if (nread == n) {
+ if (nread == -1 || nread == n) {
return nread;
}
return -1;
}
+ if (nread == -1 && fio->created) {
+ AfpInfo *ai = NULL;
+ char afpinfo_buf[AFP_INFO_SIZE];
+
+ ai = afpinfo_new(talloc_tos());
+ if (ai == NULL) {
+ return -1;
+ }
+
+ nread = afpinfo_pack(ai, afpinfo_buf);
+ TALLOC_FREE(ai);
+ if (nread != AFP_INFO_SIZE) {
+ return -1;
+ }
+
+ memcpy(data, afpinfo_buf, to_return);
+ return to_return;
+ }
+
return nread;
}
return false;
};
- if ((fio->type == ADOUBLE_META) &&
- (fio->config->meta == FRUIT_META_NETATALK))
- {
+ if (fio->type == ADOUBLE_META) {
return true;
}
files_struct *fsp, const void *data,
size_t n, off_t offset)
{
+ struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
AfpInfo *ai = NULL;
size_t nwritten;
+ int ret;
bool ok;
- ai = afpinfo_unpack(talloc_tos(), data);
- if (ai == NULL) {
+ DBG_DEBUG("Path [%s] offset=%"PRIdMAX", size=%zd\n",
+ fsp_str_dbg(fsp), (intmax_t)offset, n);
+
+ if (fio == NULL) {
return -1;
}
- nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
- if (nwritten != n) {
- return -1;
+ if (fio->fake_fd) {
+ int fd;
+
+ ret = SMB_VFS_NEXT_CLOSE(handle, fsp);
+ if (ret != 0) {
+ DBG_ERR("Close [%s] failed: %s\n",
+ fsp_str_dbg(fsp), strerror(errno));
+ fsp->fh->fd = -1;
+ return -1;
+ }
+
+ fd = SMB_VFS_NEXT_OPEN(handle,
+ fsp->fsp_name,
+ fsp,
+ fio->flags,
+ fio->mode);
+ if (fd == -1) {
+ DBG_ERR("On-demand create [%s] in write failed: %s\n",
+ fsp_str_dbg(fsp), strerror(errno));
+ return -1;
+ }
+ fsp->fh->fd = fd;
+ fio->fake_fd = false;
}
- if (!ai_empty_finderinfo(ai)) {
- return n;
+ ai = afpinfo_unpack(talloc_tos(), data);
+ if (ai == NULL) {
+ return -1;
}
- ok = set_delete_on_close(
+ if (ai_empty_finderinfo(ai)) {
+ /*
+ * Writing an all 0 blob to the metadata stream results in the
+ * stream being removed on a macOS server. This ensures we
+ * behave the same and it verified by the "delete AFP_AfpInfo by
+ * writing all 0" test.
+ */
+ ret = SMB_VFS_NEXT_FTRUNCATE(handle, fsp, 0);
+ if (ret != 0) {
+ DBG_ERR("SMB_VFS_NEXT_FTRUNCATE on [%s] failed\n",
+ fsp_str_dbg(fsp));
+ return -1;
+ }
+
+ ok = set_delete_on_close(
fsp,
true,
handle->conn->session_info->security_token,
handle->conn->session_info->unix_token);
- if (!ok) {
- DBG_ERR("set_delete_on_close on [%s] failed\n",
- fsp_str_dbg(fsp));
+ if (!ok) {
+ DBG_ERR("set_delete_on_close on [%s] failed\n",
+ fsp_str_dbg(fsp));
+ return -1;
+ }
+ return n;
+ }
+
+ nwritten = SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
+ if (nwritten != n) {
return -1;
}
return n;
}
+ /*
+ * Writing an all 0 blob to the metadata stream results in the stream
+ * being removed on a macOS server. This ensures we behave the same and
+ * it verified by the "delete AFP_AfpInfo by writing all 0" test.
+ */
+
ok = set_delete_on_close(
fsp,
true,
{
struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
ssize_t nwritten;
+ uint8_t buf[AFP_INFO_SIZE];
+ size_t to_write;
+ size_t to_copy;
+ int cmp;
- /*
- * Writing an all 0 blob to the metadata stream
- * results in the stream being removed on a macOS
- * server. This ensures we behave the same and it
- * verified by the "delete AFP_AfpInfo by writing all
- * 0" test.
- */
- if (n != AFP_INFO_SIZE || offset != 0) {
- DBG_ERR("unexpected offset=%jd or size=%jd\n",
- (intmax_t)offset, (intmax_t)n);
+ if (fio == NULL) {
+ DBG_ERR("Failed to fetch fsp extension");
return -1;
}
- if (fio == NULL) {
- DBG_ERR("Failed to fetch fsp extension");
+ if (n < 3) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (offset != 0 && n < 60) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ cmp = memcmp(data, "AFP", 3);
+ if (cmp != 0) {
+ errno = EINVAL;
return -1;
}
+ if (n <= AFP_OFF_FinderInfo) {
+ /*
+ * Nothing to do here really, just return
+ */
+ return n;
+ }
+
+ offset = 0;
+
+ to_copy = n;
+ if (to_copy > AFP_INFO_SIZE) {
+ to_copy = AFP_INFO_SIZE;
+ }
+ memcpy(buf, data, to_copy);
+
+ to_write = n;
+ if (to_write != AFP_INFO_SIZE) {
+ to_write = AFP_INFO_SIZE;
+ }
+
switch (fio->config->meta) {
case FRUIT_META_STREAM:
- nwritten = fruit_pwrite_meta_stream(handle, fsp, data,
- n, offset);
+ nwritten = fruit_pwrite_meta_stream(handle,
+ fsp,
+ buf,
+ to_write,
+ offset);
break;
case FRUIT_META_NETATALK:
- nwritten = fruit_pwrite_meta_netatalk(handle, fsp, data,
- n, offset);
+ nwritten = fruit_pwrite_meta_netatalk(handle,
+ fsp,
+ buf,
+ to_write,
+ offset);
break;
default:
return -1;
}
- return nwritten;
+ if (nwritten != to_write) {
+ return -1;
+ }
+
+ /*
+ * Return the requested amount, verified against macOS SMB server
+ */
+ return n;
}
static ssize_t fruit_pwrite_rsrc_stream(vfs_handle_struct *handle,
rc = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
}
smb_fname->stream_name = tmp_stream_name;
+
+ DBG_DEBUG("fruit_stat_base [%s] dev [%ju] ino [%ju]\n",
+ smb_fname->base_name,
+ (uintmax_t)smb_fname->st.st_ex_dev,
+ (uintmax_t)smb_fname->st.st_ex_ino);
return rc;
}
bool follow_links)
{
int ret;
+ ino_t ino;
+
+ ret = fruit_stat_base(handle, smb_fname, false);
+ if (ret != 0) {
+ return -1;
+ }
+
+ ino = fruit_inode(&smb_fname->st, smb_fname->stream_name);
if (follow_links) {
ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
}
+ smb_fname->st.st_ex_ino = ino;
+
return ret;
}
files_struct *fsp,
SMB_STRUCT_STAT *sbuf)
{
- return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+ struct fio *fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+ struct smb_filename smb_fname;
+ ino_t ino;
+ int ret;
+
+ if (fio == NULL) {
+ return -1;
+ }
+
+ if (fio->fake_fd) {
+ ret = fruit_stat_base(handle, fsp->base_fsp->fsp_name, false);
+ if (ret != 0) {
+ return -1;
+ }
+
+ *sbuf = fsp->base_fsp->fsp_name->st;
+ sbuf->st_ex_size = AFP_INFO_SIZE;
+ sbuf->st_ex_ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
+ return 0;
+ }
+
+ smb_fname = (struct smb_filename) {
+ .base_name = fsp->fsp_name->base_name,
+ };
+
+ ret = fruit_stat_base(handle, &smb_fname, false);
+ if (ret != 0) {
+ return -1;
+ }
+ *sbuf = smb_fname.st;
+
+ ino = fruit_inode(sbuf, fsp->fsp_name->stream_name);
+
+ ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
+ if (ret != 0) {
+ return -1;
+ }
+
+ sbuf->st_ex_ino = ino;
+ return 0;
}
static int fruit_fstat_meta_netatalk(vfs_handle_struct *handle,
const struct smb_filename *smb_fname,
TALLOC_CTX *mem_ctx,
unsigned int *pnum_streams,
- struct stream_struct **pstreams)
+ struct stream_struct **pstreams,
+ off_t size)
{
struct smb_filename *sname = NULL;
int ret;
return NT_STATUS_INTERNAL_ERROR;
}
+ if (size == 0) {
+ return NT_STATUS_OK;
+ }
+
sname = synthetic_smb_fname(talloc_tos(),
smb_fname->base_name,
AFPINFO_STREAM_NAME,
{
struct stream_struct *stream = *pstreams;
unsigned int num_streams = *pnum_streams;
- struct smb_filename *sname = NULL;
- char *full_name = NULL;
- uint32_t name_hash;
- struct share_mode_lock *lck = NULL;
- struct file_id id = {0};
- bool delete_on_close_set;
int i;
- int ret;
- NTSTATUS status;
- bool ok;
for (i = 0; i < num_streams; i++) {
if (strequal_m(stream[i].name, AFPINFO_STREAM)) {
DBG_ERR("Removing invalid AFPINFO_STREAM size [%jd] from [%s]\n",
(intmax_t)stream[i].size, smb_fname_str_dbg(smb_fname));
- return delete_invalid_meta_stream(handle, smb_fname, mem_ctx,
- pnum_streams, pstreams);
+ return delete_invalid_meta_stream(handle,
+ smb_fname,
+ mem_ctx,
+ pnum_streams,
+ pstreams,
+ stream[i].size);
}
- /*
- * Now check if there's a delete-on-close pending on the stream. If so,
- * hide the stream. This behaviour was verified against a macOS 10.12
- * SMB server.
- */
-
- sname = synthetic_smb_fname(talloc_tos(),
- smb_fname->base_name,
- AFPINFO_STREAM_NAME,
- NULL, 0);
- if (sname == NULL) {
- status = NT_STATUS_NO_MEMORY;
- goto out;
- }
-
- ret = SMB_VFS_NEXT_STAT(handle, sname);
- if (ret != 0) {
- status = map_nt_error_from_unix(errno);
- goto out;
- }
-
- id = SMB_VFS_NEXT_FILE_ID_CREATE(handle, &sname->st);
-
- lck = get_existing_share_mode_lock(talloc_tos(), id);
- if (lck == NULL) {
- status = NT_STATUS_OK;
- goto out;
- }
-
- full_name = talloc_asprintf(talloc_tos(),
- "%s%s",
- sname->base_name,
- AFPINFO_STREAM);
- if (full_name == NULL) {
- status = NT_STATUS_NO_MEMORY;
- goto out;
- }
-
- status = file_name_hash(handle->conn, full_name, &name_hash);
- if (!NT_STATUS_IS_OK(status)) {
- goto out;
- }
-
- delete_on_close_set = is_delete_on_close_set(lck, name_hash);
- if (delete_on_close_set) {
- ok = del_fruit_stream(mem_ctx,
- pnum_streams,
- pstreams,
- AFPINFO_STREAM);
- if (!ok) {
- status = NT_STATUS_INTERNAL_ERROR;
- goto out;
- }
- }
-
- status = NT_STATUS_OK;
-out:
- TALLOC_FREE(sname);
- TALLOC_FREE(lck);
- TALLOC_FREE(full_name);
- return status;
+ return NT_STATUS_OK;
}
static NTSTATUS fruit_streaminfo_meta_netatalk(
return status;
}
+static void fruit_filter_empty_streams(unsigned int *pnum_streams,
+ struct stream_struct **pstreams)
+{
+ unsigned num_streams = *pnum_streams;
+ struct stream_struct *streams = *pstreams;
+ unsigned i = 0;
+
+ if (!global_fruit_config.nego_aapl) {
+ return;
+ }
+
+ while (i < num_streams) {
+ struct smb_filename smb_fname = (struct smb_filename) {
+ .stream_name = streams[i].name,
+ };
+
+ if (is_ntfs_default_stream_smb_fname(&smb_fname)
+ || streams[i].size > 0)
+ {
+ i++;
+ continue;
+ }
+
+ streams[i] = streams[num_streams - 1];
+ num_streams--;
+ }
+
+ *pnum_streams = num_streams;
+}
+
static NTSTATUS fruit_streaminfo(vfs_handle_struct *handle,
struct files_struct *fsp,
const struct smb_filename *smb_fname,
return status;
}
+ fruit_filter_empty_streams(pnum_streams, pstreams);
+
status = fruit_streaminfo_meta(handle, fsp, smb_fname,
mem_ctx, pnum_streams, pstreams);
if (!NT_STATUS_IS_OK(status)) {
struct files_struct *fsp,
off_t offset)
{
- if (offset == 0) {
- return SMB_VFS_FREMOVEXATTR(fsp, AFPRESOURCE_EA_NETATALK);
- }
-
#ifdef HAVE_ATTROPEN
return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
#endif
struct files_struct *fsp,
off_t offset)
{
- if (offset == 0) {
- return SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
- }
-
return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
}
(intmax_t)offset);
if (fio == NULL) {
- if (offset == 0 &&
- global_fruit_config.nego_aapl &&
- is_ntfs_stream_smb_fname(fsp->fsp_name) &&
- !is_ntfs_default_stream_smb_fname(fsp->fsp_name))
- {
- return SMB_VFS_NEXT_UNLINK(handle, fsp->fsp_name);
- }
return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
}
NTSTATUS status;
struct fruit_config_data *config = NULL;
files_struct *fsp = NULL;
+ struct fio *fio = NULL;
status = check_aapl(handle, req, in_context_blobs, out_context_blobs);
if (!NT_STATUS_IS_OK(status)) {
*
* Cf the vfs_fruit torture tests in test_rfork_create().
*/
- if (is_afpresource_stream(fsp->fsp_name) &&
- create_disposition == FILE_OPEN)
+ if (global_fruit_config.nego_aapl &&
+ create_disposition == FILE_OPEN &&
+ smb_fname->st.st_ex_size == 0 &&
+ is_ntfs_stream_smb_fname(smb_fname) &&
+ !(is_ntfs_default_stream_smb_fname(smb_fname)))
{
- if (fsp->fsp_name->st.st_ex_size == 0) {
- status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
- goto fail;
- }
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto fail;
+ }
+
+ fio = (struct fio *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
+ if (fio != NULL && pinfo != NULL && *pinfo == FILE_WAS_CREATED) {
+ fio->created = true;
}
if (is_ntfs_stream_smb_fname(smb_fname)