1 /*
2 * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>.
3 *
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.
8 *
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.
13 *
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
17 * 02110-1301, USA.
18 */
19
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <assert.h>
25 #include <errno.h>
26 #include <libgen.h>
27 #include <byteswap.h>
28 #include <ipxe/time.h>
29 #include <ipxe/socket.h>
30 #include <ipxe/tcpip.h>
31 #include <ipxe/in.h>
32 #include <ipxe/iobuf.h>
33 #include <ipxe/xfer.h>
34 #include <ipxe/open.h>
35 #include <ipxe/uri.h>
36 #include <ipxe/features.h>
37 #include <ipxe/nfs.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>
44
45 /** @file
46 *
47 * Network File System protocol
48 *
49 */
50
51 FEATURE ( FEATURE_PROTOCOL, "NFS", DHCP_EB_FEATURE_NFS, 1 );
52
53 #define NFS_RSIZE 100000
54
55 enum nfs_pm_state {
56 NFS_PORTMAP_NONE = 0,
57 NFS_PORTMAP_MOUNTPORT,
58 NFS_PORTMAP_NFSPORT,
59 MFS_PORTMAP_CLOSED,
60 };
61
62 enum nfs_mount_state {
63 NFS_MOUNT_NONE = 0,
64 NFS_MOUNT_MNT,
65 NFS_MOUNT_UMNT,
66 NFS_MOUNT_CLOSED,
67 };
68
69 enum nfs_state {
70 NFS_NONE = 0,
71 NFS_LOOKUP,
72 NFS_LOOKUP_SENT,
73 NFS_READLINK,
74 NFS_READLINK_SENT,
75 NFS_READ,
76 NFS_READ_SENT,
77 NFS_CLOSED,
78 };
79
80 /**
81 * A NFS request
82 *
83 */
84 struct nfs_request {
85 /** Reference counter */
86 struct refcnt refcnt;
87 /** Data transfer interface */
88 struct interface xfer;
89
90 struct interface pm_intf;
91 struct interface mount_intf;
92 struct interface nfs_intf;
93
94 enum nfs_pm_state pm_state;
95 enum nfs_mount_state mount_state;
96 enum nfs_state nfs_state;
97
98 struct oncrpc_session pm_session;
99 struct oncrpc_session mount_session;
100 struct oncrpc_session nfs_session;
101
102 struct oncrpc_cred_sys auth_sys;
103
104 char * hostname;
105 struct nfs_uri uri;
106
107 struct nfs_fh readlink_fh;
108 struct nfs_fh current_fh;
109 uint64_t file_offset;
110
111 size_t remaining;
112 int eof;
113 };
114
115 static void nfs_step ( struct nfs_request *nfs );
116
117 /**
118 * Free NFS request
119 *
120 * @v refcnt Reference counter
121 */
nfs_free(struct refcnt * refcnt)122 static void nfs_free ( struct refcnt *refcnt ) {
123 struct nfs_request *nfs;
124
125 nfs = container_of ( refcnt, struct nfs_request, refcnt );
126 DBGC ( nfs, "NFS_OPEN %p freed\n", nfs );
127
128 nfs_uri_free ( &nfs->uri );
129
130 free ( nfs->hostname );
131 free ( nfs->auth_sys.hostname );
132 free ( nfs );
133 }
134
135 /**
136 * Mark NFS operation as complete
137 *
138 * @v nfs NFS request
139 * @v rc Return status code
140 */
nfs_done(struct nfs_request * nfs,int rc)141 static void nfs_done ( struct nfs_request *nfs, int rc ) {
142 if ( rc == 0 && nfs->nfs_state != NFS_CLOSED )
143 rc = -ECONNRESET;
144
145 DBGC ( nfs, "NFS_OPEN %p completed (%s)\n", nfs, strerror ( rc ) );
146
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 );
151 }
152
nfs_connect(struct interface * intf,uint16_t port,const char * hostname)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;
157
158 if ( ! intf || ! hostname || ! port )
159 return -EINVAL;
160
161 memset ( &peer, 0, sizeof ( peer ) );
162 memset ( &local, 0, sizeof ( local ) );
163 peer.st_port = htons ( port );
164
165 /* Use a local port < 1024 to avoid using the 'insecure' option in
166 * /etc/exports file. */
167 local.st_flags = TCPIP_BIND_PRIVILEGED;
168
169 return xfer_open_named_socket ( intf, SOCK_STREAM,
170 ( struct sockaddr * ) &peer, hostname,
171 ( struct sockaddr * ) &local );
172 }
173
nfs_pm_step(struct nfs_request * nfs)174 static void nfs_pm_step ( struct nfs_request *nfs ) {
175 int rc;
176
177 if ( ! xfer_window ( &nfs->pm_intf ) )
178 return;
179
180 if ( nfs->pm_state == NFS_PORTMAP_NONE ) {
181 DBGC ( nfs, "NFS_OPEN %p GETPORT call (mount)\n", nfs );
182
183 rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session,
184 ONCRPC_MOUNT, MOUNT_VERS,
185 PORTMAP_PROTO_TCP );
186 if ( rc != 0 )
187 goto err;
188
189 nfs->pm_state++;
190 return;
191 }
192
193 if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) {
194 DBGC ( nfs, "NFS_OPEN %p GETPORT call (nfs)\n", nfs );
195
196 rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session,
197 ONCRPC_NFS, NFS_VERS,
198 PORTMAP_PROTO_TCP );
199 if ( rc != 0 )
200 goto err;
201
202 return;
203 }
204
205 return;
206 err:
207 nfs_done ( nfs, rc );
208 }
209
nfs_pm_deliver(struct nfs_request * nfs,struct io_buffer * io_buf,struct xfer_metadata * meta __unused)210 static int nfs_pm_deliver ( struct nfs_request *nfs,
211 struct io_buffer *io_buf,
212 struct xfer_metadata *meta __unused ) {
213 int rc;
214 struct oncrpc_reply reply;
215 struct portmap_getport_reply getport_reply;
216
217 oncrpc_get_reply ( &nfs->pm_session, &reply, io_buf );
218 if ( reply.accept_state != 0 )
219 {
220 rc = -EPROTO;
221 goto err;
222 }
223
224 if ( nfs->pm_state == NFS_PORTMAP_MOUNTPORT ) {
225 DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (mount)\n", nfs );
226
227 rc = portmap_get_getport_reply ( &getport_reply, &reply );
228 if ( rc != 0 )
229 goto err;
230
231 rc = nfs_connect ( &nfs->mount_intf, getport_reply.port,
232 nfs->hostname );
233 if ( rc != 0 )
234 goto err;
235
236 nfs->pm_state++;
237 nfs_pm_step ( nfs );
238
239 goto done;
240 }
241
242 if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) {
243 DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (nfs)\n", nfs );
244
245 rc = portmap_get_getport_reply ( &getport_reply, &reply );
246 if ( rc != 0 )
247 goto err;
248
249 rc = nfs_connect ( &nfs->nfs_intf, getport_reply.port,
250 nfs->hostname );
251 if ( rc != 0 )
252 goto err;
253
254 intf_shutdown ( &nfs->pm_intf, 0 );
255 nfs->pm_state++;
256
257 goto done;
258 }
259
260 rc = -EPROTO;
261 err:
262 nfs_done ( nfs, rc );
263 done:
264 free_iob ( io_buf );
265 return 0;
266 }
267
nfs_mount_step(struct nfs_request * nfs)268 static void nfs_mount_step ( struct nfs_request *nfs ) {
269 int rc;
270
271 if ( ! xfer_window ( &nfs->mount_intf ) )
272 return;
273
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 ) );
277
278 rc = mount_mnt ( &nfs->mount_intf, &nfs->mount_session,
279 nfs_uri_mountpoint ( &nfs->uri ) );
280 if ( rc != 0 )
281 goto err;
282
283 nfs->mount_state++;
284 return;
285 }
286
287 if ( nfs->mount_state == NFS_MOUNT_UMNT ) {
288 DBGC ( nfs, "NFS_OPEN %p UMNT call\n", nfs );
289
290 rc = mount_umnt ( &nfs->mount_intf, &nfs->mount_session,
291 nfs_uri_mountpoint ( &nfs->uri ) );
292 if ( rc != 0 )
293 goto err;
294 }
295
296 return;
297 err:
298 nfs_done ( nfs, rc );
299 }
300
nfs_mount_deliver(struct nfs_request * nfs,struct io_buffer * io_buf,struct xfer_metadata * meta __unused)301 static int nfs_mount_deliver ( struct nfs_request *nfs,
302 struct io_buffer *io_buf,
303 struct xfer_metadata *meta __unused ) {
304 int rc;
305 struct oncrpc_reply reply;
306 struct mount_mnt_reply mnt_reply;
307
308 oncrpc_get_reply ( &nfs->mount_session, &reply, io_buf );
309 if ( reply.accept_state != 0 )
310 {
311 rc = -EPROTO;
312 goto err;
313 }
314
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 );
318 if ( rc != 0 ) {
319 switch ( mnt_reply.status ) {
320 case MNT3ERR_NOTDIR:
321 case MNT3ERR_NOENT:
322 case MNT3ERR_ACCES:
323 break;
324
325 default:
326 goto err;
327 }
328
329 if ( ! strcmp ( nfs_uri_mountpoint ( &nfs->uri ),
330 "/" ) )
331 goto err;
332
333 if ( ( rc = nfs_uri_next_mountpoint ( &nfs->uri ) ) )
334 goto err;
335
336 DBGC ( nfs, "NFS_OPEN %p MNT failed retrying with " \
337 "%s\n", nfs, nfs_uri_mountpoint ( &nfs->uri ) );
338
339 nfs->mount_state--;
340 nfs_mount_step ( nfs );
341
342 goto done;
343 }
344
345 nfs->current_fh = mnt_reply.fh;
346 nfs->nfs_state = NFS_LOOKUP;
347 nfs_step ( nfs );
348
349 goto done;
350 }
351
352 if ( nfs->mount_state == NFS_MOUNT_UMNT ) {
353 DBGC ( nfs, "NFS_OPEN %p got UMNT reply\n", nfs );
354 nfs_done ( nfs, 0 );
355
356 goto done;
357 }
358
359 rc = -EPROTO;
360 err:
361 nfs_done ( nfs, rc );
362 done:
363 free_iob ( io_buf );
364 return 0;
365 }
366
nfs_step(struct nfs_request * nfs)367 static void nfs_step ( struct nfs_request *nfs ) {
368 int rc;
369 char *path_component;
370
371 if ( ! xfer_window ( &nfs->nfs_intf ) )
372 return;
373
374 if ( nfs->nfs_state == NFS_LOOKUP ) {
375 path_component = nfs_uri_next_path_component ( &nfs->uri );
376
377 DBGC ( nfs, "NFS_OPEN %p LOOKUP call (%s)\n", nfs,
378 path_component );
379
380 rc = nfs_lookup ( &nfs->nfs_intf, &nfs->nfs_session,
381 &nfs->current_fh, path_component );
382 if ( rc != 0 )
383 goto err;
384
385 nfs->nfs_state++;
386 return;
387 }
388
389
390 if ( nfs->nfs_state == NFS_READLINK ) {
391 DBGC ( nfs, "NFS_OPEN %p READLINK call\n", nfs );
392
393 rc = nfs_readlink ( &nfs->nfs_intf, &nfs->nfs_session,
394 &nfs->readlink_fh );
395 if ( rc != 0 )
396 goto err;
397
398 nfs->nfs_state++;
399 return;
400 }
401
402 if ( nfs->nfs_state == NFS_READ ) {
403 DBGC ( nfs, "NFS_OPEN %p READ call\n", nfs );
404
405 rc = nfs_read ( &nfs->nfs_intf, &nfs->nfs_session,
406 &nfs->current_fh, nfs->file_offset,
407 NFS_RSIZE );
408 if ( rc != 0 )
409 goto err;
410
411 nfs->nfs_state++;
412 return;
413 }
414
415 return;
416 err:
417 nfs_done ( nfs, rc );
418 }
419
nfs_deliver(struct nfs_request * nfs,struct io_buffer * io_buf,struct xfer_metadata * meta __unused)420 static int nfs_deliver ( struct nfs_request *nfs,
421 struct io_buffer *io_buf,
422 struct xfer_metadata *meta __unused ) {
423 int rc;
424 struct oncrpc_reply reply;
425
426 if ( nfs->remaining == 0 ) {
427 oncrpc_get_reply ( &nfs->nfs_session, &reply, io_buf );
428 if ( reply.accept_state != 0 ) {
429 rc = -EPROTO;
430 goto err;
431 }
432 }
433
434 if ( nfs->nfs_state == NFS_LOOKUP_SENT ) {
435 struct nfs_lookup_reply lookup_reply;
436
437 DBGC ( nfs, "NFS_OPEN %p got LOOKUP reply\n", nfs );
438
439 rc = nfs_get_lookup_reply ( &lookup_reply, &reply );
440 if ( rc != 0 )
441 goto err;
442
443 if ( lookup_reply.ent_type == NFS_ATTR_SYMLINK ) {
444 nfs->readlink_fh = lookup_reply.fh;
445 nfs->nfs_state = NFS_READLINK;
446 } else {
447 nfs->current_fh = lookup_reply.fh;
448
449 if ( nfs->uri.lookup_pos[0] == '\0' )
450 nfs->nfs_state = NFS_READ;
451 else
452 nfs->nfs_state--;
453 }
454
455 nfs_step ( nfs );
456 goto done;
457 }
458
459 if ( nfs->nfs_state == NFS_READLINK_SENT ) {
460 char *path;
461 struct nfs_readlink_reply readlink_reply;
462
463 DBGC ( nfs, "NFS_OPEN %p got READLINK reply\n", nfs );
464
465 rc = nfs_get_readlink_reply ( &readlink_reply, &reply );
466 if ( rc != 0 )
467 goto err;
468
469 if ( readlink_reply.path_len == 0 )
470 {
471 rc = -EINVAL;
472 goto err;
473 }
474
475 if ( ! ( path = strndup ( readlink_reply.path,
476 readlink_reply.path_len ) ) )
477 {
478 rc = -ENOMEM;
479 goto err;
480 }
481
482 nfs_uri_symlink ( &nfs->uri, path );
483 free ( path );
484
485 DBGC ( nfs, "NFS_OPEN %p new path: %s\n", nfs,
486 nfs->uri.path );
487
488 nfs->nfs_state = NFS_LOOKUP;
489 nfs_step ( nfs );
490 goto done;
491 }
492
493 if ( nfs->nfs_state == NFS_READ_SENT ) {
494 if ( nfs->remaining == 0 ) {
495 DBGC ( nfs, "NFS_OPEN %p got READ reply\n", nfs );
496
497 struct nfs_read_reply read_reply;
498
499 rc = nfs_get_read_reply ( &read_reply, &reply );
500 if ( rc != 0 )
501 goto err;
502
503 if ( nfs->file_offset == 0 ) {
504 DBGC2 ( nfs, "NFS_OPEN %p size: %llu bytes\n",
505 nfs, read_reply.filesize );
506
507 xfer_seek ( &nfs->xfer, read_reply.filesize );
508 xfer_seek ( &nfs->xfer, 0 );
509 }
510
511 nfs->file_offset += read_reply.count;
512 nfs->remaining = read_reply.count;
513 nfs->eof = read_reply.eof;
514 }
515
516 size_t len = iob_len ( io_buf );
517 if ( len > nfs->remaining )
518 iob_unput ( io_buf, len - nfs->remaining );
519
520 nfs->remaining -= iob_len ( io_buf );
521
522 DBGC ( nfs, "NFS_OPEN %p got %zd bytes\n", nfs,
523 iob_len ( io_buf ) );
524
525 rc = xfer_deliver_iob ( &nfs->xfer, iob_disown ( io_buf ) );
526 if ( rc != 0 )
527 goto err;
528
529 if ( nfs->remaining == 0 ) {
530 if ( ! nfs->eof ) {
531 nfs->nfs_state--;
532 nfs_step ( nfs );
533 } else {
534 intf_shutdown ( &nfs->nfs_intf, 0 );
535 nfs->nfs_state++;
536 nfs->mount_state++;
537 nfs_mount_step ( nfs );
538 }
539 }
540
541 return 0;
542 }
543
544 rc = -EPROTO;
545 err:
546 nfs_done ( nfs, rc );
547 done:
548 free_iob ( io_buf );
549 return 0;
550 }
551
552 /*****************************************************************************
553 * Interfaces
554 *
555 */
556
557 static struct interface_operation nfs_xfer_operations[] = {
558 INTF_OP ( intf_close, struct nfs_request *, nfs_done ),
559 };
560
561 /** NFS data transfer interface descriptor */
562 static struct interface_descriptor nfs_xfer_desc =
563 INTF_DESC ( struct nfs_request, xfer, nfs_xfer_operations );
564
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 ),
569 };
570
571 static struct interface_descriptor nfs_pm_desc =
572 INTF_DESC ( struct nfs_request, pm_intf, nfs_pm_operations );
573
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 ),
578 };
579
580 static struct interface_descriptor nfs_mount_desc =
581 INTF_DESC ( struct nfs_request, mount_intf, nfs_mount_operations );
582
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 ),
587 };
588
589 static struct interface_descriptor nfs_desc =
590 INTF_DESC_PASSTHRU ( struct nfs_request, nfs_intf, nfs_operations,
591 xfer );
592
593 /*****************************************************************************
594 *
595 * URI opener
596 *
597 */
598
nfs_parse_uri(struct nfs_request * nfs,const struct uri * uri)599 static int nfs_parse_uri ( struct nfs_request *nfs, const struct uri *uri ) {
600 int rc;
601
602 if ( ! uri || ! uri->host || ! uri->path )
603 return -EINVAL;
604
605 if ( ( rc = nfs_uri_init ( &nfs->uri, uri ) ) != 0 )
606 return rc;
607
608 if ( ! ( nfs->hostname = strdup ( uri->host ) ) ) {
609 rc = -ENOMEM;
610 goto err_hostname;
611 }
612
613 DBGC ( nfs, "NFS_OPEN %p URI parsed: (mountpoint=%s, path=%s)\n",
614 nfs, nfs_uri_mountpoint ( &nfs->uri), nfs->uri.path );
615
616 return 0;
617
618 err_hostname:
619 nfs_uri_free ( &nfs->uri );
620 return rc;
621 }
622
623 /**
624 * Initiate a NFS connection
625 *
626 * @v xfer Data transfer interface
627 * @v uri Uniform Resource Identifier
628 * @ret rc Return status code
629 */
nfs_open(struct interface * xfer,struct uri * uri)630 static int nfs_open ( struct interface *xfer, struct uri *uri ) {
631 int rc;
632 struct nfs_request *nfs;
633
634 nfs = zalloc ( sizeof ( *nfs ) );
635 if ( ! nfs )
636 return -ENOMEM;
637
638 rc = nfs_parse_uri( nfs, uri );
639 if ( rc != 0 )
640 goto err_uri;
641
642 rc = oncrpc_init_cred_sys ( &nfs->auth_sys );
643 if ( rc != 0 )
644 goto err_cred;
645
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 );
651
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 );
655
656 DBGC ( nfs, "NFS_OPEN %p connecting to port mapper (%s:%d)...\n", nfs,
657 nfs->hostname, PORTMAP_PORT );
658
659 rc = nfs_connect ( &nfs->pm_intf, PORTMAP_PORT, nfs->hostname );
660 if ( rc != 0 )
661 goto err_connect;
662
663 /* Attach to parent interface, mortalise self, and return */
664 intf_plug_plug ( &nfs->xfer, xfer );
665 ref_put ( &nfs->refcnt );
666
667 return 0;
668
669 err_connect:
670 free ( nfs->auth_sys.hostname );
671 err_cred:
672 nfs_uri_free ( &nfs->uri );
673 free ( nfs->hostname );
674 err_uri:
675 free ( nfs );
676 return rc;
677 }
678
679 /** NFS URI opener */
680 struct uri_opener nfs_uri_opener __uri_opener = {
681 .scheme = "nfs",
682 .open = nfs_open,
683 };
684