[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v3 6/7] Mini-OS: add open and close handling to the 9pfs frontend



Juergen Gross, le lun. 13 févr. 2023 09:44:11 +0100, a ecrit:
> Add the open() and close() support to the 9pfs frontend. This requires
> to split the path name and to walk to the desired directory level.
> 
> Signed-off-by: Juergen Gross <jgross@xxxxxxxx>

Reviewed-by: Samuel Thibault <samuel.thibault@xxxxxxxxxxxx>

> ---
> V2:
> - check path to be canonical
> - avoid short read when walking the path
> - fix get_fid() (Samuel Thibault)
> - return EAGAIN if no free request got (Samuel Thibault)
> V3:
> - add check for "//" in path_canonical() (Samuel Thibault)
> ---
>  9pfront.c | 324 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 322 insertions(+), 2 deletions(-)
> 
> diff --git a/9pfront.c b/9pfront.c
> index 0b8d5461..fb2e5669 100644
> --- a/9pfront.c
> +++ b/9pfront.c
> @@ -6,6 +6,7 @@
>  #include <mini-os/os.h>
>  #include <mini-os/lib.h>
>  #include <mini-os/events.h>
> +#include <mini-os/fcntl.h>
>  #include <mini-os/gnttab.h>
>  #include <mini-os/semaphore.h>
>  #include <mini-os/wait.h>
> @@ -52,13 +53,32 @@ struct dev_9pfs {
>      struct wait_queue_head waitq;
>      struct semaphore ring_out_sem;
>      struct semaphore ring_in_sem;
> +
> +    unsigned long long fid_mask;              /* Bit mask for free fids. */
> +};
> +
> +struct file_9pfs {
> +    uint32_t fid;
> +    struct dev_9pfs *dev;
> +    bool append;
>  };
>  
>  #define DEFAULT_9PFS_RING_ORDER  4
>  
> +/* P9 protocol commands (response is either cmd+1 or P9_CMD_ERROR). */
>  #define P9_CMD_VERSION    100
>  #define P9_CMD_ATTACH     104
>  #define P9_CMD_ERROR      107
> +#define P9_CMD_WALK       110
> +#define P9_CMD_OPEN       112
> +#define P9_CMD_CREATE     114
> +#define P9_CMD_CLUNK      120
> +
> +/* P9 protocol open flags. */
> +#define P9_OREAD            0   /* read */
> +#define P9_OWRITE           1   /* write */
> +#define P9_ORDWR            2   /* read and write */
> +#define P9_OTRUNC          16   /* or'ed in, truncate file first */
>  
>  #define P9_QID_SIZE    13
>  
> @@ -69,10 +89,27 @@ struct p9_header {
>  } __attribute__((packed));
>  
>  #define P9_VERSION        "9P2000.u"
> -#define P9_ROOT_FID       ~0
> +#define P9_ROOT_FID       0
>  
>  static unsigned int ftype_9pfs;
>  
> +static unsigned int get_fid(struct dev_9pfs *dev)
> +{
> +    unsigned int fid;
> +
> +    fid = ffs(dev->fid_mask);
> +    if ( fid )
> +        dev->fid_mask &= ~(1ULL << (fid - 1));
> +
> +     return fid;
> +}
> +
> +static void put_fid(struct dev_9pfs *dev, unsigned int fid)
> +{
> +    if ( fid )
> +        dev->fid_mask |= 1ULL << (fid - 1);
> +}
> +
>  static struct req *get_free_req(struct dev_9pfs *dev)
>  {
>      struct req *req;
> @@ -140,6 +177,9 @@ static void copy_from_ring(struct dev_9pfs *dev, void 
> *data, unsigned int len)
>   * send_9p() and rcv_9p() are using a special format string for specifying
>   * the kind of data sent/expected. Each data item is represented by a single
>   * character:
> + * b: 1 byte unsigned integer (uint8_t)
> + *    Only valid for sending.
> + * u: 2 byte unsigned integer (uint16_t)
>   * U: 4 byte unsigned integer (uint32_t)
>   * S: String (2 byte length + <length> characters)
>   *    in the rcv_9p() case the data for string is allocated (length omitted,
> @@ -153,7 +193,9 @@ static void send_9p(struct dev_9pfs *dev, struct req 
> *req, const char *fmt, ...)
>      va_list ap, aq;
>      const char *f;
>      uint32_t intval;
> +    uint16_t shortval;
>      uint16_t len;
> +    uint8_t byte;
>      char *strval;
>  
>      hdr.size = sizeof(hdr);
> @@ -167,6 +209,14 @@ static void send_9p(struct dev_9pfs *dev, struct req 
> *req, const char *fmt, ...)
>      {
>          switch ( *f )
>          {
> +        case 'b':
> +            hdr.size += 1;
> +            byte = va_arg(aq, unsigned int);
> +            break;
> +        case 'u':
> +            hdr.size += 2;
> +            shortval = va_arg(aq, unsigned int);
> +            break;
>          case 'U':
>              hdr.size += 4;
>              intval = va_arg(aq, unsigned int);
> @@ -196,6 +246,14 @@ static void send_9p(struct dev_9pfs *dev, struct req 
> *req, const char *fmt, ...)
>      {
>          switch ( *f )
>          {
> +        case 'b':
> +            byte = va_arg(ap, unsigned int);
> +            copy_to_ring(dev, &byte, sizeof(byte));
> +            break;
> +        case 'u':
> +            shortval = va_arg(ap, unsigned int);
> +            copy_to_ring(dev, &shortval, sizeof(shortval));
> +            break;
>          case 'U':
>              intval = va_arg(ap, unsigned int);
>              copy_to_ring(dev, &intval, sizeof(intval));
> @@ -288,6 +346,7 @@ static void rcv_9p_copy(struct dev_9pfs *dev, struct req 
> *req,
>      char *str;
>      uint16_t len;
>      uint32_t err;
> +    uint16_t *shortval;
>      uint32_t *val;
>      char **strval;
>      uint8_t *qval;
> @@ -345,6 +404,10 @@ static void rcv_9p_copy(struct dev_9pfs *dev, struct req 
> *req,
>      {
>          switch ( *f )
>          {
> +        case 'u':
> +            shortval = va_arg(ap, uint16_t *);
> +            copy_bufs(&buf1, &buf2, &len1, &len2, shortval, 
> sizeof(*shortval));
> +            break;
>          case 'U':
>              val = va_arg(ap, uint32_t *);
>              copy_bufs(&buf1, &buf2, &len1, &len2, val, sizeof(*val));
> @@ -486,6 +549,170 @@ static int p9_attach(struct dev_9pfs *dev)
>      return ret;
>  }
>  
> +static int p9_clunk(struct dev_9pfs *dev, uint32_t fid)
> +{
> +    struct req *req = get_free_req(dev);
> +    int ret;
> +
> +    if ( !req )
> +        return EAGAIN;
> +
> +    req->cmd = P9_CMD_CLUNK;
> +    send_9p(dev, req, "U", fid);
> +    rcv_9p(dev, req, "");
> +    ret = req->result;
> +
> +    put_free_req(dev, req);
> +
> +    return ret;
> +}
> +
> +static int p9_walk(struct dev_9pfs *dev, uint32_t fid, uint32_t newfid,
> +                   char *name)
> +{
> +    struct req *req = get_free_req(dev);
> +    int ret;
> +    uint16_t nqid;
> +    uint8_t qid[P9_QID_SIZE];
> +
> +    if ( !req )
> +        return EAGAIN;
> +
> +    req->cmd = P9_CMD_WALK;
> +    if ( name[0] )
> +    {
> +        send_9p(dev, req, "UUuS", fid, newfid, 1, name);
> +        rcv_9p(dev, req, "uQ", &nqid, qid);
> +    }
> +    else
> +    {
> +        send_9p(dev, req, "UUu", fid, newfid, 0);
> +        rcv_9p(dev, req, "u", &nqid);
> +    }
> +
> +    ret = req->result;
> +
> +    put_free_req(dev, req);
> +
> +    return ret;
> +}
> +
> +static int p9_open(struct dev_9pfs *dev, uint32_t fid, uint8_t omode)
> +{
> +    struct req *req = get_free_req(dev);
> +    int ret;
> +    uint8_t qid[P9_QID_SIZE];
> +    uint32_t iounit;
> +
> +    if ( !req )
> +        return EAGAIN;
> +
> +    req->cmd = P9_CMD_OPEN;
> +    send_9p(dev, req, "Ub", fid, omode);
> +    rcv_9p(dev, req, "QU", qid, &iounit);
> +
> +    ret = req->result;
> +
> +    put_free_req(dev, req);
> +
> +    return ret;
> +}
> +
> +static int p9_create(struct dev_9pfs *dev, uint32_t fid, char *path,
> +                     uint32_t mode, uint8_t omode)
> +{
> +    struct req *req = get_free_req(dev);
> +    int ret;
> +    uint8_t qid[P9_QID_SIZE];
> +    uint32_t iounit;
> +
> +    if ( !req )
> +        return EAGAIN;
> +
> +    req->cmd = P9_CMD_CREATE;
> +    send_9p(dev, req, "USUbS", fid, path, mode, omode, "");
> +    rcv_9p(dev, req, "QU", qid, &iounit);
> +
> +    ret = req->result;
> +
> +    put_free_req(dev, req);
> +
> +    return ret;
> +}
> +
> +/*
> + * Walk from root <steps> levels with the levels listed in <*paths> as a
> + * sequence of names. Returns the number of steps not having been able to
> + * walk, with <*paths> pointing at the name of the failing walk step.
> + * <fid> will be associated with the last successful walk step. Note that
> + * the first step should always succeed, as it is an empty walk in order
> + * to start at the root (needed for creating new files in root).
> + */
> +static unsigned int walk_9pfs(struct dev_9pfs *dev, uint32_t fid,
> +                              unsigned int steps, char **paths)
> +{
> +    uint32_t curr_fid = P9_ROOT_FID;
> +    int ret;
> +
> +    while ( steps-- )
> +    {
> +        ret = p9_walk(dev, curr_fid, fid, *paths);
> +        if ( ret )
> +            return steps + 1;
> +        curr_fid = fid;
> +        *paths += strlen(*paths) + 1;
> +    }
> +
> +    return 0;
> +}
> +
> +static unsigned int split_path(const char *pathname, char **split_ptr)
> +{
> +    unsigned int parts = 1;
> +    char *p;
> +
> +    *split_ptr = strdup(pathname);
> +
> +    for ( p = strchr(*split_ptr, '/'); p; p = strchr(p + 1, '/') )
> +    {
> +        *p = 0;
> +        parts++;
> +    }
> +
> +    return parts;
> +}
> +
> +static bool path_canonical(const char *pathname)
> +{
> +    unsigned int len = strlen(pathname);
> +    const char *c;
> +
> +    /* Empty path is allowed. */
> +    if ( !len )
> +        return true;
> +
> +    /* No trailing '/'. */
> +    if ( pathname[len - 1] == '/' )
> +        return false;
> +
> +    /* No self or parent references. */
> +    c = pathname;
> +    while ( (c = strstr(c, "/.")) != NULL )
> +    {
> +        if ( c[2] == '.' )
> +            c++;
> +        if ( c[2] == 0 || c[2] == '/' )
> +            return false;
> +        c += 2;
> +    }
> +
> +    /* No "//". */
> +    if ( strstr(pathname, "//") )
> +        return false;
> +
> +    return true;
> +}
> +
>  static int connect_9pfs(struct dev_9pfs *dev)
>  {
>      int ret;
> @@ -504,10 +731,101 @@ static void intr_9pfs(evtchn_port_t port, struct 
> pt_regs *regs, void *data)
>      wake_up(&dev->waitq);
>  }
>  
> +static int close_9pfs(struct file *file)
> +{
> +    struct file_9pfs *f9pfs = file->filedata;
> +
> +    if ( f9pfs->fid != P9_ROOT_FID )
> +    {
> +        p9_clunk(f9pfs->dev, f9pfs->fid);
> +        put_fid(f9pfs->dev, f9pfs->fid);
> +    }
> +
> +    free(f9pfs);
> +
> +    return 0;
> +}
> +
>  static int open_9pfs(struct mount_point *mnt, const char *pathname, int 
> flags,
>                       mode_t mode)
>  {
> -    errno = ENOSYS;
> +    int fd;
> +    char *path = NULL;
> +    char *p;
> +    struct file *file;
> +    struct file_9pfs *f9pfs;
> +    uint16_t nwalk;
> +    uint8_t omode;
> +    int ret;
> +
> +    if ( !path_canonical(pathname) )
> +        return EINVAL;
> +
> +    f9pfs = calloc(1, sizeof(*f9pfs));
> +    f9pfs->dev = mnt->dev;
> +    f9pfs->fid = P9_ROOT_FID;
> +
> +    fd = alloc_fd(ftype_9pfs);
> +    file = get_file_from_fd(fd);
> +    file->filedata = f9pfs;
> +
> +    switch ( flags & O_ACCMODE )
> +    {
> +    case O_RDONLY:
> +        omode = P9_OREAD;
> +        break;
> +    case O_WRONLY:
> +        omode = P9_OWRITE;
> +        break;
> +    case O_RDWR:
> +        omode = P9_ORDWR;
> +        break;
> +    default:
> +        ret = EINVAL;
> +        goto err;
> +    }
> +
> +    if ( flags & O_TRUNC )
> +        omode |= P9_OTRUNC;
> +    f9pfs->append = flags & O_APPEND;
> +
> +    nwalk = split_path(pathname, &path);
> +
> +    f9pfs->fid = get_fid(mnt->dev);
> +    if ( !f9pfs->fid )
> +    {
> +        ret = ENFILE;
> +        goto err;
> +    }
> +    p = path;
> +    nwalk = walk_9pfs(mnt->dev, f9pfs->fid, nwalk, &p);
> +    if ( nwalk )
> +    {
> +        if ( nwalk > 1 || !(flags & O_CREAT) )
> +        {
> +            ret = ENOENT;
> +            goto err;
> +        }
> +
> +        ret = p9_create(mnt->dev, f9pfs->fid, p, mode, omode);
> +        if ( ret )
> +            goto err;
> +        goto out;
> +    }
> +
> +    ret = p9_open(mnt->dev, f9pfs->fid, omode);
> +    if ( ret )
> +        goto err;
> +
> + out:
> +    free(path);
> +
> +    return fd;
> +
> + err:
> +    free(path);
> +    close(fd);
> +    errno = ret;
>  
>      return -1;
>  }
> @@ -552,6 +870,7 @@ void *init_9pfront(unsigned int id, const char *mnt)
>      init_waitqueue_head(&dev->waitq);
>      init_SEMAPHORE(&dev->ring_out_sem, 1);
>      init_SEMAPHORE(&dev->ring_in_sem, 1);
> +    dev->fid_mask = ~0ULL;
>  
>      for ( i = 0; i < N_REQS; i++ )
>      {
> @@ -753,6 +1072,7 @@ void shutdown_9pfront(void *dev)
>  
>  static const struct file_ops ops_9pfs = {
>      .name = "9pfs",
> +    .close = close_9pfs,
>  };
>  
>  __attribute__((constructor))
> -- 
> 2.35.3
> 

-- 
Samuel
---
Pour une évaluation indépendante, transparente et rigoureuse !
Je soutiens la Commission d'Évaluation de l'Inria.



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.