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:
Juergen Gross authored and Andrew Cooper committed 2024-02-15 16:24:39 +00:00
1 parent 7809132b1a
commit bcec59cf7f
1 file changed
+201
+201
View File
@@ -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);
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.