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