@@ -22,8 +22,12 @@
#include "xen-9pfsd.h"
/* P9 protocol commands (response is either cmd+1 or P9_CMD_ERROR). */
+#define P9_CMD_VERSION 100
#define P9_CMD_ERROR 107
+#define P9_MIN_MSIZE 2048
+#define P9_VERSION "9P2000.u"
+
struct p9_qid {
uint8_t type;
#define QID_TYPE_DIR 0x80
@@ -294,6 +298,169 @@ static unsigned int add_string(struct ring *ring, const char *str,
return ret;
}
+static bool chk_data(struct ring *ring, void *data, unsigned int len)
+{
+ struct p9_header *hdr = ring->buffer;
+
+ if ( data + len <= ring->buffer + hdr->size )
+ return true;
+
+ errno = E2BIG;
+
+ return false;
+}
+
+static bool fill_data_elem(void **par, void **array, unsigned int *array_sz,
+ unsigned int elem_sz, void *data)
+{
+ if ( *array_sz && !*array )
+ {
+ *array = calloc(*array_sz, elem_sz);
+ if ( !*array )
+ return false;
+ *par = *array;
+ }
+
+ memcpy(*par, data, elem_sz);
+
+ if ( *array_sz )
+ {
+ *par += elem_sz;
+ *array_sz -= 1;
+ }
+
+ return true;
+}
+
+/*
+ * Fill variables with request data.
+ * fmt is a sequence of format characters. Supported characters are:
+ * a: an array (2 bytes number of elements + the following format as elements)
+ * The number of elements is stored in the first unsigned int parameter, the
+ * next parameter is a pointer to an array of elements as denoted by the next
+ * format character. The array is allocated dynamically.
+ * b: 1 byte unsigned integer
+ * The value is stored in the next parameter with type uint8_t.
+ * D: Data blob (4 byte length + <length> bytes)
+ * 2 parameters are consumed, first an unsigned int for the length, then a
+ * pointer to the first uint8_t value.
+ * No array support.
+ * L: 8 byte unsigned integer
+ * The value is stored in the next parameter with type uint64_t.
+ * S: String (2 byte length + <length> characters)
+ * The 0-terminated string is stored in device->str + off, off is stored in
+ * the next parameter with type unsigned int.
+ * U: 4 byte unsigned integer
+ * The value is stored in the next parameter with type uint32_t.
+ *
+ * Return value: number of filled variables, errno will be set in case of
+ * error.
+ */
+static int fill_data(struct ring *ring, const char *fmt, ...)
+{
+ struct p9_header *hdr = ring->buffer;
+ void *data = hdr + 1;
+ void *par;
+ unsigned int pars = 0;
+ const char *f;
+ va_list ap;
+ unsigned int len;
+ unsigned int str_off;
+ unsigned int array_sz = 0;
+ void **array = NULL;
+
+ va_start(ap, fmt);
+
+ for ( f = fmt; *f; f++ )
+ {
+ if ( !array_sz )
+ par = va_arg(ap, void *);
+
+ switch ( *f )
+ {
+ case 'a':
+ f++;
+ if ( !*f || array_sz )
+ fmt_err(fmt);
+ if ( !chk_data(ring, data, sizeof(uint16_t)) )
+ return pars;
+ array_sz = get_unaligned((uint16_t *)data);
+ data += sizeof(uint16_t);
+ *(unsigned int *)par = array_sz;
+ array = va_arg(ap, void **);
+ *array = NULL;
+ break;
+
+ case 'b':
+ if ( !chk_data(ring, data, sizeof(uint8_t)) )
+ return pars;
+ if ( !fill_data_elem(&par, array, &array_sz, sizeof(uint8_t),
+ data) )
+ return pars;
+ data += sizeof(uint8_t);
+ break;
+
+ case 'D':
+ if ( array_sz )
+ fmt_err(fmt);
+ if ( !chk_data(ring, data, sizeof(uint32_t)) )
+ return pars;
+ len = get_unaligned((uint32_t *)data);
+ data += sizeof(uint32_t);
+ *(unsigned int *)par = len;
+ par = va_arg(ap, void *);
+ if ( !chk_data(ring, data, len) )
+ return pars;
+ memcpy(par, data, len);
+ data += len;
+ break;
+
+ case 'L':
+ if ( !chk_data(ring, data, sizeof(uint64_t)) )
+ return pars;
+ if ( !fill_data_elem(&par, array, &array_sz, sizeof(uint64_t),
+ data) )
+ return pars;
+ data += sizeof(uint64_t);
+ break;
+
+ case 'S':
+ if ( !chk_data(ring, data, sizeof(uint16_t)) )
+ return pars;
+ len = get_unaligned((uint16_t *)data);
+ data += sizeof(uint16_t);
+ if ( !chk_data(ring, data, len) )
+ return pars;
+ str_off = add_string(ring, data, len);
+ if ( str_off == ~0 )
+ return pars;
+ if ( !fill_data_elem(&par, array, &array_sz, sizeof(unsigned int),
+ &str_off) )
+ return pars;
+ data += len;
+ break;
+
+ case 'U':
+ if ( !chk_data(ring, data, sizeof(uint32_t)) )
+ return pars;
+ if ( !fill_data_elem(&par, array, &array_sz, sizeof(uint32_t),
+ data) )
+ return pars;
+ data += sizeof(uint32_t);
+ break;
+
+ default:
+ fmt_err(fmt);
+ }
+
+ if ( array_sz )
+ f--;
+ pars++;
+ }
+
+ return pars;
+}
+
static void p9_error(struct ring *ring, uint16_t tag, uint32_t err)
{
unsigned int erroff;
@@ -305,6 +472,36 @@ static void p9_error(struct ring *ring, uint16_t tag, uint32_t err)
&err);
}
+static void p9_version(struct ring *ring, struct p9_header *hdr)
+{
+ uint32_t max_size;
+ unsigned int off;
+ char *version;
+ int ret;
+
+ ret = fill_data(ring, "US", &max_size, &off);
+ if ( ret != 2 )
+ {
+ p9_error(ring, hdr->tag, errno);
+ return;
+ }
+
+ if ( max_size < P9_MIN_MSIZE )
+ {
+ p9_error(ring, hdr->tag, EMSGSIZE);
+ return;
+ }
+
+ if ( max_size < ring->max_size )
+ ring->max_size = max_size;
+
+ version = ring->str + off;
+ if ( strcmp(version, P9_VERSION) )
+ version = "unknown";
+
+ fill_buffer(ring, hdr->cmd + 1, hdr->tag, "US", &ring->max_size, version);
+}
+
void *io_thread(void *arg)
{
struct ring *ring = arg;
@@ -360,6 +557,10 @@ void *io_thread(void *arg)
switch ( hdr.cmd )
{
+ case P9_CMD_VERSION:
+ p9_version(ring, &hdr);
+ break;
+
default:
syslog(LOG_DEBUG, "%u.%u sent unhandled command %u\n",
ring->device->domid, ring->device->devid, hdr.cmd);