mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
tools/xen-9pfsd: add 9pfs version request support
Add the version request of the 9pfs protocol. For the version use the "9P2000.u" variant, as it is supported by Mini-OS and Linux. For the request parsing add all format items needed even in future in order to avoid code churn for those additions later. Signed-off-by: Juergen Gross <jgross@suse.com> Acked-by: Anthony PERARD <anthony.perard@citrix.com> Reviewed-by: Jason Andryuk <jandryuk@gmail.com>
This commit is contained in:
1 parent
7809132b1a
commit
bcec59cf7f
1 file changed
+201
@@ -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);
|
||||
|
||||