+
+/* Helper Functions for exchangedata(2) */
+
+/*
+ * hfs_exchangedata_getxattr
+ * arguments:
+ * vp: vnode to extract the EA for
+ * name_selector: the index into the array of EA name entries.
+ * buffer: address for output buffer to store the output EA
+ * NOTE: This function will allocate the buffer, it is the caller's responsibility to free it.
+ * xattr_size: output argument; will return the size of the EA, to correspond with the buffer.
+ *
+ * Return: 0 on success.
+ * errno on error. If we return any error, the buffer is guaranteed to be NULL.
+ *
+ * Assumes CNODE lock held on cnode for 'vp'
+ */
+static
+int hfs_exchangedata_getxattr (struct vnode *vp, uint32_t name_selector, void **buffer, size_t *xattr_size) {
+ void *xattr_rawdata = NULL;
+ void *extracted_xattr = NULL;
+ uio_t uio;
+ size_t memsize = MAX_EXCHANGE_EA_SIZE;
+ size_t attrsize;
+ int error = 0;
+ struct hfsmount *hfsmp = NULL;
+
+ /* Sanity check inputs */
+ if (name_selector > MAX_NUM_XATTR_NAMES) {
+ return EINVAL;
+ }
+
+ if (buffer == NULL || xattr_size == NULL) {
+ return EINVAL;
+ }
+
+ hfsmp = VTOHFS(vp);
+
+ //allocate 4k memory to hold the EA. We don't use this for "large" EAs, and the default
+ //EA B-tree size should produce inline attributes of size < 4K
+ xattr_rawdata = hfs_malloc (MAX_EXCHANGE_EA_SIZE);
+ if (!xattr_rawdata) {
+ return ENOMEM;
+ }
+
+ //now create the UIO
+ uio = uio_create (1, 0, UIO_SYSSPACE, UIO_READ);
+ if (!uio) {
+ hfs_free (xattr_rawdata, memsize);
+ return ENOMEM;
+ }
+ uio_addiov(uio, CAST_USER_ADDR_T(xattr_rawdata), memsize);
+ attrsize = memsize;
+
+ struct vnop_getxattr_args vga = {
+ .a_uio = uio,
+ .a_name = XATTR_NAMES[name_selector],
+ .a_size = &attrsize
+ };
+
+ // this takes care of grabbing the systemfile locks for us.
+ error = hfs_getxattr_internal (VTOC(vp), &vga, hfsmp, 0);
+
+ if (error) {
+ /*
+ * We could have gotten a variety of errors back from the XATTR tree:
+ * is it too big? (bigger than 4k?) == ERANGE
+ * was the EA not found? == ENOATTR
+ */
+ uio_free(uio);
+ hfs_free (xattr_rawdata, memsize);
+ return error;
+ }
+
+ //free the UIO
+ uio_free(uio);
+
+ //upon success, a_size/attrsize now contains the actua/exported EA size
+ extracted_xattr = hfs_malloc (attrsize);
+ memcpy (extracted_xattr, xattr_rawdata, attrsize);
+ hfs_free (xattr_rawdata, memsize);
+
+ *xattr_size = attrsize;
+ *buffer = extracted_xattr;
+
+ return error;
+}
+
+
+/*
+ * hfs_exchangedata_setxattr
+ *
+ * Note: This function takes fileIDs in as inputs, because exchangedata does
+ * swizzly things with the two cnodes (See big block comment in hfs_vnop_exchange)
+ * so we operate with FileIDs more or less directly on the XATTR b-tree.
+ *
+ * arguments:
+ * hfsmp: the mount we're working on
+ * fileid: the fileID of the EA to store into the tree.
+ * name_selector: selector into the EA name array.
+ * buffer: pointer to the memory of the EA to write.
+ * xattr_size: size of the EA to write.
+ *
+ * Returns 0 on success
+ * errno on failure
+ *
+ * Assumes that a transaction has already begun when this is called
+ */
+
+static
+int hfs_exchangedata_setxattr (struct hfsmount *hfsmp, uint32_t fileid,
+ uint32_t name_selector, void *buffer, size_t xattr_size) {
+
+ int error = 0;
+
+
+ /* Sanity check arguments */
+ if (name_selector > MAX_NUM_XATTR_NAMES) {
+ return EINVAL;
+ }
+
+ if (buffer == NULL || xattr_size == 0 || fileid < kHFSFirstUserCatalogNodeID ) {
+ return EINVAL;
+ }
+
+ // is the size too big?
+ if (xattr_size > hfsmp->hfs_max_inline_attrsize) {
+ return EINVAL;
+ }
+
+ /* setup the arguments to setxattr*/
+ struct vnop_setxattr_args vsa = {
+ .a_desc = NULL,
+ .a_vp = NULL,
+ .a_name = XATTR_NAMES[name_selector],
+ .a_uio = NULL, // we use the data_ptr argument to setxattr_internal instead
+ .a_options = 0,
+ .a_context = NULL // no context needed, only done from within exchangedata
+ };
+
+ /*
+ * Since we must be in a transaction to guard the exchangedata operation, this will start
+ * a nested transaction within the exchangedata one.
+ */
+ error = hfs_setxattr_internal (NULL, (caddr_t) buffer, xattr_size, &vsa, hfsmp, fileid);
+
+ return error;
+
+}
+