bool ad_unconvert(TALLOC_CTX *mem_ctx,
		  struct vfs_handle_struct *handle,
		  const char *catia_mappings,
		  struct smb_filename *smb_fname,
		  bool *converted)
{
	static struct char_mappings **cmaps = NULL;
	TALLOC_CTX *frame = talloc_stackframe();
	struct ad_collect_state state;
	struct stream_struct *streams = NULL;
	struct smb_filename *adpath = NULL;
	struct adouble *ad = NULL;
	unsigned int num_streams = 0;
	size_t to_convert = 0;
	bool have_rsrc = false;
	files_struct *fsp = NULL;
	size_t i;
	NTSTATUS status;
	int ret;
	bool ok;

	*converted = false;

	if (cmaps == NULL) {
		const char **mappings = NULL;

		mappings = str_list_make_v3_const(
			frame, catia_mappings, NULL);
		if (mappings == NULL) {
			ok = false;
			goto out;
		}
		cmaps = string_replace_init_map(mem_ctx, mappings);
		TALLOC_FREE(mappings);
	}

	ok = ad_unconvert_get_streams(handle,
				      smb_fname,
				      frame,
				      &num_streams,
				      &streams);
	if (!ok) {
		goto out;
	}

	for (i = 0; i < num_streams; i++) {
		if (strcasecmp_m(streams[i].name, "::$DATA") == 0) {
			continue;
		}
		to_convert++;
		if (is_afpresource_stream(streams[i].name)) {
			have_rsrc = true;
		}
	}

	if (to_convert == 0) {
		ok = true;
		goto out;
	}

	state = (struct ad_collect_state) {
		.adx_data_off = 0,
	};

	ret = adouble_path(frame, smb_fname, &adpath);
	if (ret != 0) {
		ok = false;
		goto out;
	}

	ret = SMB_VFS_STAT(handle->conn, adpath);
	if (ret == 0) {
		state.have_adfile = true;
	} else {
		if (errno != ENOENT) {
			ok = false;
			goto out;
		}
		state.have_adfile = false;
	}

	if (to_convert == 1 && have_rsrc && state.have_adfile) {
		/*
		 * So we have just a single stream, the resource fork stream
		 * from an AppleDouble file. Fine, that means there's nothing to
		 * convert.
		 */
		ok = true;
		goto out;
	}

	ad = ad_init(frame, ADOUBLE_RSRC);
	if (ad == NULL) {
		ok = false;
		goto out;
	}

	for (i = 0; i < num_streams; i++) {
		ok = ad_collect_one_stream(handle,
					   cmaps,
					   smb_fname,
					   &streams[i],
					   ad,
					   &state);
		if (!ok) {
			goto out;
		}
	}

	ok = ad_unconvert_open_ad(frame, handle, smb_fname, adpath, &fsp);
	if (!ok) {
		DBG_ERR("Failed to open adfile [%s]\n",
			smb_fname_str_dbg(smb_fname));
		goto out;
	}

	ret = ad_fset(handle, ad, fsp);
	if (ret != 0) {
		ok = false;
		goto out;
	}

	*converted = true;
	ok = true;

out:
	if (fsp != NULL) {
		status = close_file(NULL, fsp, NORMAL_CLOSE);
		if (!NT_STATUS_IS_OK(status)) {
			DBG_ERR("close_file [%s] failed: %s\n",
				smb_fname_str_dbg(smb_fname),
				nt_errstr(status));
			ok = false;
		}
	}
	TALLOC_FREE(frame);
	return ok;
}
bool ad_unconvert(TALLOC_CTX *mem_ctx,
		  struct vfs_handle_struct *handle,
		  const char *catia_mappings,
		  struct smb_filename *smb_fname,
		  bool *converted)
{
	static struct char_mappings **cmaps = NULL;
	TALLOC_CTX *frame = talloc_stackframe();
	struct ad_collect_state state;
	struct stream_struct *streams = NULL;
	struct smb_filename *adpath = NULL;
	struct adouble *ad = NULL;
	unsigned int num_streams = 0;
	size_t to_convert = 0;
	bool have_rsrc = false;
	files_struct *fsp = NULL;
	size_t i;
	NTSTATUS status;
	int ret;
	bool ok;

	*converted = false;

	if (cmaps == NULL) {
		const char **mappings = NULL;

		mappings = str_list_make_v3_const(
			frame, catia_mappings, NULL);
		if (mappings == NULL) {
			ok = false;
			goto out;
		}
		cmaps = string_replace_init_map(mem_ctx, mappings);
		TALLOC_FREE(mappings);
	}

	ok = ad_unconvert_get_streams(handle,
				      smb_fname,
				      frame,
				      &num_streams,
				      &streams);
	if (!ok) {
		goto out;
	}

	for (i = 0; i < num_streams; i++) {
		if (strcasecmp_m(streams[i].name, "::$DATA") == 0) {
			continue;
		}
		to_convert++;
		if (is_afpresource_stream(streams[i].name)) {
			have_rsrc = true;
		}
	}

	if (to_convert == 0) {
		ok = true;
		goto out;
	}

	state = (struct ad_collect_state) {
		.adx_data_off = 0,
	};

	ret = adouble_path(frame, smb_fname, &adpath);
	if (ret != 0) {
		ok = false;
		goto out;
	}

	ret = SMB_VFS_STAT(handle->conn, adpath);
	if (ret == 0) {
		state.have_adfile = true;
	} else {
		if (errno != ENOENT) {
			ok = false;
			goto out;
		}
		state.have_adfile = false;
	}

	if (to_convert == 1 && have_rsrc && state.have_adfile) {
		 
		ok = true;
		goto out;
	}

	ad = ad_init(frame, ADOUBLE_RSRC);
	if (ad == NULL) {
		ok = false;
		goto out;
	}

	for (i = 0; i < num_streams; i++) {
		ok = ad_collect_one_stream(handle,
					   cmaps,
					   smb_fname,
					   &streams[i],
					   ad,
					   &state);
		if (!ok) {
			goto out;
		}
	}

	ok = ad_unconvert_open_ad(frame, handle, smb_fname, adpath, &fsp);
	if (!ok) {
		DBG_ERR("Failed to open adfile [%s]\n",
			smb_fname_str_dbg(smb_fname));
		goto out;
	}

	ret = ad_fset(handle, ad, fsp);
	if (ret != 0) {
		ok = false;
		goto out;
	}

	*converted = true;
	ok = true;

out:
	if (fsp != NULL) {
		status = close_file(NULL, fsp, NORMAL_CLOSE);
		if (!NT_STATUS_IS_OK(status)) {
			DBG_ERR("close_file [%s] failed: %s\n",
				smb_fname_str_dbg(smb_fname),
				nt_errstr(status));
			ok = false;
		}
	}
	TALLOC_FREE(frame);
	return ok;
}