+ * Request ID allocation and temporary dispatch routines.
+ */
+
+#define REQUEST_ID_OFFSET 256
+
+struct sftp_request {
+ unsigned id;
+ int registered;
+};
+
+static int sftp_reqcmp(void *av, void *bv)
+{
+ struct sftp_request *a = (struct sftp_request *)av;
+ struct sftp_request *b = (struct sftp_request *)bv;
+ if (a->id < b->id)
+ return -1;
+ if (a->id > b->id)
+ return +1;
+ return 0;
+}
+static int sftp_reqfind(void *av, void *bv)
+{
+ unsigned *a = (unsigned *) av;
+ struct sftp_request *b = (struct sftp_request *)bv;
+ if (*a < b->id)
+ return -1;
+ if (*a > b->id)
+ return +1;
+ return 0;
+}
+
+static tree234 *sftp_requests;
+
+static struct sftp_request *sftp_alloc_request(void)
+{
+ const unsigned CHANNEL_NUMBER_OFFSET = 256;
+ unsigned low, high, mid;
+ int tsize;
+ struct sftp_request *r;
+
+ if (sftp_requests == NULL)
+ sftp_requests = newtree234(sftp_reqcmp);
+
+ /*
+ * First-fit allocation of request IDs: always pick the lowest
+ * unused one. To do this, binary-search using the counted
+ * B-tree to find the largest ID which is in a contiguous
+ * sequence from the beginning. (Precisely everything in that
+ * sequence must have ID equal to its tree index plus
+ * SEQUENCE_NUMBER_OFFSET.)
+ */
+ tsize = count234(sftp_requests);
+
+ low = -1;
+ high = tsize;
+ while (high - low > 1) {
+ mid = (high + low) / 2;
+ r = index234(sftp_requests, mid);
+ if (r->id == mid + REQUEST_ID_OFFSET)
+ low = mid; /* this one is fine */
+ else
+ high = mid; /* this one is past it */
+ }
+ /*
+ * Now low points to either -1, or the tree index of the
+ * largest ID in the initial sequence.
+ */
+ {
+ unsigned i = low + 1 + REQUEST_ID_OFFSET;
+ assert(NULL == find234(sftp_requests, &i, sftp_reqfind));
+ }
+
+ /*
+ * So the request ID we need to create is
+ * low + 1 + REQUEST_ID_OFFSET.
+ */
+ r = snew(struct sftp_request);
+ r->id = low + 1 + REQUEST_ID_OFFSET;
+ r->registered = 0;
+ add234(sftp_requests, r);
+ return r;
+}
+
+void sftp_register(struct sftp_request *req)
+{
+ req->registered = 1;
+}
+
+struct sftp_request *sftp_find_request(struct sftp_packet *pktin)
+{
+ unsigned long id;
+ struct sftp_request *req;
+
+ if (!pktin) {
+ fxp_internal_error("did not receive a valid SFTP packet\n");
+ return NULL;
+ }
+
+ id = sftp_pkt_getuint32(pktin);
+ req = find234(sftp_requests, &id, sftp_reqfind);
+
+ if (!req || !req->registered) {
+ fxp_internal_error("request ID mismatch\n");
+ sftp_pkt_free(pktin);
+ return NULL;
+ }
+
+ return req;
+}
+
+/* ----------------------------------------------------------------------