2 * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
28 #include <ipxe/time.h>
29 #include <ipxe/socket.h>
30 #include <ipxe/tcpip.h>
32 #include <ipxe/iobuf.h>
33 #include <ipxe/xfer.h>
34 #include <ipxe/open.h>
36 #include <ipxe/features.h>
38 #include <ipxe/nfs_open.h>
39 #include <ipxe/oncrpc.h>
40 #include <ipxe/oncrpc_iob.h>
41 #include <ipxe/portmap.h>
42 #include <ipxe/mount.h>
43 #include <ipxe/nfs_uri.h>
47 * Network File System protocol
51 FEATURE ( FEATURE_PROTOCOL, "NFS", DHCP_EB_FEATURE_NFS, 1 );
53 #define NFS_RSIZE 100000
57 NFS_PORTMAP_MOUNTPORT,
62 enum nfs_mount_state {
85 /** Reference counter */
87 /** Data transfer interface */
88 struct interface xfer;
90 struct interface pm_intf;
91 struct interface mount_intf;
92 struct interface nfs_intf;
94 enum nfs_pm_state pm_state;
95 enum nfs_mount_state mount_state;
96 enum nfs_state nfs_state;
98 struct oncrpc_session pm_session;
99 struct oncrpc_session mount_session;
100 struct oncrpc_session nfs_session;
102 struct oncrpc_cred_sys auth_sys;
107 struct nfs_fh readlink_fh;
108 struct nfs_fh current_fh;
109 uint64_t file_offset;
115 static void nfs_step ( struct nfs_request *nfs );
120 * @v refcnt Reference counter
122 static void nfs_free ( struct refcnt *refcnt ) {
123 struct nfs_request *nfs;
125 nfs = container_of ( refcnt, struct nfs_request, refcnt );
126 DBGC ( nfs, "NFS_OPEN %p freed\n", nfs );
128 nfs_uri_free ( &nfs->uri );
130 free ( nfs->hostname );
131 free ( nfs->auth_sys.hostname );
136 * Mark NFS operation as complete
139 * @v rc Return status code
141 static void nfs_done ( struct nfs_request *nfs, int rc ) {
142 if ( rc == 0 && nfs->nfs_state != NFS_CLOSED )
145 DBGC ( nfs, "NFS_OPEN %p completed (%s)\n", nfs, strerror ( rc ) );
147 intf_shutdown ( &nfs->xfer, rc );
148 intf_shutdown ( &nfs->pm_intf, rc );
149 intf_shutdown ( &nfs->mount_intf, rc );
150 intf_shutdown ( &nfs->nfs_intf, rc );
153 static int nfs_connect ( struct interface *intf, uint16_t port,
154 const char *hostname ) {
155 struct sockaddr_tcpip peer;
156 struct sockaddr_tcpip local;
158 if ( ! intf || ! hostname || ! port )
161 memset ( &peer, 0, sizeof ( peer ) );
162 memset ( &local, 0, sizeof ( local ) );
163 peer.st_port = htons ( port );
165 /* Use a local port < 1024 to avoid using the 'insecure' option in
166 * /etc/exports file. */
167 local.st_flags = TCPIP_BIND_PRIVILEGED;
169 return xfer_open_named_socket ( intf, SOCK_STREAM,
170 ( struct sockaddr * ) &peer, hostname,
171 ( struct sockaddr * ) &local );
174 static void nfs_pm_step ( struct nfs_request *nfs ) {
177 if ( ! xfer_window ( &nfs->pm_intf ) )
180 if ( nfs->pm_state == NFS_PORTMAP_NONE ) {
181 DBGC ( nfs, "NFS_OPEN %p GETPORT call (mount)\n", nfs );
183 rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session,
184 ONCRPC_MOUNT, MOUNT_VERS,
193 if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) {
194 DBGC ( nfs, "NFS_OPEN %p GETPORT call (nfs)\n", nfs );
196 rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session,
197 ONCRPC_NFS, NFS_VERS,
207 nfs_done ( nfs, rc );
210 static int nfs_pm_deliver ( struct nfs_request *nfs,
211 struct io_buffer *io_buf,
212 struct xfer_metadata *meta __unused ) {
214 struct oncrpc_reply reply;
215 struct portmap_getport_reply getport_reply;
217 oncrpc_get_reply ( &nfs->pm_session, &reply, io_buf );
218 if ( reply.accept_state != 0 )
224 if ( nfs->pm_state == NFS_PORTMAP_MOUNTPORT ) {
225 DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (mount)\n", nfs );
227 rc = portmap_get_getport_reply ( &getport_reply, &reply );
231 rc = nfs_connect ( &nfs->mount_intf, getport_reply.port,
242 if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) {
243 DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (nfs)\n", nfs );
245 rc = portmap_get_getport_reply ( &getport_reply, &reply );
249 rc = nfs_connect ( &nfs->nfs_intf, getport_reply.port,
254 intf_shutdown ( &nfs->pm_intf, 0 );
262 nfs_done ( nfs, rc );
268 static void nfs_mount_step ( struct nfs_request *nfs ) {
271 if ( ! xfer_window ( &nfs->mount_intf ) )
274 if ( nfs->mount_state == NFS_MOUNT_NONE ) {
275 DBGC ( nfs, "NFS_OPEN %p MNT call (%s)\n", nfs,
276 nfs_uri_mountpoint ( &nfs->uri ) );
278 rc = mount_mnt ( &nfs->mount_intf, &nfs->mount_session,
279 nfs_uri_mountpoint ( &nfs->uri ) );
287 if ( nfs->mount_state == NFS_MOUNT_UMNT ) {
288 DBGC ( nfs, "NFS_OPEN %p UMNT call\n", nfs );
290 rc = mount_umnt ( &nfs->mount_intf, &nfs->mount_session,
291 nfs_uri_mountpoint ( &nfs->uri ) );
298 nfs_done ( nfs, rc );
301 static int nfs_mount_deliver ( struct nfs_request *nfs,
302 struct io_buffer *io_buf,
303 struct xfer_metadata *meta __unused ) {
305 struct oncrpc_reply reply;
306 struct mount_mnt_reply mnt_reply;
308 oncrpc_get_reply ( &nfs->mount_session, &reply, io_buf );
309 if ( reply.accept_state != 0 )
315 if ( nfs->mount_state == NFS_MOUNT_MNT ) {
316 DBGC ( nfs, "NFS_OPEN %p got MNT reply\n", nfs );
317 rc = mount_get_mnt_reply ( &mnt_reply, &reply );
319 switch ( mnt_reply.status ) {
329 if ( ! strcmp ( nfs_uri_mountpoint ( &nfs->uri ),
333 if ( ( rc = nfs_uri_next_mountpoint ( &nfs->uri ) ) )
336 DBGC ( nfs, "NFS_OPEN %p MNT failed retrying with " \
337 "%s\n", nfs, nfs_uri_mountpoint ( &nfs->uri ) );
340 nfs_mount_step ( nfs );
345 nfs->current_fh = mnt_reply.fh;
346 nfs->nfs_state = NFS_LOOKUP;
352 if ( nfs->mount_state == NFS_MOUNT_UMNT ) {
353 DBGC ( nfs, "NFS_OPEN %p got UMNT reply\n", nfs );
361 nfs_done ( nfs, rc );
367 static void nfs_step ( struct nfs_request *nfs ) {
369 char *path_component;
371 if ( ! xfer_window ( &nfs->nfs_intf ) )
374 if ( nfs->nfs_state == NFS_LOOKUP ) {
375 path_component = nfs_uri_next_path_component ( &nfs->uri );
377 DBGC ( nfs, "NFS_OPEN %p LOOKUP call (%s)\n", nfs,
380 rc = nfs_lookup ( &nfs->nfs_intf, &nfs->nfs_session,
381 &nfs->current_fh, path_component );
390 if ( nfs->nfs_state == NFS_READLINK ) {
391 DBGC ( nfs, "NFS_OPEN %p READLINK call\n", nfs );
393 rc = nfs_readlink ( &nfs->nfs_intf, &nfs->nfs_session,
402 if ( nfs->nfs_state == NFS_READ ) {
403 DBGC ( nfs, "NFS_OPEN %p READ call\n", nfs );
405 rc = nfs_read ( &nfs->nfs_intf, &nfs->nfs_session,
406 &nfs->current_fh, nfs->file_offset,
417 nfs_done ( nfs, rc );
420 static int nfs_deliver ( struct nfs_request *nfs,
421 struct io_buffer *io_buf,
422 struct xfer_metadata *meta __unused ) {
424 struct oncrpc_reply reply;
426 if ( nfs->remaining == 0 ) {
427 oncrpc_get_reply ( &nfs->nfs_session, &reply, io_buf );
428 if ( reply.accept_state != 0 ) {
434 if ( nfs->nfs_state == NFS_LOOKUP_SENT ) {
435 struct nfs_lookup_reply lookup_reply;
437 DBGC ( nfs, "NFS_OPEN %p got LOOKUP reply\n", nfs );
439 rc = nfs_get_lookup_reply ( &lookup_reply, &reply );
443 if ( lookup_reply.ent_type == NFS_ATTR_SYMLINK ) {
444 nfs->readlink_fh = lookup_reply.fh;
445 nfs->nfs_state = NFS_READLINK;
447 nfs->current_fh = lookup_reply.fh;
449 if ( nfs->uri.lookup_pos[0] == '\0' )
450 nfs->nfs_state = NFS_READ;
459 if ( nfs->nfs_state == NFS_READLINK_SENT ) {
461 struct nfs_readlink_reply readlink_reply;
463 DBGC ( nfs, "NFS_OPEN %p got READLINK reply\n", nfs );
465 rc = nfs_get_readlink_reply ( &readlink_reply, &reply );
469 if ( readlink_reply.path_len == 0 )
475 if ( ! ( path = strndup ( readlink_reply.path,
476 readlink_reply.path_len ) ) )
482 nfs_uri_symlink ( &nfs->uri, path );
485 DBGC ( nfs, "NFS_OPEN %p new path: %s\n", nfs,
488 nfs->nfs_state = NFS_LOOKUP;
493 if ( nfs->nfs_state == NFS_READ_SENT ) {
494 if ( nfs->remaining == 0 ) {
495 DBGC ( nfs, "NFS_OPEN %p got READ reply\n", nfs );
497 struct nfs_read_reply read_reply;
499 rc = nfs_get_read_reply ( &read_reply, &reply );
503 if ( nfs->file_offset == 0 ) {
504 DBGC2 ( nfs, "NFS_OPEN %p size: %llu bytes\n",
505 nfs, read_reply.filesize );
507 xfer_seek ( &nfs->xfer, read_reply.filesize );
508 xfer_seek ( &nfs->xfer, 0 );
511 nfs->file_offset += read_reply.count;
512 nfs->remaining = read_reply.count;
513 nfs->eof = read_reply.eof;
516 size_t len = iob_len ( io_buf );
517 if ( len > nfs->remaining )
518 iob_unput ( io_buf, len - nfs->remaining );
520 nfs->remaining -= iob_len ( io_buf );
522 DBGC ( nfs, "NFS_OPEN %p got %zd bytes\n", nfs,
523 iob_len ( io_buf ) );
525 rc = xfer_deliver_iob ( &nfs->xfer, iob_disown ( io_buf ) );
529 if ( nfs->remaining == 0 ) {
534 intf_shutdown ( &nfs->nfs_intf, 0 );
537 nfs_mount_step ( nfs );
546 nfs_done ( nfs, rc );
552 /*****************************************************************************
557 static struct interface_operation nfs_xfer_operations[] = {
558 INTF_OP ( intf_close, struct nfs_request *, nfs_done ),
561 /** NFS data transfer interface descriptor */
562 static struct interface_descriptor nfs_xfer_desc =
563 INTF_DESC ( struct nfs_request, xfer, nfs_xfer_operations );
565 static struct interface_operation nfs_pm_operations[] = {
566 INTF_OP ( intf_close, struct nfs_request *, nfs_done ),
567 INTF_OP ( xfer_deliver, struct nfs_request *, nfs_pm_deliver ),
568 INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_pm_step ),
571 static struct interface_descriptor nfs_pm_desc =
572 INTF_DESC ( struct nfs_request, pm_intf, nfs_pm_operations );
574 static struct interface_operation nfs_mount_operations[] = {
575 INTF_OP ( intf_close, struct nfs_request *, nfs_done ),
576 INTF_OP ( xfer_deliver, struct nfs_request *, nfs_mount_deliver ),
577 INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_mount_step ),
580 static struct interface_descriptor nfs_mount_desc =
581 INTF_DESC ( struct nfs_request, mount_intf, nfs_mount_operations );
583 static struct interface_operation nfs_operations[] = {
584 INTF_OP ( intf_close, struct nfs_request *, nfs_done ),
585 INTF_OP ( xfer_deliver, struct nfs_request *, nfs_deliver ),
586 INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_step ),
589 static struct interface_descriptor nfs_desc =
590 INTF_DESC_PASSTHRU ( struct nfs_request, nfs_intf, nfs_operations,
593 /*****************************************************************************
599 static int nfs_parse_uri ( struct nfs_request *nfs, const struct uri *uri ) {
602 if ( ! uri || ! uri->host || ! uri->path )
605 if ( ( rc = nfs_uri_init ( &nfs->uri, uri ) ) != 0 )
608 if ( ! ( nfs->hostname = strdup ( uri->host ) ) ) {
613 DBGC ( nfs, "NFS_OPEN %p URI parsed: (mountpoint=%s, path=%s)\n",
614 nfs, nfs_uri_mountpoint ( &nfs->uri), nfs->uri.path );
619 nfs_uri_free ( &nfs->uri );
624 * Initiate a NFS connection
626 * @v xfer Data transfer interface
627 * @v uri Uniform Resource Identifier
628 * @ret rc Return status code
630 static int nfs_open ( struct interface *xfer, struct uri *uri ) {
632 struct nfs_request *nfs;
634 nfs = zalloc ( sizeof ( *nfs ) );
638 rc = nfs_parse_uri( nfs, uri );
642 rc = oncrpc_init_cred_sys ( &nfs->auth_sys );
646 ref_init ( &nfs->refcnt, nfs_free );
647 intf_init ( &nfs->xfer, &nfs_xfer_desc, &nfs->refcnt );
648 intf_init ( &nfs->pm_intf, &nfs_pm_desc, &nfs->refcnt );
649 intf_init ( &nfs->mount_intf, &nfs_mount_desc, &nfs->refcnt );
650 intf_init ( &nfs->nfs_intf, &nfs_desc, &nfs->refcnt );
652 portmap_init_session ( &nfs->pm_session, &nfs->auth_sys.credential );
653 mount_init_session ( &nfs->mount_session, &nfs->auth_sys.credential );
654 nfs_init_session ( &nfs->nfs_session, &nfs->auth_sys.credential );
656 DBGC ( nfs, "NFS_OPEN %p connecting to port mapper (%s:%d)...\n", nfs,
657 nfs->hostname, PORTMAP_PORT );
659 rc = nfs_connect ( &nfs->pm_intf, PORTMAP_PORT, nfs->hostname );
663 /* Attach to parent interface, mortalise self, and return */
664 intf_plug_plug ( &nfs->xfer, xfer );
665 ref_put ( &nfs->refcnt );
670 free ( nfs->auth_sys.hostname );
672 nfs_uri_free ( &nfs->uri );
673 free ( nfs->hostname );
679 /** NFS URI opener */
680 struct uri_opener nfs_uri_opener __uri_opener = {