Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / net / oncrpc / nfs_open.c
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  */
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  */
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
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
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
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
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
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
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
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
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  */
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 };