diff options
Diffstat (limited to 'qemu/roms/ipxe/src/net/oncrpc')
-rw-r--r-- | qemu/roms/ipxe/src/net/oncrpc/mount.c | 119 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/net/oncrpc/nfs.c | 288 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/net/oncrpc/nfs_open.c | 683 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/net/oncrpc/nfs_uri.c | 148 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/net/oncrpc/oncrpc_iob.c | 200 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/net/oncrpc/portmap.c | 90 |
6 files changed, 1528 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/net/oncrpc/mount.c b/qemu/roms/ipxe/src/net/oncrpc/mount.c new file mode 100644 index 000000000..8838a147c --- /dev/null +++ b/qemu/roms/ipxe/src/net/oncrpc/mount.c @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>. + * + * 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 <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <libgen.h> +#include <byteswap.h> +#include <ipxe/time.h> +#include <ipxe/iobuf.h> +#include <ipxe/open.h> +#include <ipxe/features.h> +#include <ipxe/oncrpc.h> +#include <ipxe/oncrpc_iob.h> +#include <ipxe/nfs.h> +#include <ipxe/mount.h> + +/** @file + * + * NFS MOUNT protocol + * + */ + +/** MNT procedure number */ +#define MOUNT_MNT 1 +/** UMNT procedure number */ +#define MOUNT_UMNT 3 + +/** + * Send a MNT request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v mountpoinrt The path of the directory to mount. + * @ret rc Return status code + */ +int mount_mnt ( struct interface *intf, struct oncrpc_session *session, + const char *mountpoint ) { + struct oncrpc_field fields[] = { + ONCRPC_FIELD ( str, mountpoint ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, MOUNT_MNT, fields ); +} + +/** + * Send a UMNT request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v mountpoinrt The path of the directory to unmount. + * @ret rc Return status code + */ +int mount_umnt ( struct interface *intf, struct oncrpc_session *session, + const char *mountpoint ) { + struct oncrpc_field fields[] = { + ONCRPC_FIELD ( str, mountpoint ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, MOUNT_UMNT, fields ); +} + +/** + * Parse an MNT reply + * + * @v mnt_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int mount_get_mnt_reply ( struct mount_mnt_reply *mnt_reply, + struct oncrpc_reply *reply ) { + if ( ! mnt_reply || ! reply ) + return -EINVAL; + + mnt_reply->status = oncrpc_iob_get_int ( reply->data ); + + switch ( mnt_reply->status ) + { + case MNT3_OK: + break; + case MNT3ERR_NOENT: + return -ENOENT; + case MNT3ERR_IO: + return -EIO; + case MNT3ERR_ACCES: + return -EACCES; + case MNT3ERR_NOTDIR: + return -ENOTDIR; + case MNT3ERR_NAMETOOLONG: + return -ENAMETOOLONG; + default: + return -EPROTO; + } + + nfs_iob_get_fh ( reply->data, &mnt_reply->fh ); + + return 0; +} diff --git a/qemu/roms/ipxe/src/net/oncrpc/nfs.c b/qemu/roms/ipxe/src/net/oncrpc/nfs.c new file mode 100644 index 000000000..b6118f91a --- /dev/null +++ b/qemu/roms/ipxe/src/net/oncrpc/nfs.c @@ -0,0 +1,288 @@ +/* + * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>. + * + * 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 <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <libgen.h> +#include <byteswap.h> +#include <ipxe/time.h> +#include <ipxe/iobuf.h> +#include <ipxe/open.h> +#include <ipxe/features.h> +#include <ipxe/nfs.h> +#include <ipxe/oncrpc.h> +#include <ipxe/oncrpc_iob.h> +#include <ipxe/portmap.h> +#include <ipxe/mount.h> +#include <ipxe/settings.h> + +/** @file + * + * Network File System protocol + * + */ + +/** NFS LOOKUP procedure */ +#define NFS_LOOKUP 3 +/** NFS READLINK procedure */ +#define NFS_READLINK 5 +/** NFS READ procedure */ +#define NFS_READ 6 + +/** + * Extract a file handle from the beginning of an I/O buffer + * + * @v io_buf I/O buffer + * @v fh File handle + * @ret size Size of the data read + */ +size_t nfs_iob_get_fh ( struct io_buffer *io_buf, struct nfs_fh *fh ) { + fh->size = oncrpc_iob_get_int ( io_buf ); + + if ( fh->size > 64 ) + return sizeof ( uint32_t ); + + memcpy (fh->fh, io_buf->data, fh->size ); + iob_pull ( io_buf, fh->size ); + + return fh->size + sizeof ( uint32_t ); +} + +/** + * Add a file handle to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v fh File handle + * @ret size Size of the data written + */ +size_t nfs_iob_add_fh ( struct io_buffer *io_buf, const struct nfs_fh *fh ) { + size_t s; + + s = oncrpc_iob_add_int ( io_buf, fh->size ); + memcpy ( iob_put ( io_buf, fh->size ), &fh->fh, fh->size ); + + return s + fh->size; +} + +/** + * Send a LOOKUP request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v fh The file handle of the the directory + * @v filename The file name + * @ret rc Return status code + */ +int nfs_lookup ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh, const char *filename ) { + struct oncrpc_field fields[] = { + ONCRPC_SUBFIELD ( array, fh->size, &fh->fh ), + ONCRPC_FIELD ( str, filename ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, NFS_LOOKUP, fields ); +} + +/** + * Send a READLINK request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v fh The symlink file handle + * @ret rc Return status code + */ +int nfs_readlink ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh ) { + struct oncrpc_field fields[] = { + ONCRPC_SUBFIELD ( array, fh->size, &fh->fh ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, NFS_READLINK, fields ); +} + +/** + * Send a READ request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v fh The file handle + * @v offset Offset + * @v count Byte count + * @ret rc Return status code + */ +int nfs_read ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh, uint64_t offset, uint32_t count ) { + struct oncrpc_field fields[] = { + ONCRPC_SUBFIELD ( array, fh->size, &fh->fh ), + ONCRPC_FIELD ( int64, offset ), + ONCRPC_FIELD ( int32, count ), + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, NFS_READ, fields ); +} + +/** + * Parse a LOOKUP reply + * + * @v lookup_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int nfs_get_lookup_reply ( struct nfs_lookup_reply *lookup_reply, + struct oncrpc_reply *reply ) { + if ( ! lookup_reply || ! reply ) + return -EINVAL; + + lookup_reply->status = oncrpc_iob_get_int ( reply->data ); + switch ( lookup_reply->status ) + { + case NFS3_OK: + break; + case NFS3ERR_PERM: + return -EPERM; + case NFS3ERR_NOENT: + return -ENOENT; + case NFS3ERR_IO: + return -EIO; + case NFS3ERR_ACCES: + return -EACCES; + case NFS3ERR_NOTDIR: + return -ENOTDIR; + case NFS3ERR_NAMETOOLONG: + return -ENAMETOOLONG; + case NFS3ERR_STALE: + return -ESTALE; + case NFS3ERR_BADHANDLE: + case NFS3ERR_SERVERFAULT: + default: + return -EPROTO; + } + + nfs_iob_get_fh ( reply->data, &lookup_reply->fh ); + + if ( oncrpc_iob_get_int ( reply->data ) == 1 ) + lookup_reply->ent_type = oncrpc_iob_get_int ( reply->data ); + + return 0; +} +/** + * Parse a READLINK reply + * + * @v readlink_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int nfs_get_readlink_reply ( struct nfs_readlink_reply *readlink_reply, + struct oncrpc_reply *reply ) { + if ( ! readlink_reply || ! reply ) + return -EINVAL; + + readlink_reply->status = oncrpc_iob_get_int ( reply->data ); + switch ( readlink_reply->status ) + { + case NFS3_OK: + break; + case NFS3ERR_IO: + return -EIO; + case NFS3ERR_ACCES: + return -EACCES; + case NFS3ERR_INVAL: + return -EINVAL; + case NFS3ERR_NOTSUPP: + return -ENOTSUP; + case NFS3ERR_STALE: + return -ESTALE; + case NFS3ERR_BADHANDLE: + case NFS3ERR_SERVERFAULT: + default: + return -EPROTO; + } + + if ( oncrpc_iob_get_int ( reply->data ) == 1 ) + iob_pull ( reply->data, 5 * sizeof ( uint32_t ) + + 8 * sizeof ( uint64_t ) ); + + readlink_reply->path_len = oncrpc_iob_get_int ( reply->data ); + readlink_reply->path = reply->data->data; + + return 0; +} + +/** + * Parse a READ reply + * + * @v read_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int nfs_get_read_reply ( struct nfs_read_reply *read_reply, + struct oncrpc_reply *reply ) { + if ( ! read_reply || ! reply ) + return -EINVAL; + + read_reply->status = oncrpc_iob_get_int ( reply->data ); + switch ( read_reply->status ) + { + case NFS3_OK: + break; + case NFS3ERR_PERM: + return -EPERM; + case NFS3ERR_NOENT: + return -ENOENT; + case NFS3ERR_IO: + return -EIO; + case NFS3ERR_NXIO: + return -ENXIO; + case NFS3ERR_ACCES: + return -EACCES; + case NFS3ERR_INVAL: + return -EINVAL; + case NFS3ERR_STALE: + return -ESTALE; + case NFS3ERR_BADHANDLE: + case NFS3ERR_SERVERFAULT: + default: + return -EPROTO; + } + + if ( oncrpc_iob_get_int ( reply->data ) == 1 ) + { + iob_pull ( reply->data, 5 * sizeof ( uint32_t ) ); + read_reply->filesize = oncrpc_iob_get_int64 ( reply->data ); + iob_pull ( reply->data, 7 * sizeof ( uint64_t ) ); + } + + read_reply->count = oncrpc_iob_get_int ( reply->data ); + read_reply->eof = oncrpc_iob_get_int ( reply->data ); + read_reply->data_len = oncrpc_iob_get_int ( reply->data ); + read_reply->data = reply->data->data; + + if ( read_reply->count != read_reply->data_len ) + return -EPROTO; + + return 0; +} + 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 <ipxe@mareo.fr>. + * + * 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 <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <libgen.h> +#include <byteswap.h> +#include <ipxe/time.h> +#include <ipxe/socket.h> +#include <ipxe/tcpip.h> +#include <ipxe/in.h> +#include <ipxe/iobuf.h> +#include <ipxe/xfer.h> +#include <ipxe/open.h> +#include <ipxe/uri.h> +#include <ipxe/features.h> +#include <ipxe/nfs.h> +#include <ipxe/nfs_open.h> +#include <ipxe/oncrpc.h> +#include <ipxe/oncrpc_iob.h> +#include <ipxe/portmap.h> +#include <ipxe/mount.h> +#include <ipxe/nfs_uri.h> + +/** @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, +}; diff --git a/qemu/roms/ipxe/src/net/oncrpc/nfs_uri.c b/qemu/roms/ipxe/src/net/oncrpc/nfs_uri.c new file mode 100644 index 000000000..c4c3f21e9 --- /dev/null +++ b/qemu/roms/ipxe/src/net/oncrpc/nfs_uri.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2014 Marin Hannache <ipxe@mareo.fr>. + * + * 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 <stdlib.h> +#include <string.h> +#include <errno.h> +#include <libgen.h> +#include <ipxe/nfs_uri.h> + +/** @file + * + * Network File System protocol URI handling functions + * + */ + +int nfs_uri_init ( struct nfs_uri *nfs_uri, const struct uri *uri ) { + if ( ! ( nfs_uri->mountpoint = strdup ( uri->path ) ) ) + return -ENOMEM; + + nfs_uri->filename = basename ( nfs_uri->mountpoint ); + if ( strchr ( uri->path, '/' ) != NULL ) + nfs_uri->mountpoint = dirname ( nfs_uri->mountpoint ); + + if ( nfs_uri->filename[0] == '\0' ) { + free ( nfs_uri->mountpoint ); + return -EINVAL; + } + + if ( ! ( nfs_uri->path = strdup ( nfs_uri->filename ) ) ) { + free ( nfs_uri->mountpoint ); + return -ENOMEM; + } + nfs_uri->lookup_pos = nfs_uri->path; + + return 0; +} + +char *nfs_uri_mountpoint ( const struct nfs_uri *uri ) { + if ( uri->mountpoint + 1 == uri->filename || + uri->mountpoint == uri->filename ) + return "/"; + + return uri->mountpoint; +} + +int nfs_uri_next_mountpoint ( struct nfs_uri *uri ) { + char *sep; + + if ( uri->mountpoint + 1 == uri->filename || + uri->mountpoint == uri->filename ) + return -ENOENT; + + sep = strrchr ( uri->mountpoint, '/' ); + uri->filename[-1] = '/'; + uri->filename = sep + 1; + *sep = '\0'; + + free ( uri->path ); + if ( ! ( uri->path = strdup ( uri->filename ) ) ) { + uri->path = NULL; + return -ENOMEM; + } + uri->lookup_pos = uri->path; + + return 0; +} + +int nfs_uri_symlink ( struct nfs_uri *uri, const char *symlink ) { + size_t len; + char *new_path; + + if ( ! uri->path ) + return -EINVAL; + + if ( *symlink == '/' ) + { + if ( strncmp ( symlink, uri->mountpoint, + strlen ( uri->mountpoint ) ) != 0 ) + return -EINVAL; + + len = strlen ( uri->lookup_pos ) + strlen ( symlink ) - \ + strlen ( uri->mountpoint ); + if ( ! ( new_path = malloc ( len * sizeof ( char ) ) ) ) + return -ENOMEM; + + strcpy ( new_path, symlink + strlen ( uri->mountpoint ) ); + strcpy ( new_path + strlen ( new_path ), uri->lookup_pos ); + + } else { + len = strlen ( uri->lookup_pos ) + strlen ( symlink ); + if ( ! ( new_path = malloc ( len * sizeof ( char ) ) ) ) + return -ENOMEM; + + + strcpy ( new_path, symlink ); + strcpy ( new_path + strlen ( new_path ), uri->lookup_pos ); + } + + free ( uri->path ); + uri->lookup_pos = uri->path = new_path; + + return 0; +} + +char *nfs_uri_next_path_component ( struct nfs_uri *uri ) { + char *sep; + char *start; + + if ( ! uri->path ) + return NULL; + + for ( sep = uri->lookup_pos ; *sep != '\0' && *sep != '/'; sep++ ) + ; + + start = uri->lookup_pos; + uri->lookup_pos = sep; + if ( *sep != '\0' ) { + uri->lookup_pos++; + *sep = '\0'; + if ( *start == '\0' ) + return nfs_uri_next_path_component ( uri ); + } + + return start; +} + +void nfs_uri_free ( struct nfs_uri *uri ) { + free ( uri->mountpoint ); + free ( uri->path ); + uri->mountpoint = NULL; + uri->path = NULL; +} diff --git a/qemu/roms/ipxe/src/net/oncrpc/oncrpc_iob.c b/qemu/roms/ipxe/src/net/oncrpc/oncrpc_iob.c new file mode 100644 index 000000000..be51805e7 --- /dev/null +++ b/qemu/roms/ipxe/src/net/oncrpc/oncrpc_iob.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>. + * + * 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 <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/socket.h> +#include <ipxe/tcpip.h> +#include <ipxe/in.h> +#include <ipxe/iobuf.h> +#include <ipxe/xfer.h> +#include <ipxe/open.h> +#include <ipxe/uri.h> +#include <ipxe/features.h> +#include <ipxe/oncrpc.h> +#include <ipxe/oncrpc_iob.h> + +/** @file + * + * SUN ONC RPC protocol + * + */ + +size_t oncrpc_iob_add_fields ( struct io_buffer *io_buf, + const struct oncrpc_field fields[] ) { + size_t i; + size_t s = 0; + + struct oncrpc_field f; + + if ( ! io_buf ) + return 0; + + for ( i = 0; fields[i].type != oncrpc_none; i++ ) { + f = fields[i]; + switch ( f.type ) { + case oncrpc_int32: + s += oncrpc_iob_add_int ( io_buf, f.value.int32 ); + break; + + case oncrpc_int64: + s += oncrpc_iob_add_int64 ( io_buf, f.value.int64 ); + break; + + case oncrpc_str: + s += oncrpc_iob_add_string ( io_buf, f.value.str ); + break; + + case oncrpc_array: + s += oncrpc_iob_add_array ( io_buf, + f.value.array.length, + f.value.array.ptr ); + break; + + case oncrpc_intarray: + s += oncrpc_iob_add_intarray ( io_buf, + f.value.intarray.length, + f.value.intarray.ptr ); + break; + + case oncrpc_cred: + s += oncrpc_iob_add_cred ( io_buf, f.value.cred); + break; + + default: + return s; + } + } + + return s; +} + +/** + * Add an array of bytes to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v val String + * @ret size Size of the data written + * + * In the ONC RPC protocol, every data is four byte paded, we add padding when + * necessary by using oncrpc_align() + */ +size_t oncrpc_iob_add_array ( struct io_buffer *io_buf, size_t length, + const void *data ) { + size_t padding = oncrpc_align ( length ) - length; + + oncrpc_iob_add_int ( io_buf, length ); + memcpy ( iob_put ( io_buf, length ), data, length ); + memset ( iob_put ( io_buf, padding ), 0, padding ); + + return length + padding + sizeof ( uint32_t ); +} + +/** + * Add an int array to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v length Length od the array + * @v val Int array + * @ret size Size of the data written + */ +size_t oncrpc_iob_add_intarray ( struct io_buffer *io_buf, size_t length, + const uint32_t *array ) { + size_t i; + + oncrpc_iob_add_int ( io_buf, length ); + + for ( i = 0; i < length; ++i ) + oncrpc_iob_add_int ( io_buf, array[i] ); + + return ( ( length + 1 ) * sizeof ( uint32_t ) ); +} + +/** + * Add credential information to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v cred Credential information + * @ret size Size of the data written + */ +size_t oncrpc_iob_add_cred ( struct io_buffer *io_buf, + const struct oncrpc_cred *cred ) { + struct oncrpc_cred_sys *syscred; + size_t s; + + struct oncrpc_field credfields[] = { + ONCRPC_FIELD ( int32, cred->flavor ), + ONCRPC_FIELD ( int32, cred->length ), + ONCRPC_FIELD_END, + }; + + if ( ! io_buf || ! cred ) + return 0; + + s = oncrpc_iob_add_fields ( io_buf, credfields); + + switch ( cred->flavor ) { + case ONCRPC_AUTH_NONE: + break; + + case ONCRPC_AUTH_SYS: + syscred = container_of ( cred, struct oncrpc_cred_sys, + credential ); + + struct oncrpc_field syscredfields[] = { + ONCRPC_FIELD ( int32, syscred->stamp ), + ONCRPC_FIELD ( str, syscred->hostname ), + ONCRPC_FIELD ( int32, syscred->uid ), + ONCRPC_FIELD ( int32, syscred->gid ), + ONCRPC_SUBFIELD ( intarray, syscred->aux_gid_len, + syscred->aux_gid ), + ONCRPC_FIELD_END, + }; + + s += oncrpc_iob_add_fields ( io_buf, syscredfields ); + break; + } + + return s; +} + +/** + * Get credential information from the beginning of an I/O buffer + * + * @v io_buf I/O buffer + * @v cred Struct where the information will be saved + * @ret size Size of the data read + */ +size_t oncrpc_iob_get_cred ( struct io_buffer *io_buf, + struct oncrpc_cred *cred ) { + if ( cred == NULL ) + return * ( uint32_t * ) io_buf->data; + + cred->flavor = oncrpc_iob_get_int ( io_buf ); + cred->length = oncrpc_iob_get_int ( io_buf ); + + iob_pull ( io_buf, cred->length ); + + return ( 2 * sizeof ( uint32_t ) + cred->length ); +} diff --git a/qemu/roms/ipxe/src/net/oncrpc/portmap.c b/qemu/roms/ipxe/src/net/oncrpc/portmap.c new file mode 100644 index 000000000..df62221dc --- /dev/null +++ b/qemu/roms/ipxe/src/net/oncrpc/portmap.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>. + * + * 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 <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/socket.h> +#include <ipxe/tcpip.h> +#include <ipxe/in.h> +#include <ipxe/iobuf.h> +#include <ipxe/xfer.h> +#include <ipxe/open.h> +#include <ipxe/uri.h> +#include <ipxe/features.h> +#include <ipxe/timer.h> +#include <ipxe/oncrpc.h> +#include <ipxe/oncrpc_iob.h> +#include <ipxe/portmap.h> + +/** @file + * + * PORTMAPPER protocol. + * + */ + +/** PORTMAP GETPORT procedure. */ +#define PORTMAP_GETPORT 3 + +/** + * Send a GETPORT request + * + * @v intf Interface to send the request on + * @v session ONC RPC session + * @v prog ONC RPC program number + * @v vers ONC RPC rogram version number + * @v proto Protocol (TCP or UDP) + * @ret rc Return status code + */ +int portmap_getport ( struct interface *intf, struct oncrpc_session *session, + uint32_t prog, uint32_t vers, uint32_t proto ) { + struct oncrpc_field fields[] = { + ONCRPC_FIELD ( int32, prog ), + ONCRPC_FIELD ( int32, vers ), + ONCRPC_FIELD ( int32, proto ), + ONCRPC_FIELD ( int32, 0 ), /* The port field is only meaningful + in GETPORT reply */ + ONCRPC_FIELD_END, + }; + + return oncrpc_call ( intf, session, PORTMAP_GETPORT, fields ); +} + +/** + * Parse a GETPORT reply + * + * @v getport_reply A structure where the data will be saved + * @v reply The ONC RPC reply to get data from + * @ret rc Return status code + */ +int portmap_get_getport_reply ( struct portmap_getport_reply *getport_reply, + struct oncrpc_reply *reply ) { + if ( ! getport_reply || ! reply ) + return -EINVAL; + + getport_reply->port = oncrpc_iob_get_int ( reply->data ); + if ( getport_reply == 0 || getport_reply->port >= 65536 ) + return -EINVAL; + + return 0; +} |