+/*
+ * Lock a pair of cnodes.
+ */
+__private_extern__
+int
+hfs_lockpair(struct cnode *cp1, struct cnode *cp2, enum hfslocktype locktype)
+{
+ struct cnode *first, *last;
+ int error;
+
+ /*
+ * If cnodes match then just lock one.
+ */
+ if (cp1 == cp2) {
+ return hfs_lock(cp1, locktype);
+ }
+
+ /*
+ * Lock in cnode parent-child order (if there is a relationship);
+ * otherwise lock in cnode address order.
+ */
+ if ((IFTOVT(cp1->c_mode) == VDIR) && (cp1->c_fileid == cp2->c_parentcnid)) {
+ first = cp1;
+ last = cp2;
+ } else if (cp1 < cp2) {
+ first = cp1;
+ last = cp2;
+ } else {
+ first = cp2;
+ last = cp1;
+ }
+
+ if ( (error = hfs_lock(first, locktype))) {
+ return (error);
+ }
+ if ( (error = hfs_lock(last, locktype))) {
+ hfs_unlock(first);
+ return (error);
+ }
+ return (0);
+}
+
+/*
+ * Check ordering of two cnodes. Return true if they are are in-order.
+ */
+static int
+hfs_isordered(struct cnode *cp1, struct cnode *cp2)
+{
+ if (cp1 == cp2)
+ return (0);
+ if (cp1 == NULL || cp2 == (struct cnode *)0xffffffff)
+ return (1);
+ if (cp2 == NULL || cp1 == (struct cnode *)0xffffffff)
+ return (0);
+ if (cp1->c_fileid == cp2->c_parentcnid)
+ return (1); /* cp1 is the parent and should go first */
+ if (cp2->c_fileid == cp1->c_parentcnid)
+ return (0); /* cp1 is the child and should go last */
+
+ return (cp1 < cp2); /* fall-back is to use address order */
+}
+
+/*
+ * Acquire 4 cnode locks.
+ * - locked in cnode parent-child order (if there is a relationship)
+ * otherwise lock in cnode address order (lesser address first).
+ * - all or none of the locks are taken
+ * - only one lock taken per cnode (dup cnodes are skipped)
+ * - some of the cnode pointers may be null
+ */
+__private_extern__
+int
+hfs_lockfour(struct cnode *cp1, struct cnode *cp2, struct cnode *cp3,
+ struct cnode *cp4, enum hfslocktype locktype)
+{
+ struct cnode * a[3];
+ struct cnode * b[3];
+ struct cnode * list[4];
+ struct cnode * tmp;
+ int i, j, k;
+ int error;
+
+ if (hfs_isordered(cp1, cp2)) {
+ a[0] = cp1; a[1] = cp2;
+ } else {
+ a[0] = cp2; a[1] = cp1;
+ }
+ if (hfs_isordered(cp3, cp4)) {
+ b[0] = cp3; b[1] = cp4;
+ } else {
+ b[0] = cp4; b[1] = cp3;
+ }
+ a[2] = (struct cnode *)0xffffffff; /* sentinel value */
+ b[2] = (struct cnode *)0xffffffff; /* sentinel value */
+
+ /*
+ * Build the lock list, skipping over duplicates
+ */
+ for (i = 0, j = 0, k = 0; (i < 2 || j < 2); ) {
+ tmp = hfs_isordered(a[i], b[j]) ? a[i++] : b[j++];
+ if (k == 0 || tmp != list[k-1])
+ list[k++] = tmp;
+ }
+
+ /*
+ * Now we can lock using list[0 - k].
+ * Skip over NULL entries.
+ */
+ for (i = 0; i < k; ++i) {
+ if (list[i])
+ if ((error = hfs_lock(list[i], locktype))) {
+ /* Drop any locks we acquired. */
+ while (--i >= 0) {
+ if (list[i])
+ hfs_unlock(list[i]);
+ }
+ return (error);
+ }
+ }