X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=qemu%2Froms%2Fipxe%2Fsrc%2Fnet%2Foncrpc%2Fnfs_open.c;fp=qemu%2Froms%2Fipxe%2Fsrc%2Fnet%2Foncrpc%2Fnfs_open.c;h=c0dceb82f63a1e7787c90f3653ef5d7500028f3f;hb=e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb;hp=0000000000000000000000000000000000000000;hpb=9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00;p=kvmfornfv.git diff --git a/qemu/roms/ipxe/src/net/oncrpc/nfs_open.c b/qemu/roms/ipxe/src/net/oncrpc/nfs_open.c new file mode 100644 index 000000000..c0dceb82f --- /dev/null +++ b/qemu/roms/ipxe/src/net/oncrpc/nfs_open.c @@ -0,0 +1,683 @@ +/* + * Copyright (C) 2013 Marin Hannache . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Network File System protocol + * + */ + +FEATURE ( FEATURE_PROTOCOL, "NFS", DHCP_EB_FEATURE_NFS, 1 ); + +#define NFS_RSIZE 100000 + +enum nfs_pm_state { + NFS_PORTMAP_NONE = 0, + NFS_PORTMAP_MOUNTPORT, + NFS_PORTMAP_NFSPORT, + MFS_PORTMAP_CLOSED, +}; + +enum nfs_mount_state { + NFS_MOUNT_NONE = 0, + NFS_MOUNT_MNT, + NFS_MOUNT_UMNT, + NFS_MOUNT_CLOSED, +}; + +enum nfs_state { + NFS_NONE = 0, + NFS_LOOKUP, + NFS_LOOKUP_SENT, + NFS_READLINK, + NFS_READLINK_SENT, + NFS_READ, + NFS_READ_SENT, + NFS_CLOSED, +}; + +/** + * A NFS request + * + */ +struct nfs_request { + /** Reference counter */ + struct refcnt refcnt; + /** Data transfer interface */ + struct interface xfer; + + struct interface pm_intf; + struct interface mount_intf; + struct interface nfs_intf; + + enum nfs_pm_state pm_state; + enum nfs_mount_state mount_state; + enum nfs_state nfs_state; + + struct oncrpc_session pm_session; + struct oncrpc_session mount_session; + struct oncrpc_session nfs_session; + + struct oncrpc_cred_sys auth_sys; + + char * hostname; + struct nfs_uri uri; + + struct nfs_fh readlink_fh; + struct nfs_fh current_fh; + uint64_t file_offset; + + size_t remaining; + int eof; +}; + +static void nfs_step ( struct nfs_request *nfs ); + +/** + * Free NFS request + * + * @v refcnt Reference counter + */ +static void nfs_free ( struct refcnt *refcnt ) { + struct nfs_request *nfs; + + nfs = container_of ( refcnt, struct nfs_request, refcnt ); + DBGC ( nfs, "NFS_OPEN %p freed\n", nfs ); + + nfs_uri_free ( &nfs->uri ); + + free ( nfs->hostname ); + free ( nfs->auth_sys.hostname ); + free ( nfs ); +} + +/** + * Mark NFS operation as complete + * + * @v nfs NFS request + * @v rc Return status code + */ +static void nfs_done ( struct nfs_request *nfs, int rc ) { + if ( rc == 0 && nfs->nfs_state != NFS_CLOSED ) + rc = -ECONNRESET; + + DBGC ( nfs, "NFS_OPEN %p completed (%s)\n", nfs, strerror ( rc ) ); + + intf_shutdown ( &nfs->xfer, rc ); + intf_shutdown ( &nfs->pm_intf, rc ); + intf_shutdown ( &nfs->mount_intf, rc ); + intf_shutdown ( &nfs->nfs_intf, rc ); +} + +static int nfs_connect ( struct interface *intf, uint16_t port, + const char *hostname ) { + struct sockaddr_tcpip peer; + struct sockaddr_tcpip local; + + if ( ! intf || ! hostname || ! port ) + return -EINVAL; + + memset ( &peer, 0, sizeof ( peer ) ); + memset ( &local, 0, sizeof ( local ) ); + peer.st_port = htons ( port ); + + /* Use a local port < 1024 to avoid using the 'insecure' option in + * /etc/exports file. */ + local.st_flags = TCPIP_BIND_PRIVILEGED; + + return xfer_open_named_socket ( intf, SOCK_STREAM, + ( struct sockaddr * ) &peer, hostname, + ( struct sockaddr * ) &local ); +} + +static void nfs_pm_step ( struct nfs_request *nfs ) { + int rc; + + if ( ! xfer_window ( &nfs->pm_intf ) ) + return; + + if ( nfs->pm_state == NFS_PORTMAP_NONE ) { + DBGC ( nfs, "NFS_OPEN %p GETPORT call (mount)\n", nfs ); + + rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session, + ONCRPC_MOUNT, MOUNT_VERS, + PORTMAP_PROTO_TCP ); + if ( rc != 0 ) + goto err; + + nfs->pm_state++; + return; + } + + if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) { + DBGC ( nfs, "NFS_OPEN %p GETPORT call (nfs)\n", nfs ); + + rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session, + ONCRPC_NFS, NFS_VERS, + PORTMAP_PROTO_TCP ); + if ( rc != 0 ) + goto err; + + return; + } + + return; +err: + nfs_done ( nfs, rc ); +} + +static int nfs_pm_deliver ( struct nfs_request *nfs, + struct io_buffer *io_buf, + struct xfer_metadata *meta __unused ) { + int rc; + struct oncrpc_reply reply; + struct portmap_getport_reply getport_reply; + + oncrpc_get_reply ( &nfs->pm_session, &reply, io_buf ); + if ( reply.accept_state != 0 ) + { + rc = -EPROTO; + goto err; + } + + if ( nfs->pm_state == NFS_PORTMAP_MOUNTPORT ) { + DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (mount)\n", nfs ); + + rc = portmap_get_getport_reply ( &getport_reply, &reply ); + if ( rc != 0 ) + goto err; + + rc = nfs_connect ( &nfs->mount_intf, getport_reply.port, + nfs->hostname ); + if ( rc != 0 ) + goto err; + + nfs->pm_state++; + nfs_pm_step ( nfs ); + + goto done; + } + + if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) { + DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (nfs)\n", nfs ); + + rc = portmap_get_getport_reply ( &getport_reply, &reply ); + if ( rc != 0 ) + goto err; + + rc = nfs_connect ( &nfs->nfs_intf, getport_reply.port, + nfs->hostname ); + if ( rc != 0 ) + goto err; + + intf_shutdown ( &nfs->pm_intf, 0 ); + nfs->pm_state++; + + goto done; + } + + rc = -EPROTO; +err: + nfs_done ( nfs, rc ); +done: + free_iob ( io_buf ); + return 0; +} + +static void nfs_mount_step ( struct nfs_request *nfs ) { + int rc; + + if ( ! xfer_window ( &nfs->mount_intf ) ) + return; + + if ( nfs->mount_state == NFS_MOUNT_NONE ) { + DBGC ( nfs, "NFS_OPEN %p MNT call (%s)\n", nfs, + nfs_uri_mountpoint ( &nfs->uri ) ); + + rc = mount_mnt ( &nfs->mount_intf, &nfs->mount_session, + nfs_uri_mountpoint ( &nfs->uri ) ); + if ( rc != 0 ) + goto err; + + nfs->mount_state++; + return; + } + + if ( nfs->mount_state == NFS_MOUNT_UMNT ) { + DBGC ( nfs, "NFS_OPEN %p UMNT call\n", nfs ); + + rc = mount_umnt ( &nfs->mount_intf, &nfs->mount_session, + nfs_uri_mountpoint ( &nfs->uri ) ); + if ( rc != 0 ) + goto err; + } + + return; +err: + nfs_done ( nfs, rc ); +} + +static int nfs_mount_deliver ( struct nfs_request *nfs, + struct io_buffer *io_buf, + struct xfer_metadata *meta __unused ) { + int rc; + struct oncrpc_reply reply; + struct mount_mnt_reply mnt_reply; + + oncrpc_get_reply ( &nfs->mount_session, &reply, io_buf ); + if ( reply.accept_state != 0 ) + { + rc = -EPROTO; + goto err; + } + + if ( nfs->mount_state == NFS_MOUNT_MNT ) { + DBGC ( nfs, "NFS_OPEN %p got MNT reply\n", nfs ); + rc = mount_get_mnt_reply ( &mnt_reply, &reply ); + if ( rc != 0 ) { + switch ( mnt_reply.status ) { + case MNT3ERR_NOTDIR: + case MNT3ERR_NOENT: + case MNT3ERR_ACCES: + break; + + default: + goto err; + } + + if ( ! strcmp ( nfs_uri_mountpoint ( &nfs->uri ), + "/" ) ) + goto err; + + if ( ( rc = nfs_uri_next_mountpoint ( &nfs->uri ) ) ) + goto err; + + DBGC ( nfs, "NFS_OPEN %p MNT failed retrying with " \ + "%s\n", nfs, nfs_uri_mountpoint ( &nfs->uri ) ); + + nfs->mount_state--; + nfs_mount_step ( nfs ); + + goto done; + } + + nfs->current_fh = mnt_reply.fh; + nfs->nfs_state = NFS_LOOKUP; + nfs_step ( nfs ); + + goto done; + } + + if ( nfs->mount_state == NFS_MOUNT_UMNT ) { + DBGC ( nfs, "NFS_OPEN %p got UMNT reply\n", nfs ); + nfs_done ( nfs, 0 ); + + goto done; + } + + rc = -EPROTO; +err: + nfs_done ( nfs, rc ); +done: + free_iob ( io_buf ); + return 0; +} + +static void nfs_step ( struct nfs_request *nfs ) { + int rc; + char *path_component; + + if ( ! xfer_window ( &nfs->nfs_intf ) ) + return; + + if ( nfs->nfs_state == NFS_LOOKUP ) { + path_component = nfs_uri_next_path_component ( &nfs->uri ); + + DBGC ( nfs, "NFS_OPEN %p LOOKUP call (%s)\n", nfs, + path_component ); + + rc = nfs_lookup ( &nfs->nfs_intf, &nfs->nfs_session, + &nfs->current_fh, path_component ); + if ( rc != 0 ) + goto err; + + nfs->nfs_state++; + return; + } + + + if ( nfs->nfs_state == NFS_READLINK ) { + DBGC ( nfs, "NFS_OPEN %p READLINK call\n", nfs ); + + rc = nfs_readlink ( &nfs->nfs_intf, &nfs->nfs_session, + &nfs->readlink_fh ); + if ( rc != 0 ) + goto err; + + nfs->nfs_state++; + return; + } + + if ( nfs->nfs_state == NFS_READ ) { + DBGC ( nfs, "NFS_OPEN %p READ call\n", nfs ); + + rc = nfs_read ( &nfs->nfs_intf, &nfs->nfs_session, + &nfs->current_fh, nfs->file_offset, + NFS_RSIZE ); + if ( rc != 0 ) + goto err; + + nfs->nfs_state++; + return; + } + + return; +err: + nfs_done ( nfs, rc ); +} + +static int nfs_deliver ( struct nfs_request *nfs, + struct io_buffer *io_buf, + struct xfer_metadata *meta __unused ) { + int rc; + struct oncrpc_reply reply; + + if ( nfs->remaining == 0 ) { + oncrpc_get_reply ( &nfs->nfs_session, &reply, io_buf ); + if ( reply.accept_state != 0 ) { + rc = -EPROTO; + goto err; + } + } + + if ( nfs->nfs_state == NFS_LOOKUP_SENT ) { + struct nfs_lookup_reply lookup_reply; + + DBGC ( nfs, "NFS_OPEN %p got LOOKUP reply\n", nfs ); + + rc = nfs_get_lookup_reply ( &lookup_reply, &reply ); + if ( rc != 0 ) + goto err; + + if ( lookup_reply.ent_type == NFS_ATTR_SYMLINK ) { + nfs->readlink_fh = lookup_reply.fh; + nfs->nfs_state = NFS_READLINK; + } else { + nfs->current_fh = lookup_reply.fh; + + if ( nfs->uri.lookup_pos[0] == '\0' ) + nfs->nfs_state = NFS_READ; + else + nfs->nfs_state--; + } + + nfs_step ( nfs ); + goto done; + } + + if ( nfs->nfs_state == NFS_READLINK_SENT ) { + char *path; + struct nfs_readlink_reply readlink_reply; + + DBGC ( nfs, "NFS_OPEN %p got READLINK reply\n", nfs ); + + rc = nfs_get_readlink_reply ( &readlink_reply, &reply ); + if ( rc != 0 ) + goto err; + + if ( readlink_reply.path_len == 0 ) + { + rc = -EINVAL; + goto err; + } + + if ( ! ( path = strndup ( readlink_reply.path, + readlink_reply.path_len ) ) ) + { + rc = -ENOMEM; + goto err; + } + + nfs_uri_symlink ( &nfs->uri, path ); + free ( path ); + + DBGC ( nfs, "NFS_OPEN %p new path: %s\n", nfs, + nfs->uri.path ); + + nfs->nfs_state = NFS_LOOKUP; + nfs_step ( nfs ); + goto done; + } + + if ( nfs->nfs_state == NFS_READ_SENT ) { + if ( nfs->remaining == 0 ) { + DBGC ( nfs, "NFS_OPEN %p got READ reply\n", nfs ); + + struct nfs_read_reply read_reply; + + rc = nfs_get_read_reply ( &read_reply, &reply ); + if ( rc != 0 ) + goto err; + + if ( nfs->file_offset == 0 ) { + DBGC2 ( nfs, "NFS_OPEN %p size: %llu bytes\n", + nfs, read_reply.filesize ); + + xfer_seek ( &nfs->xfer, read_reply.filesize ); + xfer_seek ( &nfs->xfer, 0 ); + } + + nfs->file_offset += read_reply.count; + nfs->remaining = read_reply.count; + nfs->eof = read_reply.eof; + } + + size_t len = iob_len ( io_buf ); + if ( len > nfs->remaining ) + iob_unput ( io_buf, len - nfs->remaining ); + + nfs->remaining -= iob_len ( io_buf ); + + DBGC ( nfs, "NFS_OPEN %p got %zd bytes\n", nfs, + iob_len ( io_buf ) ); + + rc = xfer_deliver_iob ( &nfs->xfer, iob_disown ( io_buf ) ); + if ( rc != 0 ) + goto err; + + if ( nfs->remaining == 0 ) { + if ( ! nfs->eof ) { + nfs->nfs_state--; + nfs_step ( nfs ); + } else { + intf_shutdown ( &nfs->nfs_intf, 0 ); + nfs->nfs_state++; + nfs->mount_state++; + nfs_mount_step ( nfs ); + } + } + + return 0; + } + + rc = -EPROTO; +err: + nfs_done ( nfs, rc ); +done: + free_iob ( io_buf ); + return 0; +} + +/***************************************************************************** + * Interfaces + * + */ + +static struct interface_operation nfs_xfer_operations[] = { + INTF_OP ( intf_close, struct nfs_request *, nfs_done ), +}; + +/** NFS data transfer interface descriptor */ +static struct interface_descriptor nfs_xfer_desc = + INTF_DESC ( struct nfs_request, xfer, nfs_xfer_operations ); + +static struct interface_operation nfs_pm_operations[] = { + INTF_OP ( intf_close, struct nfs_request *, nfs_done ), + INTF_OP ( xfer_deliver, struct nfs_request *, nfs_pm_deliver ), + INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_pm_step ), +}; + +static struct interface_descriptor nfs_pm_desc = + INTF_DESC ( struct nfs_request, pm_intf, nfs_pm_operations ); + +static struct interface_operation nfs_mount_operations[] = { + INTF_OP ( intf_close, struct nfs_request *, nfs_done ), + INTF_OP ( xfer_deliver, struct nfs_request *, nfs_mount_deliver ), + INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_mount_step ), +}; + +static struct interface_descriptor nfs_mount_desc = + INTF_DESC ( struct nfs_request, mount_intf, nfs_mount_operations ); + +static struct interface_operation nfs_operations[] = { + INTF_OP ( intf_close, struct nfs_request *, nfs_done ), + INTF_OP ( xfer_deliver, struct nfs_request *, nfs_deliver ), + INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_step ), +}; + +static struct interface_descriptor nfs_desc = + INTF_DESC_PASSTHRU ( struct nfs_request, nfs_intf, nfs_operations, + xfer ); + +/***************************************************************************** + * + * URI opener + * + */ + +static int nfs_parse_uri ( struct nfs_request *nfs, const struct uri *uri ) { + int rc; + + if ( ! uri || ! uri->host || ! uri->path ) + return -EINVAL; + + if ( ( rc = nfs_uri_init ( &nfs->uri, uri ) ) != 0 ) + return rc; + + if ( ! ( nfs->hostname = strdup ( uri->host ) ) ) { + rc = -ENOMEM; + goto err_hostname; + } + + DBGC ( nfs, "NFS_OPEN %p URI parsed: (mountpoint=%s, path=%s)\n", + nfs, nfs_uri_mountpoint ( &nfs->uri), nfs->uri.path ); + + return 0; + +err_hostname: + nfs_uri_free ( &nfs->uri ); + return rc; +} + +/** + * Initiate a NFS connection + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int nfs_open ( struct interface *xfer, struct uri *uri ) { + int rc; + struct nfs_request *nfs; + + nfs = zalloc ( sizeof ( *nfs ) ); + if ( ! nfs ) + return -ENOMEM; + + rc = nfs_parse_uri( nfs, uri ); + if ( rc != 0 ) + goto err_uri; + + rc = oncrpc_init_cred_sys ( &nfs->auth_sys ); + if ( rc != 0 ) + goto err_cred; + + ref_init ( &nfs->refcnt, nfs_free ); + intf_init ( &nfs->xfer, &nfs_xfer_desc, &nfs->refcnt ); + intf_init ( &nfs->pm_intf, &nfs_pm_desc, &nfs->refcnt ); + intf_init ( &nfs->mount_intf, &nfs_mount_desc, &nfs->refcnt ); + intf_init ( &nfs->nfs_intf, &nfs_desc, &nfs->refcnt ); + + portmap_init_session ( &nfs->pm_session, &nfs->auth_sys.credential ); + mount_init_session ( &nfs->mount_session, &nfs->auth_sys.credential ); + nfs_init_session ( &nfs->nfs_session, &nfs->auth_sys.credential ); + + DBGC ( nfs, "NFS_OPEN %p connecting to port mapper (%s:%d)...\n", nfs, + nfs->hostname, PORTMAP_PORT ); + + rc = nfs_connect ( &nfs->pm_intf, PORTMAP_PORT, nfs->hostname ); + if ( rc != 0 ) + goto err_connect; + + /* Attach to parent interface, mortalise self, and return */ + intf_plug_plug ( &nfs->xfer, xfer ); + ref_put ( &nfs->refcnt ); + + return 0; + +err_connect: + free ( nfs->auth_sys.hostname ); +err_cred: + nfs_uri_free ( &nfs->uri ); + free ( nfs->hostname ); +err_uri: + free ( nfs ); + return rc; +} + +/** NFS URI opener */ +struct uri_opener nfs_uri_opener __uri_opener = { + .scheme = "nfs", + .open = nfs_open, +};