diff options
author | Marin Hannache <git@mareo.fr> | 2013-07-15 17:56:29 +0200 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2013-07-15 17:56:29 +0200 |
commit | 30de9e8300e97c4b56e29f5a3b08e4615b30606f (patch) | |
tree | 48d66fac04d1a3eb129e878c2696f9afef2f6e8f | |
parent | ed28c8304c1653a340088137966fff56bae15ad0 (diff) | |
download | ipxe-30de9e8300e97c4b56e29f5a3b08e4615b30606f.tar.gz |
[nfs] Add support for NFS protocol
Tested-by: Robin Smidsrød <robin@smidsrod.no>
Signed-off-by: Marin Hannache <git@mareo.fr>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r-- | src/Makefile | 2 | ||||
-rw-r--r-- | src/config/config.c | 3 | ||||
-rw-r--r-- | src/config/general.h | 1 | ||||
-rw-r--r-- | src/include/big_bswap.h | 2 | ||||
-rw-r--r-- | src/include/ipxe/dhcp.h | 12 | ||||
-rw-r--r-- | src/include/ipxe/errfile.h | 6 | ||||
-rw-r--r-- | src/include/ipxe/features.h | 1 | ||||
-rw-r--r-- | src/include/ipxe/mount.h | 76 | ||||
-rw-r--r-- | src/include/ipxe/nfs.h | 157 | ||||
-rw-r--r-- | src/include/ipxe/nfs_open.h | 12 | ||||
-rw-r--r-- | src/include/ipxe/oncrpc.h | 128 | ||||
-rw-r--r-- | src/include/ipxe/oncrpc_iob.h | 102 | ||||
-rw-r--r-- | src/include/ipxe/portmap.h | 63 | ||||
-rw-r--r-- | src/include/ipxe/uri.h | 18 | ||||
-rw-r--r-- | src/include/little_bswap.h | 2 | ||||
-rw-r--r-- | src/net/oncrpc/mount.c | 119 | ||||
-rw-r--r-- | src/net/oncrpc/nfs.c | 288 | ||||
-rw-r--r-- | src/net/oncrpc/nfs_open.c | 711 | ||||
-rw-r--r-- | src/net/oncrpc/oncrpc_iob.c | 200 | ||||
-rw-r--r-- | src/net/oncrpc/portmap.c | 90 | ||||
-rw-r--r-- | src/net/tcp/oncrpc.c | 250 |
21 files changed, 2238 insertions, 5 deletions
diff --git a/src/Makefile b/src/Makefile index 792fad99d..acd45fcf3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -61,7 +61,7 @@ ZLIB_DIR := /usr SRCDIRS := SRCDIRS += libgcc SRCDIRS += core -SRCDIRS += net net/tcp net/udp net/infiniband net/80211 +SRCDIRS += net net/oncrpc net/tcp net/udp net/infiniband net/80211 SRCDIRS += image SRCDIRS += drivers/bus SRCDIRS += drivers/net diff --git a/src/config/config.c b/src/config/config.c index 45dc29bcf..b17b335b7 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -129,6 +129,9 @@ REQUIRE_OBJECT ( https ); #ifdef DOWNLOAD_PROTO_FTP REQUIRE_OBJECT ( ftp ); #endif +#ifdef DOWNLOAD_PROTO_NFS +REQUIRE_OBJECT ( nfs_open ); +#endif #ifdef DOWNLOAD_PROTO_SLAM REQUIRE_OBJECT ( slam ); #endif diff --git a/src/config/general.h b/src/config/general.h index 62edd3fe8..3fd7408dd 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -59,6 +59,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #undef DOWNLOAD_PROTO_HTTPS /* Secure Hypertext Transfer Protocol */ #undef DOWNLOAD_PROTO_FTP /* File Transfer Protocol */ #undef DOWNLOAD_PROTO_SLAM /* Scalable Local Area Multicast */ +#undef DOWNLOAD_PROTO_NFS /* Network File System Protocol */ /* * SAN boot protocols diff --git a/src/include/big_bswap.h b/src/include/big_bswap.h index 3775fac10..6c375a573 100644 --- a/src/include/big_bswap.h +++ b/src/include/big_bswap.h @@ -1,6 +1,8 @@ #ifndef ETHERBOOT_BIG_BSWAP_H #define ETHERBOOT_BIG_BSWAP_H +#define htonll(x) (x) +#define ntohll(x) (x) #define ntohl(x) (x) #define htonl(x) (x) #define ntohs(x) (x) diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index b97dfe32c..6c028466c 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -450,6 +450,18 @@ struct dhcp_netdev_desc { */ #define DHCP_EB_REVERSE_PASSWORD DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc1 ) +/** User ID + * + * This will be used as the user id for AUTH_SYS based authentication in NFS. + */ +#define DHCP_EB_UID DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc2 ) + +/** Group ID + * + * This will be used as the group id for AUTH_SYS based authentication in NFS. + */ +#define DHCP_EB_GID DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xc3 ) + /** iPXE version number */ #define DHCP_EB_VERSION DHCP_ENCAP_OPT ( DHCP_EB_ENCAP, 0xeb ) diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index aad3f358b..0fd3facaa 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -208,6 +208,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_fcoe ( ERRFILE_NET | 0x002e0000 ) #define ERRFILE_fcns ( ERRFILE_NET | 0x002f0000 ) #define ERRFILE_vlan ( ERRFILE_NET | 0x00300000 ) +#define ERRFILE_oncrpc ( ERRFILE_NET | 0x00310000 ) +#define ERRFILE_portmap ( ERRFILE_NET | 0x00320000 ) +#define ERRFILE_nfs ( ERRFILE_NET | 0x00330000 ) +#define ERRFILE_nfs_open ( ERRFILE_NET | 0x00340000 ) +#define ERRFILE_mount ( ERRFILE_NET | 0x00350000 ) +#define ERRFILE_oncrpc_iob ( ERRFILE_NET | 0x00360000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/include/ipxe/features.h b/src/include/ipxe/features.h index 0c92f5be9..d8b8b2184 100644 --- a/src/include/ipxe/features.h +++ b/src/include/ipxe/features.h @@ -54,6 +54,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define DHCP_EB_FEATURE_VLAN 0x26 /**< VLAN support */ #define DHCP_EB_FEATURE_MENU 0x27 /**< Menu support */ #define DHCP_EB_FEATURE_SDI 0x28 /**< SDI image support */ +#define DHCP_EB_FEATURE_NFS 0x29 /**< NFS protocol */ /** @} */ diff --git a/src/include/ipxe/mount.h b/src/include/ipxe/mount.h new file mode 100644 index 000000000..ca958117a --- /dev/null +++ b/src/include/ipxe/mount.h @@ -0,0 +1,76 @@ +#ifndef _IPXE_MOUNT_H +#define _IPXE_MOUNT_H + +#include <ipxe/nfs.h> + +/** @file + * + * NFS MOUNT protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** NFS MOUNT protocol number */ +#define ONCRPC_MOUNT 100005 +/** NFS MOUNT protocol version */ +#define MOUNT_VERS 3 + + +/** No error */ +#define MNT3_OK 0 +/** Not owner */ +#define MNT3ERR_PERM 1 +/** No such file or directory */ +#define MNT3ERR_NOENT 2 +/** I/O error */ +#define MNT3ERR_IO 5 +/** Permission denied */ +#define MNT3ERR_ACCES 13 +/** Not a directory */ +#define MNT3ERR_NOTDIR 20 +/** Invalid argument */ +#define MNT3ERR_INVAL 22 +/** Filename too long */ +#define MNT3ERR_NAMETOOLONG 63 +/** Operation not supported */ +#define MNT3ERR_NOTSUPP 10004 +/** A failure on the server */ +#define MNT3ERR_SERVERFAULT 10006 + + +/** + * A MOUNT MNT reply + * + */ +struct mount_mnt_reply { + /** Reply status */ + uint32_t status; + /** Root file handle */ + struct nfs_fh fh; +}; + +/** + * Prepare an ONC RPC session to be used as a MOUNT session + * + * @v session ONC RPC session + * @v credential ONC RPC credential + * + * The credential parameter must not be NULL, use 'oncrpc_auth_none' if you + * don't want a particular scheme to be used. + */ +static inline void mount_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential ) { + oncrpc_init_session ( session, credential, &oncrpc_auth_none, + ONCRPC_MOUNT, MOUNT_VERS ); +} + +int mount_mnt ( struct interface *intf, struct oncrpc_session *session, + const char *mountpoint ); +int mount_umnt ( struct interface *intf, struct oncrpc_session *session, + const char *mountpoint ); + +int mount_get_mnt_reply ( struct mount_mnt_reply *mnt_reply, + struct oncrpc_reply *reply ); + +#endif /* _IPXE_MOUNT_H */ diff --git a/src/include/ipxe/nfs.h b/src/include/ipxe/nfs.h new file mode 100644 index 000000000..498ed5a27 --- /dev/null +++ b/src/include/ipxe/nfs.h @@ -0,0 +1,157 @@ +#ifndef _IPXE_NFS_H +#define _IPXE_NFS_H + +#include <stdint.h> +#include <ipxe/oncrpc.h> + +/** @file + * + * Network File System protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** NFS protocol number */ +#define ONCRPC_NFS 100003 + +/** NFS protocol version */ +#define NFS_VERS 3 + +/** No error*/ +#define NFS3_OK 0 +/** Not owner */ +#define NFS3ERR_PERM 1 +/** No such file or directory */ +#define NFS3ERR_NOENT 2 +/** I/O error */ +#define NFS3ERR_IO 5 +/** No such device or address */ +#define NFS3ERR_NXIO 6 +/** Permission denied */ +#define NFS3ERR_ACCES 13 +/** The file specified already exists */ +#define NFS3ERR_EXIST 17 +/** Attempt to do a cross-device hard link */ +#define NFS3ERR_XDEV 18 +/** No such device */ +#define NFS3ERR_NODEV 19 +/** Not a directory */ +#define NFS3ERR_NOTDIR 20 + /**Is a directory */ +#define NFS3ERR_ISDIR 21 +/** Invalid argument */ +#define NFS3ERR_INVAL 22 +/** Filename too long */ +#define NFS3ERR_NAMETOOLONG 63 +/** Invalid file handle */ +#define NFS3ERR_STALE 70 +/** Too many levels of remote in path */ +#define NFS3ERR_REMOTE 71 +/** Illegal NFS file handle */ +#define NFS3ERR_BADHANDLE 10001 +/** READDIR or READDIRPLUS cookie is stale */ +#define NFS3ERR_BAD_COOKIE 10003 +/** Operation not supported */ +#define NFS3ERR_NOTSUPP 10004 +/** Buffer or request is too small */ +#define NFS3ERR_TOOSMALL 10005 +/** An error occurred on the server which does not map to any of the legal NFS + * version 3 protocol error values */ +#define NFS3ERR_SERVERFAULT 10006 +/** The server initiated the request, but was not able to complete it in a + * timely fashion */ +#define NFS3ERR_JUKEBOX 10008 + +enum nfs_attr_type { + NFS_ATTR_SYMLINK = 5, +}; + +/** + * A NFS file handle + * + */ +struct nfs_fh { + uint8_t fh[64]; + size_t size; +}; + +/** + * A NFS LOOKUP reply + * + */ +struct nfs_lookup_reply { + /** Reply status */ + uint32_t status; + /** Entity type */ + enum nfs_attr_type ent_type; + /** File handle */ + struct nfs_fh fh; +}; + +/** + * A NFS READLINK reply + * + */ +struct nfs_readlink_reply { + /** Reply status */ + uint32_t status; + /** File path length */ + uint32_t path_len; + /** File path */ + char *path; +}; + + +/** + * A NFS READ reply + * + */ +struct nfs_read_reply { + /** Reply status */ + uint32_t status; + /** File size */ + uint64_t filesize; + /** Bytes read */ + uint32_t count; + /** End-of-File indicator */ + uint32_t eof; + /** Data length */ + uint32_t data_len; + /** Data read */ + void *data; +}; + +size_t nfs_iob_get_fh ( struct io_buffer *io_buf, struct nfs_fh *fh ); +size_t nfs_iob_add_fh ( struct io_buffer *io_buf, const struct nfs_fh *fh ); + +/** + * Prepare an ONC RPC session to be used as a NFS session + * + * @v session ONC RPC session + * @v credential ONC RPC credential + * + * The credential parameter must not be NULL, use 'oncrpc_auth_none' if you + * don't want a particular scheme to be used. + */ +static inline void nfs_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential ) { + oncrpc_init_session ( session, credential, &oncrpc_auth_none, + ONCRPC_NFS, NFS_VERS ); +} + +int nfs_lookup ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh, const char *filename ); +int nfs_readlink ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh ); +int nfs_read ( struct interface *intf, struct oncrpc_session *session, + const struct nfs_fh *fh, uint64_t offset, uint32_t count ); + +int nfs_get_lookup_reply ( struct nfs_lookup_reply *lookup_reply, + struct oncrpc_reply *reply ); +int nfs_get_readlink_reply ( struct nfs_readlink_reply *readlink_reply, + struct oncrpc_reply *reply ); +int nfs_get_read_reply ( struct nfs_read_reply *read_reply, + struct oncrpc_reply *reply ); + +#endif /* _IPXE_NFS_H */ diff --git a/src/include/ipxe/nfs_open.h b/src/include/ipxe/nfs_open.h new file mode 100644 index 000000000..caba977f7 --- /dev/null +++ b/src/include/ipxe/nfs_open.h @@ -0,0 +1,12 @@ +#ifndef _IPXE_NFS_OPEN_H +#define _IPXE_NFS_OPEN_H + +/** @file + * + * Network File System protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#endif /* _IPXE_NFS_OPEN_H */ diff --git a/src/include/ipxe/oncrpc.h b/src/include/ipxe/oncrpc.h new file mode 100644 index 000000000..76c1260f2 --- /dev/null +++ b/src/include/ipxe/oncrpc.h @@ -0,0 +1,128 @@ +#ifndef _IPXE_ONCRPC_H +#define _IPXE_ONCRPC_H + +#include <stdint.h> +#include <ipxe/interface.h> +#include <ipxe/iobuf.h> + +/** @file + * + * SUN ONC RPC protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** ONC RCP Version */ +#define ONCRPC_VERS 2 + +/** ONC RPC Null Authentication */ +#define ONCRPC_AUTH_NONE 0 + +/** ONC RPC System Authentication (also called UNIX Authentication) */ +#define ONCRPC_AUTH_SYS 1 + +/** Size of an ONC RPC header */ +#define ONCRPC_HEADER_SIZE ( 11 * sizeof ( uint32_t ) ) + +#define ONCRPC_FIELD( type, value ) { oncrpc_ ## type, { .type = value } } +#define ONCRPC_SUBFIELD( type, args... ) \ + { oncrpc_ ## type, { .type = { args } } } + +#define ONCRPC_FIELD_END { oncrpc_none, { } } + +/** Enusure that size is a multiple of four */ +#define oncrpc_align( size ) ( ( (size) + 3 ) & ~3 ) + +/** + * Calculate the length of a string, including padding bytes. + * + * @v str String + * @ret size Length of the padded string + */ +#define oncrpc_strlen( str ) ( oncrpc_align ( strlen ( str ) ) + \ + sizeof ( uint32_t ) ) + +struct oncrpc_cred { + uint32_t flavor; + uint32_t length; +}; + +struct oncrpc_cred_sys { + struct oncrpc_cred credential; + uint32_t stamp; + char *hostname; + uint32_t uid; + uint32_t gid; + uint32_t aux_gid_len; + uint32_t aux_gid[16]; +}; + +struct oncrpc_reply +{ + struct oncrpc_cred *verifier; + uint32_t rpc_id; + uint32_t reply_state; + uint32_t accept_state; + uint32_t frame_size; + struct io_buffer *data; +}; + +struct oncrpc_session { + struct oncrpc_reply pending_reply; + struct oncrpc_cred *credential; + struct oncrpc_cred *verifier; + uint32_t rpc_id; + uint32_t prog_name; + uint32_t prog_vers; +}; + +enum oncrpc_field_type { + oncrpc_none = 0, + oncrpc_int32, + oncrpc_int64, + oncrpc_str, + oncrpc_array, + oncrpc_intarray, + oncrpc_cred, +}; + +union oncrpc_field_value { + struct { + size_t length; + const void *ptr; + } array; + + struct { + size_t length; + const uint32_t *ptr; + } intarray; + + int64_t int64; + int32_t int32; + const char *str; + const struct oncrpc_cred *cred; +}; + +struct oncrpc_field { + enum oncrpc_field_type type; + union oncrpc_field_value value; +}; + +extern struct oncrpc_cred oncrpc_auth_none; + +int oncrpc_init_cred_sys ( struct oncrpc_cred_sys *auth_sys ); +void oncrpc_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential, + struct oncrpc_cred *verifier, uint32_t prog_name, + uint32_t prog_vers ); + +int oncrpc_call ( struct interface *intf, struct oncrpc_session *session, + uint32_t proc_name, const struct oncrpc_field fields[] ); + +size_t oncrpc_compute_size ( const struct oncrpc_field fields[] ); + +int oncrpc_get_reply ( struct oncrpc_session *session, + struct oncrpc_reply *reply, struct io_buffer *io_buf ); + +#endif /* _IPXE_ONCRPC_H */ diff --git a/src/include/ipxe/oncrpc_iob.h b/src/include/ipxe/oncrpc_iob.h new file mode 100644 index 000000000..4858d96b5 --- /dev/null +++ b/src/include/ipxe/oncrpc_iob.h @@ -0,0 +1,102 @@ +#ifndef _IPXE_ONCRPC_IOB_H +#define _IPXE_ONCRPC_IOB_H + +#include <stdint.h> +#include <string.h> +#include <ipxe/iobuf.h> +#include <ipxe/refcnt.h> +#include <ipxe/oncrpc.h> + +/** @file + * + * SUN ONC RPC protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** + * Add a string to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v val String + * @ret size Size of the data written + */ +#define oncrpc_iob_add_string( buf, str ) \ +( { \ + const char * _str = (str); \ + oncrpc_iob_add_array ( (buf), strlen ( _str ), _str ); \ +} ) + +/** + * Get a 32 bits integer from the beginning of an I/O buffer + * + * @v buf I/O buffer + * @ret int Integer + */ + +#define oncrpc_iob_get_int( buf ) \ +( { \ + uint32_t *_val; \ + _val = (buf)->data; \ + iob_pull ( (buf), sizeof ( uint32_t ) ); \ + ntohl ( *_val ); \ +} ) + +/** + * Get a 64 bits integer from the beginning of an I/O buffer + * + * @v buf I/O buffer + * @ret int Integer + */ +#define oncrpc_iob_get_int64( buf ) \ +( { \ + uint64_t *_val; \ + _val = (buf)->data; \ + iob_pull ( (buf), sizeof ( uint64_t ) ); \ + ntohll ( *_val ); \ +} ) + + +size_t oncrpc_iob_add_fields ( struct io_buffer *io_buf, + const struct oncrpc_field fields[] ); + +size_t oncrpc_iob_add_array ( struct io_buffer *io_buf, size_t length, + const void *data ); + +size_t oncrpc_iob_add_intarray ( struct io_buffer *io_buf, size_t length, + const uint32_t *array ); + +size_t oncrpc_iob_add_cred ( struct io_buffer *io_buf, + const struct oncrpc_cred *cred ); + +size_t oncrpc_iob_get_cred ( struct io_buffer *io_buf, + struct oncrpc_cred *cred ); + +/** + * Add a 32 bits integer to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v val Integer + * @ret size Size of the data written + */ +static inline size_t oncrpc_iob_add_int ( struct io_buffer *io_buf, + uint32_t val ) { + * ( uint32_t * ) iob_put ( io_buf, sizeof ( val ) ) = htonl ( val ); + return ( sizeof ( val) ); +} + +/** + * Add a 64 bits integer to the end of an I/O buffer + * + * @v io_buf I/O buffer + * @v val Integer + * @ret size Size of the data written + */ +static inline size_t oncrpc_iob_add_int64 ( struct io_buffer *io_buf, + uint64_t val ) { + * ( uint64_t * ) iob_put ( io_buf, sizeof ( val ) ) = htonll ( val ); + return ( sizeof ( val) ); +} + +#endif /* _IPXE_ONCRPC_IOB_H */ diff --git a/src/include/ipxe/portmap.h b/src/include/ipxe/portmap.h new file mode 100644 index 000000000..9b735bbca --- /dev/null +++ b/src/include/ipxe/portmap.h @@ -0,0 +1,63 @@ +#ifndef _IPXE_PORTMAP_H +#define _IPXE_PORTMAP_H + +#include <stdint.h> +#include <ipxe/oncrpc.h> + +/** @file + * + * SUN ONC RPC protocol. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +/** PORTMAP default port */ +#define PORTMAP_PORT 111 + +/** PORTMAP protocol number */ +#define ONCRPC_PORTMAP 100000 + +/** PORTMAP version */ +#define PORTMAP_VERS 2 + + +/** TCP protocol number */ +#define PORTMAP_PROTO_TCP 6 +/** UDB protocol number */ +#define PORTMAP_PROTO_UDP 17 + + +/** + * A PORTMAP GETPORT reply + * + */ +struct portmap_getport_reply { + /** Port returned */ + uint32_t port; +}; + + +/** + * Prepare an ONC RPC session to be used as a PORTMAP session + * + * @v session ONC RPC session + * @v credential ONC RPC credential + * + * The credential parameter must not be NULL, use 'oncrpc_auth_none' if you + * don't want a particular scheme to be used. + */ +static inline void portmap_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential) { + oncrpc_init_session ( session, credential, &oncrpc_auth_none, + ONCRPC_PORTMAP, PORTMAP_VERS ); +} + + +int portmap_getport ( struct interface *intf, struct oncrpc_session *session, + uint32_t prog, uint32_t vers, uint32_t proto ); +int portmap_get_getport_reply ( struct portmap_getport_reply *getport_reply, + struct oncrpc_reply *reply ); + + +#endif /* _IPXE_PORTMAP_H */ diff --git a/src/include/ipxe/uri.h b/src/include/ipxe/uri.h index b7b8b4417..9a134690f 100644 --- a/src/include/ipxe/uri.h +++ b/src/include/ipxe/uri.h @@ -109,17 +109,27 @@ enum { * Note that this is a separate concept from a URI with an absolute * path. */ -static inline int uri_is_absolute ( struct uri *uri ) { +static inline int uri_is_absolute ( const struct uri *uri ) { return ( uri->scheme != NULL ); } /** + * URI has an opaque part + * + * @v uri URI + * @ret has_opaque URI has an opaque part + */ +static inline int uri_has_opaque ( const struct uri *uri ) { + return ( uri->opaque && ( uri->opaque[0] != '\0' ) ); + +} +/** * URI has a path * * @v uri URI * @ret has_path URI has a path */ -static inline int uri_has_path ( struct uri *uri ) { +static inline int uri_has_path ( const struct uri *uri ) { return ( uri->path && ( uri->path[0] != '\0' ) ); } @@ -133,7 +143,7 @@ static inline int uri_has_path ( struct uri *uri ) { * concept from an absolute URI. Note also that a URI may not have a * path at all. */ -static inline int uri_has_absolute_path ( struct uri *uri ) { +static inline int uri_has_absolute_path ( const struct uri *uri ) { return ( uri->path && ( uri->path[0] == '/' ) ); } @@ -147,7 +157,7 @@ static inline int uri_has_absolute_path ( struct uri *uri ) { * this is a separate concept from a relative URI. Note also that a * URI may not have a path at all. */ -static inline int uri_has_relative_path ( struct uri *uri ) { +static inline int uri_has_relative_path ( const struct uri *uri ) { return ( uri->path && ( uri->path[0] != '/' ) ); } diff --git a/src/include/little_bswap.h b/src/include/little_bswap.h index a5dc9c875..92dd26ba1 100644 --- a/src/include/little_bswap.h +++ b/src/include/little_bswap.h @@ -3,6 +3,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#define htonll(x) __bswap_64(x) +#define ntohll(x) __bswap_64(x) #define ntohl(x) __bswap_32(x) #define htonl(x) __bswap_32(x) #define ntohs(x) __bswap_16(x) diff --git a/src/net/oncrpc/mount.c b/src/net/oncrpc/mount.c new file mode 100644 index 000000000..8838a147c --- /dev/null +++ b/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/src/net/oncrpc/nfs.c b/src/net/oncrpc/nfs.c new file mode 100644 index 000000000..b6118f91a --- /dev/null +++ b/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/src/net/oncrpc/nfs_open.c b/src/net/oncrpc/nfs_open.c new file mode 100644 index 000000000..ff2b7404a --- /dev/null +++ b/src/net/oncrpc/nfs_open.c @@ -0,0 +1,711 @@ +/* + * 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> + +/** @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; + char * path; + char * mountpoint; + char * filename; + size_t filename_offset; + + 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 ); + + free ( nfs->hostname ); + free ( nfs->path ); + 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 ( &peer, 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_port = htons ( 1 + ( rand() % 1023 ) ); + + 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->mountpoint ); + + rc = mount_mnt ( &nfs->mount_intf, &nfs->mount_session, + nfs->mountpoint ); + 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->mountpoint ); + 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; + char *sep; + 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 ) { + if ( mnt_reply.status != MNT3ERR_NOTDIR ) + goto err; + + if ( strcmp ( nfs->mountpoint, "/" ) == 0 ) + goto err; + + sep = strrchr ( nfs->mountpoint, '/' ); + nfs->filename[-1] = '/'; + nfs->filename = sep + 1; + *sep = '\0'; + + DBGC ( nfs, "NFS_OPEN %p ENOTDIR received retrying" \ + "with %s\n", nfs, nfs->mountpoint ); + 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 = NULL; + + if ( ! xfer_window ( &nfs->nfs_intf ) ) + return; + + if ( nfs->nfs_state == NFS_LOOKUP ) { + while ( path_component == NULL || path_component[0] == '\0') { + path_component = nfs->filename; + while ( *nfs->filename != '\0' ) { + nfs->filename_offset++; + if ( *nfs->filename++ == '/' ) { + *(nfs->filename - 1) = '\0'; + break; + } + } + } + + 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->filename[0] == '\0' ) + nfs->nfs_state = NFS_READ; + else + nfs->nfs_state--; + } + + nfs_step ( nfs ); + goto done; + } + + if ( nfs->nfs_state == NFS_READLINK_SENT ) { + char *new_filename; + 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 ) + return -EINVAL; + + if ( readlink_reply.path[0] == '/' ) { + if ( strncmp ( readlink_reply.path, nfs->mountpoint, + strlen ( nfs->mountpoint ) ) != 0 ) + return -EINVAL; + + /* The mountpoint part of the path is ended by a '/' */ + if ( strlen ( nfs->mountpoint ) != + readlink_reply.path_len ) { + readlink_reply.path += 1; + readlink_reply.path_len -= 1; + } + + /* We are considering the last part of the absolute + * path as a relative path from the mountpoint. + */ + readlink_reply.path += strlen ( nfs->mountpoint ); + readlink_reply.path_len -= strlen ( nfs->mountpoint ); + } + + new_filename = malloc ( readlink_reply.path_len + + strlen ( nfs->filename ) + 2 ); + if ( ! new_filename ) { + rc = -ENOMEM; + goto err; + } + + memcpy ( new_filename, readlink_reply.path, + readlink_reply.path_len ); + strcpy ( new_filename + readlink_reply.path_len + 1, + nfs->filename ); + new_filename[readlink_reply.path_len] = '/'; + + free ( nfs->filename - nfs->filename_offset ); + nfs->filename = new_filename; + nfs->filename_offset = 0; + + DBGC ( nfs, "NFS_OPEN %p new filename: %s\n", nfs, + nfs->filename ); + + 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 ( ! ( nfs->path = strdup ( uri->path ) ) ) + return -ENOMEM; + + if ( ! ( nfs->hostname = strdup ( uri->host ) ) ) { + rc = -ENOMEM; + goto err_hostname; + } + + nfs->filename = basename ( nfs->path ); + nfs->mountpoint = dirname ( nfs->path ); + + if ( nfs->filename[0] == '\0' ) + goto err_filename; + + DBGC ( nfs, "NFS_OPEN %p URI parsed: (mountpoint=%s, filename=%s)\n", + nfs, nfs->mountpoint, nfs->filename ); + + return 0; + +err_filename: + rc = -EINVAL; + free ( nfs->hostname ); +err_hostname: + free ( nfs->path ); + 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 ); + + 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: +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/src/net/oncrpc/oncrpc_iob.c b/src/net/oncrpc/oncrpc_iob.c new file mode 100644 index 000000000..be51805e7 --- /dev/null +++ b/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/src/net/oncrpc/portmap.c b/src/net/oncrpc/portmap.c new file mode 100644 index 000000000..df62221dc --- /dev/null +++ b/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; +} diff --git a/src/net/tcp/oncrpc.c b/src/net/tcp/oncrpc.c new file mode 100644 index 000000000..819d31794 --- /dev/null +++ b/src/net/tcp/oncrpc.c @@ -0,0 +1,250 @@ +/* + * 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/dhcp.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> +#include <ipxe/init.h> +#include <ipxe/settings.h> +#include <config/general.h> + +/** @file + * + * SUN ONC RPC protocol + * + */ + +/** Set most significant bit to 1. */ +#define SET_LAST_FRAME( x ) ( (x) | 1 << 31 ) +#define GET_FRAME_SIZE( x ) ( (x) & ~( 1 << 31 ) ) + +#define ONCRPC_CALL 0 +#define ONCRPC_REPLY 1 + +/** AUTH NONE authentication flavor */ +struct oncrpc_cred oncrpc_auth_none = { + .flavor = ONCRPC_AUTH_NONE, + .length = 0 +}; + +struct setting uid_setting __setting ( SETTING_AUTH ) = { + .name = "uid", + .description = "User ID", + .tag = DHCP_EB_UID, + .type = &setting_type_uint32 +}; + +struct setting gid_setting __setting ( SETTING_AUTH ) = { + .name = "gid", + .description = "Group ID", + .tag = DHCP_EB_GID, + .type = &setting_type_uint32 +}; + +/** + * Initialize an ONC RPC AUTH SYS credential structure + * + * @v auth_sys The structure to initialize + * + * The hostname field is filled with the value of the hostname setting, if the + * hostname setting is empty, PRODUCT_SHORT_NAME (usually "iPXE") is used + * instead. + */ +int oncrpc_init_cred_sys ( struct oncrpc_cred_sys *auth_sys ) { + if ( ! auth_sys ) + return -EINVAL; + + fetch_string_setting_copy ( NULL, &hostname_setting, + &auth_sys->hostname ); + if ( ! auth_sys->hostname ) + if ( ! ( auth_sys->hostname = strdup ( PRODUCT_SHORT_NAME ) ) ) + return -ENOMEM; + + auth_sys->uid = fetch_uintz_setting ( NULL, &uid_setting ); + auth_sys->gid = fetch_uintz_setting ( NULL, &uid_setting ); + auth_sys->aux_gid_len = 0; + auth_sys->stamp = 0; + + auth_sys->credential.flavor = ONCRPC_AUTH_SYS; + auth_sys->credential.length = 16 + + oncrpc_strlen ( auth_sys->hostname ); + + return 0; +} + +/** + * Prepare an ONC RPC session structure to be used by the ONC RPC layer + * + * @v session ONC RPC session + * @v credential Credential structure pointer + * @v verifier Verifier structure pointer + * @v prog_name ONC RPC program number + * @v prog_vers ONC RPC program version number + */ +void oncrpc_init_session ( struct oncrpc_session *session, + struct oncrpc_cred *credential, + struct oncrpc_cred *verifier, uint32_t prog_name, + uint32_t prog_vers ) { + if ( ! session ) + return; + + session->rpc_id = rand(); + session->credential = credential; + session->verifier = verifier; + session->prog_name = prog_name; + session->prog_vers = prog_vers; +} + +int oncrpc_call ( struct interface *intf, struct oncrpc_session *session, + uint32_t proc_name, const struct oncrpc_field fields[] ) { + int rc; + size_t frame_size; + struct io_buffer *io_buf; + + if ( ! session ) + return -EINVAL; + + struct oncrpc_field header[] = { + ONCRPC_FIELD ( int32, 0 ), + ONCRPC_FIELD ( int32, ++session->rpc_id ), + ONCRPC_FIELD ( int32, ONCRPC_CALL ), + ONCRPC_FIELD ( int32, ONCRPC_VERS ), + ONCRPC_FIELD ( int32, session->prog_name ), + ONCRPC_FIELD ( int32, session->prog_vers ), + ONCRPC_FIELD ( int32, proc_name ), + ONCRPC_FIELD ( cred, session->credential ), + ONCRPC_FIELD ( cred, session->verifier ), + ONCRPC_FIELD_END, + }; + + frame_size = oncrpc_compute_size ( header ); + frame_size += oncrpc_compute_size ( fields ); + + io_buf = alloc_iob ( frame_size ); + if ( ! io_buf ) + return -ENOBUFS; + + header[0].value.int32 = SET_LAST_FRAME ( frame_size - + sizeof ( uint32_t ) ); + + oncrpc_iob_add_fields ( io_buf, header ); + oncrpc_iob_add_fields ( io_buf, fields ); + + rc = xfer_deliver_iob ( intf, io_buf ); + if ( rc != 0 ) + free_iob ( io_buf ); + + return rc; +} + +size_t oncrpc_compute_size ( const struct oncrpc_field fields[] ) { + + size_t i; + size_t size = 0; + + for ( i = 0; fields[i].type != oncrpc_none; i++ ) { + switch ( fields[i].type ) { + case oncrpc_int32: + size += sizeof ( uint32_t ); + break; + + case oncrpc_int64: + size += sizeof ( uint64_t ); + break; + + case oncrpc_str: + size += oncrpc_strlen ( fields[i].value.str ); + break; + + case oncrpc_array: + size += oncrpc_align ( fields[i].value.array.length ); + size += sizeof ( uint32_t ); + break; + + case oncrpc_intarray: + size += sizeof ( uint32_t ) * + fields[i].value.intarray.length; + size += sizeof ( uint32_t ); + break; + + case oncrpc_cred: + size += fields[i].value.cred->length; + size += 2 * sizeof ( uint32_t ); + break; + + default: + return size; + } + } + + return size; +} + +/** + * Parse an I/O buffer to extract a ONC RPC REPLY + * @v session ONC RPC session + * @v reply Reply structure where data will be saved + * @v io_buf I/O buffer + */ +int oncrpc_get_reply ( struct oncrpc_session *session __unused, + struct oncrpc_reply *reply, struct io_buffer *io_buf ) { + if ( ! reply || ! io_buf ) + return -EINVAL; + + reply->frame_size = GET_FRAME_SIZE ( oncrpc_iob_get_int ( io_buf ) ); + reply->rpc_id = oncrpc_iob_get_int ( io_buf ); + + /* iPXE has no support for handling ONC RPC call */ + if ( oncrpc_iob_get_int ( io_buf ) != ONCRPC_REPLY ) + return -ENOSYS; + + reply->reply_state = oncrpc_iob_get_int ( io_buf ); + + if ( reply->reply_state == 0 ) + { + /* verifier.flavor */ + oncrpc_iob_get_int ( io_buf ); + /* verifier.length */ + iob_pull ( io_buf, oncrpc_iob_get_int ( io_buf )); + + /* We don't use the verifier in iPXE, let it be an empty + verifier. */ + reply->verifier = &oncrpc_auth_none; + } + + reply->accept_state = oncrpc_iob_get_int ( io_buf ); + reply->data = io_buf; + + return 0; +} |