static bool ad_pack_xattrs(struct vfs_handle_struct *handle,
			   struct adouble *ad,
			   files_struct *fsp)
{
	struct ad_xattr_header *h = &ad->adx_header;
	size_t oldsize;
	uint32_t off;
	uint32_t data_off;
	uint16_t i;
	bool ok;

	if (ad->adx_entries == NULL) {
		/* No xattrs, nothing to pack */
		return true;
	}

	if (fsp == NULL) {
		DBG_ERR("fsp unexpectedly NULL\n");
		return false;
	}

	oldsize = talloc_get_size(ad->ad_data);
	if (oldsize < AD_XATTR_MAX_HDR_SIZE) {
		ad->ad_data = talloc_realloc(ad,
					     ad->ad_data,
					     char,
					     AD_XATTR_MAX_HDR_SIZE);
		if (ad->ad_data == NULL) {
			return false;
		}
		memset(ad->ad_data + oldsize,
		       0,
		       AD_XATTR_MAX_HDR_SIZE - oldsize);
	}

	/*
	 * First, let's calculate the start of the xattr data area which will be
	 * after the xattr header + header entries.
	 */

	data_off = ad_getentryoff(ad, ADEID_FINDERI);
	data_off += ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
	/* 2 bytes padding */
	data_off += 2;

	for (i = 0; i < h->adx_num_attrs; i++) {
		struct ad_xattr_entry *e = &ad->adx_entries[i];

		/* Align on 4 byte boundary */
		data_off = (data_off + 3) & ~3;

		data_off += e->adx_namelen + ADX_ENTRY_FIXED_SIZE;
		if (data_off >= AD_XATTR_MAX_HDR_SIZE) {
			return false;
		}
	}

	off = ad_getentryoff(ad, ADEID_FINDERI);
	off +=  ADEDLEN_FINDERI + AD_XATTR_HDR_SIZE;
	/* 2 bytes padding */
	off += 2;

	for (i = 0; i < h->adx_num_attrs; i++) {
		struct ad_xattr_entry *e = &ad->adx_entries[i];

		/* Align on 4 byte boundary */
		off = (off + 3) & ~3;

		e->adx_offset = data_off;
		data_off += e->adx_length;

		DBG_DEBUG("%zu(%s){%zu}: off [%zu] adx_length [%zu] "
			  "adx_data_off [%zu]\n",
			  (size_t)i,
			  e->adx_name,
			  (size_t)e->adx_namelen,
			  (size_t)off,
			  (size_t)e->adx_length,
			  (size_t)e->adx_offset);

		if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
			return false;
		}
		RSIVAL(ad->ad_data, off, e->adx_offset);
		off += 4;

		if (off + 4 >= AD_XATTR_MAX_HDR_SIZE) {
			return false;
		}
		RSIVAL(ad->ad_data, off, e->adx_length);
		off += 4;

		if (off + 2 >= AD_XATTR_MAX_HDR_SIZE) {
			return false;
		}
		RSSVAL(ad->ad_data, off, e->adx_flags);
		off += 2;

		if (off + 1 >= AD_XATTR_MAX_HDR_SIZE) {
			return false;
		}
		SCVAL(ad->ad_data, off, e->adx_namelen);
		off += 1;

		if (off + e->adx_namelen >= AD_XATTR_MAX_HDR_SIZE) {
			return false;
		}
		memcpy(ad->ad_data + off, e->adx_name, e->adx_namelen);
		off += e->adx_namelen;
	}

	h->adx_data_start = off;
	h->adx_data_length = talloc_get_size(ad->adx_data);
	h->adx_total_size = h->adx_data_start + h->adx_data_length;

	if (talloc_get_size(ad->ad_data) < h->adx_total_size) {
		ad->ad_data = talloc_realloc(ad,
					     ad->ad_data,
					     char,
					     h->adx_total_size);
		if (ad->ad_data == NULL) {
			return false;
		}
	}

	memcpy(ad->ad_data + h->adx_data_start,
	       ad->adx_data,
	       h->adx_data_length);

	ad_setentrylen(ad,
		       ADEID_FINDERI,
		       h->adx_total_size - ad_getentryoff(ad, ADEID_FINDERI));

	ad_setentryoff(ad,
		       ADEID_RFORK,
		       ad_getentryoff(ad, ADEID_FINDERI) +
		       ad_getentrylen(ad, ADEID_FINDERI));

	memcpy(ad->ad_data + ADEDOFF_FILLER, AD_FILLER_TAG_OSX, ADEDLEN_FILLER);

	/*
	 * Rewind, then update the header fields.
	 */

	off = ad_getentryoff(ad, ADEID_FINDERI) + ADEDLEN_FINDERI;
	/* 2 bytes padding */
	off += 2;

	RSIVAL(ad->ad_data, off, AD_XATTR_HDR_MAGIC);
	off += 4;
	RSIVAL(ad->ad_data, off, 0);
	off += 4;
	RSIVAL(ad->ad_data, off, h->adx_total_size);
	off += 4;
	RSIVAL(ad->ad_data, off, h->adx_data_start);
	off += 4;
	RSIVAL(ad->ad_data, off, h->adx_data_length);
	off += 4;

	/* adx_reserved and adx_flags */
	memset(ad->ad_data + off, 0, 3 * 4 + 2);
	off += 3 * 4 + 2;

	RSSVAL(ad->ad_data, off, h->adx_num_attrs);
	off += 2;

	ok = ad_pack_move_reso(handle, ad, fsp);
	if (!ok) {
		DBG_ERR("Moving resourcefork of [%s] failed\n",
			fsp_str_dbg(fsp));
		return false;
	}

	return true;
}