iPXE
nfs_open.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2013 Marin Hannache <ipxe@mareo.fr>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  */
00019 
00020 #include <stdint.h>
00021 #include <stdlib.h>
00022 #include <stdio.h>
00023 #include <string.h>
00024 #include <assert.h>
00025 #include <errno.h>
00026 #include <libgen.h>
00027 #include <byteswap.h>
00028 #include <ipxe/time.h>
00029 #include <ipxe/socket.h>
00030 #include <ipxe/tcpip.h>
00031 #include <ipxe/in.h>
00032 #include <ipxe/iobuf.h>
00033 #include <ipxe/xfer.h>
00034 #include <ipxe/open.h>
00035 #include <ipxe/uri.h>
00036 #include <ipxe/features.h>
00037 #include <ipxe/nfs.h>
00038 #include <ipxe/nfs_open.h>
00039 #include <ipxe/oncrpc.h>
00040 #include <ipxe/oncrpc_iob.h>
00041 #include <ipxe/portmap.h>
00042 #include <ipxe/mount.h>
00043 #include <ipxe/nfs_uri.h>
00044 
00045 /** @file
00046  *
00047  * Network File System protocol
00048  *
00049  */
00050 
00051 FEATURE ( FEATURE_PROTOCOL, "NFS", DHCP_EB_FEATURE_NFS, 1 );
00052 
00053 #define NFS_RSIZE 100000
00054 
00055 enum nfs_pm_state {
00056         NFS_PORTMAP_NONE = 0,
00057         NFS_PORTMAP_MOUNTPORT,
00058         NFS_PORTMAP_NFSPORT,
00059         MFS_PORTMAP_CLOSED,
00060 };
00061 
00062 enum nfs_mount_state {
00063         NFS_MOUNT_NONE = 0,
00064         NFS_MOUNT_MNT,
00065         NFS_MOUNT_UMNT,
00066         NFS_MOUNT_CLOSED,
00067 };
00068 
00069 enum nfs_state {
00070         NFS_NONE = 0,
00071         NFS_LOOKUP,
00072         NFS_LOOKUP_SENT,
00073         NFS_READLINK,
00074         NFS_READLINK_SENT,
00075         NFS_READ,
00076         NFS_READ_SENT,
00077         NFS_CLOSED,
00078 };
00079 
00080 /**
00081  * A NFS request
00082  *
00083  */
00084 struct nfs_request {
00085         /** Reference counter */
00086         struct refcnt           refcnt;
00087         /** Data transfer interface */
00088         struct interface        xfer;
00089 
00090         struct interface        pm_intf;
00091         struct interface        mount_intf;
00092         struct interface        nfs_intf;
00093 
00094         enum nfs_pm_state       pm_state;
00095         enum nfs_mount_state    mount_state;
00096         enum nfs_state          nfs_state;
00097 
00098         struct oncrpc_session   pm_session;
00099         struct oncrpc_session   mount_session;
00100         struct oncrpc_session   nfs_session;
00101 
00102         struct oncrpc_cred_sys  auth_sys;
00103 
00104         char *                  hostname;
00105         struct nfs_uri          uri;
00106 
00107         struct nfs_fh           readlink_fh;
00108         struct nfs_fh           current_fh;
00109         uint64_t                file_offset;
00110 
00111         size_t                  remaining;
00112         int                     eof;
00113 };
00114 
00115 static void nfs_step ( struct nfs_request *nfs );
00116 
00117 /**
00118  * Free NFS request
00119  *
00120  * @v refcnt            Reference counter
00121  */
00122 static void nfs_free ( struct refcnt *refcnt ) {
00123         struct nfs_request      *nfs;
00124 
00125         nfs = container_of ( refcnt, struct nfs_request, refcnt );
00126         DBGC ( nfs, "NFS_OPEN %p freed\n", nfs );
00127 
00128         nfs_uri_free ( &nfs->uri );
00129 
00130         free ( nfs->hostname );
00131         free ( nfs->auth_sys.hostname );
00132         free ( nfs );
00133 }
00134 
00135 /**
00136  * Mark NFS operation as complete
00137  *
00138  * @v nfs               NFS request
00139  * @v rc                Return status code
00140  */
00141 static void nfs_done ( struct nfs_request *nfs, int rc ) {
00142         if ( rc == 0 && nfs->nfs_state != NFS_CLOSED )
00143                 rc = -ECONNRESET;
00144 
00145         DBGC ( nfs, "NFS_OPEN %p completed (%s)\n", nfs, strerror ( rc ) );
00146 
00147         intf_shutdown ( &nfs->xfer, rc );
00148         intf_shutdown ( &nfs->pm_intf, rc );
00149         intf_shutdown ( &nfs->mount_intf, rc );
00150         intf_shutdown ( &nfs->nfs_intf, rc );
00151 }
00152 
00153 static int nfs_connect ( struct interface *intf, uint16_t port,
00154                          const char *hostname ) {
00155         struct sockaddr_tcpip   peer;
00156         struct sockaddr_tcpip   local;
00157 
00158         if ( ! intf || ! hostname || ! port )
00159                 return -EINVAL;
00160 
00161         memset ( &peer, 0, sizeof ( peer ) );
00162         memset ( &local, 0, sizeof ( local ) );
00163         peer.st_port = htons ( port );
00164 
00165         /* Use a local port < 1024 to avoid using the 'insecure' option in
00166          * /etc/exports file. */
00167         local.st_flags = TCPIP_BIND_PRIVILEGED;
00168 
00169         return xfer_open_named_socket ( intf, SOCK_STREAM,
00170                                         ( struct sockaddr * ) &peer, hostname,
00171                                         ( struct sockaddr * ) &local );
00172 }
00173 
00174 static void nfs_pm_step ( struct nfs_request *nfs ) {
00175         int     rc;
00176 
00177         if ( ! xfer_window ( &nfs->pm_intf ) )
00178                 return;
00179 
00180         if ( nfs->pm_state == NFS_PORTMAP_NONE ) {
00181                 DBGC ( nfs, "NFS_OPEN %p GETPORT call (mount)\n", nfs );
00182 
00183                 rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session,
00184                                        ONCRPC_MOUNT, MOUNT_VERS,
00185                                        PORTMAP_PROTO_TCP );
00186                 if ( rc != 0 )
00187                         goto err;
00188 
00189                 nfs->pm_state++;
00190                 return;
00191         }
00192 
00193         if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) {
00194                 DBGC ( nfs, "NFS_OPEN %p GETPORT call (nfs)\n", nfs );
00195 
00196                 rc = portmap_getport ( &nfs->pm_intf, &nfs->pm_session,
00197                                        ONCRPC_NFS, NFS_VERS,
00198                                        PORTMAP_PROTO_TCP );
00199                 if ( rc != 0 )
00200                         goto err;
00201 
00202                 return;
00203         }
00204 
00205         return;
00206 err:
00207         nfs_done ( nfs, rc );
00208 }
00209 
00210 static int nfs_pm_deliver ( struct nfs_request *nfs,
00211                             struct io_buffer *io_buf,
00212                             struct xfer_metadata *meta __unused ) {
00213         int                             rc;
00214         struct oncrpc_reply             reply;
00215         struct portmap_getport_reply    getport_reply;
00216 
00217         oncrpc_get_reply ( &nfs->pm_session, &reply, io_buf );
00218         if ( reply.accept_state != 0 )
00219         {
00220                 rc = -EPROTO;
00221                 goto err;
00222         }
00223 
00224         if ( nfs->pm_state == NFS_PORTMAP_MOUNTPORT ) {
00225                 DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (mount)\n", nfs );
00226 
00227                 rc = portmap_get_getport_reply ( &getport_reply, &reply );
00228                 if ( rc != 0 )
00229                         goto err;
00230 
00231                 rc = nfs_connect ( &nfs->mount_intf, getport_reply.port,
00232                                    nfs->hostname );
00233                 if ( rc != 0 )
00234                         goto err;
00235 
00236                 nfs->pm_state++;
00237                 nfs_pm_step ( nfs );
00238 
00239                 goto done;
00240         }
00241 
00242         if ( nfs->pm_state == NFS_PORTMAP_NFSPORT ) {
00243                 DBGC ( nfs, "NFS_OPEN %p got GETPORT reply (nfs)\n", nfs );
00244 
00245                 rc = portmap_get_getport_reply ( &getport_reply, &reply );
00246                 if ( rc != 0 )
00247                         goto err;
00248 
00249                 rc = nfs_connect ( &nfs->nfs_intf, getport_reply.port,
00250                                    nfs->hostname );
00251                 if ( rc != 0 )
00252                         goto err;
00253 
00254                 intf_shutdown ( &nfs->pm_intf, 0 );
00255                 nfs->pm_state++;
00256 
00257                 goto done;
00258         }
00259 
00260         rc = -EPROTO;
00261 err:
00262         nfs_done ( nfs, rc );
00263 done:
00264         free_iob ( io_buf );
00265         return 0;
00266 }
00267 
00268 static void nfs_mount_step ( struct nfs_request *nfs ) {
00269         int     rc;
00270 
00271         if ( ! xfer_window ( &nfs->mount_intf ) )
00272                 return;
00273 
00274         if ( nfs->mount_state == NFS_MOUNT_NONE ) {
00275                 DBGC ( nfs, "NFS_OPEN %p MNT call (%s)\n", nfs,
00276                        nfs_uri_mountpoint ( &nfs->uri ) );
00277 
00278                 rc = mount_mnt ( &nfs->mount_intf, &nfs->mount_session,
00279                                  nfs_uri_mountpoint ( &nfs->uri ) );
00280                 if ( rc != 0 )
00281                         goto err;
00282 
00283                 nfs->mount_state++;
00284                 return;
00285         }
00286 
00287         if ( nfs->mount_state == NFS_MOUNT_UMNT ) {
00288                 DBGC ( nfs, "NFS_OPEN %p UMNT call\n", nfs );
00289 
00290                 rc = mount_umnt ( &nfs->mount_intf, &nfs->mount_session,
00291                                  nfs_uri_mountpoint ( &nfs->uri ) );
00292                 if ( rc != 0 )
00293                         goto err;
00294         }
00295 
00296         return;
00297 err:
00298         nfs_done ( nfs, rc );
00299 }
00300 
00301 static int nfs_mount_deliver ( struct nfs_request *nfs,
00302                                struct io_buffer *io_buf,
00303                                struct xfer_metadata *meta __unused ) {
00304         int                     rc;
00305         struct oncrpc_reply     reply;
00306         struct mount_mnt_reply  mnt_reply;
00307 
00308         oncrpc_get_reply ( &nfs->mount_session, &reply, io_buf );
00309         if ( reply.accept_state != 0 )
00310         {
00311                 rc = -EPROTO;
00312                 goto err;
00313         }
00314 
00315         if ( nfs->mount_state == NFS_MOUNT_MNT ) {
00316                 DBGC ( nfs, "NFS_OPEN %p got MNT reply\n", nfs );
00317                 rc = mount_get_mnt_reply ( &mnt_reply, &reply );
00318                 if ( rc != 0 ) {
00319                         switch ( mnt_reply.status ) {
00320                                 case MNT3ERR_NOTDIR:
00321                                 case MNT3ERR_NOENT:
00322                                 case MNT3ERR_ACCES:
00323                                         break;
00324 
00325                                 default:
00326                                         goto err;
00327                         }
00328 
00329                         if ( ! strcmp ( nfs_uri_mountpoint ( &nfs->uri ),
00330                                         "/" ) )
00331                                 goto err;
00332 
00333                         if ( ( rc = nfs_uri_next_mountpoint ( &nfs->uri ) ) )
00334                                 goto err;
00335 
00336                         DBGC ( nfs, "NFS_OPEN %p MNT failed retrying with " \
00337                                "%s\n", nfs, nfs_uri_mountpoint ( &nfs->uri ) );
00338 
00339                         nfs->mount_state--;
00340                         nfs_mount_step ( nfs );
00341 
00342                         goto done;
00343                 }
00344 
00345                 nfs->current_fh = mnt_reply.fh;
00346                 nfs->nfs_state = NFS_LOOKUP;
00347                 nfs_step ( nfs );
00348 
00349                 goto done;
00350         }
00351 
00352         if ( nfs->mount_state == NFS_MOUNT_UMNT ) {
00353                 DBGC ( nfs, "NFS_OPEN %p got UMNT reply\n", nfs );
00354                 nfs_done ( nfs, 0 );
00355 
00356                 goto done;
00357         }
00358 
00359         rc = -EPROTO;
00360 err:
00361         nfs_done ( nfs, rc );
00362 done:
00363         free_iob ( io_buf );
00364         return 0;
00365 }
00366 
00367 static void nfs_step ( struct nfs_request *nfs ) {
00368         int     rc;
00369         char    *path_component;
00370 
00371         if ( ! xfer_window ( &nfs->nfs_intf ) )
00372                 return;
00373 
00374         if ( nfs->nfs_state == NFS_LOOKUP ) {
00375                 path_component = nfs_uri_next_path_component ( &nfs->uri );
00376 
00377                 DBGC ( nfs, "NFS_OPEN %p LOOKUP call (%s)\n", nfs,
00378                        path_component );
00379 
00380                 rc = nfs_lookup ( &nfs->nfs_intf, &nfs->nfs_session,
00381                                   &nfs->current_fh, path_component );
00382                 if ( rc != 0 )
00383                         goto err;
00384 
00385                 nfs->nfs_state++;
00386                 return;
00387         }
00388 
00389 
00390         if ( nfs->nfs_state == NFS_READLINK ) {
00391                 DBGC ( nfs, "NFS_OPEN %p READLINK call\n", nfs );
00392 
00393                 rc = nfs_readlink ( &nfs->nfs_intf, &nfs->nfs_session,
00394                                     &nfs->readlink_fh );
00395                 if ( rc != 0 )
00396                         goto err;
00397 
00398                 nfs->nfs_state++;
00399                 return;
00400         }
00401 
00402         if ( nfs->nfs_state == NFS_READ ) {
00403                 DBGC ( nfs, "NFS_OPEN %p READ call\n", nfs );
00404 
00405                 rc = nfs_read ( &nfs->nfs_intf, &nfs->nfs_session,
00406                                 &nfs->current_fh, nfs->file_offset,
00407                                 NFS_RSIZE );
00408                 if ( rc != 0 )
00409                         goto err;
00410 
00411                 nfs->nfs_state++;
00412                 return;
00413         }
00414 
00415         return;
00416 err:
00417         nfs_done ( nfs, rc );
00418 }
00419 
00420 static int nfs_deliver ( struct nfs_request *nfs,
00421                          struct io_buffer *io_buf,
00422                          struct xfer_metadata *meta __unused ) {
00423         int                     rc;
00424         struct oncrpc_reply     reply;
00425 
00426         if ( nfs->remaining == 0 ) {
00427                 oncrpc_get_reply ( &nfs->nfs_session, &reply, io_buf );
00428                 if ( reply.accept_state != 0 ) {
00429                         rc = -EPROTO;
00430                         goto err;
00431                 }
00432         }
00433 
00434         if ( nfs->nfs_state == NFS_LOOKUP_SENT ) {
00435                 struct nfs_lookup_reply lookup_reply;
00436 
00437                 DBGC ( nfs, "NFS_OPEN %p got LOOKUP reply\n", nfs );
00438 
00439                 rc = nfs_get_lookup_reply ( &lookup_reply, &reply );
00440                 if ( rc != 0 )
00441                         goto err;
00442 
00443                 if ( lookup_reply.ent_type == NFS_ATTR_SYMLINK ) {
00444                         nfs->readlink_fh = lookup_reply.fh;
00445                         nfs->nfs_state   = NFS_READLINK;
00446                 } else {
00447                         nfs->current_fh = lookup_reply.fh;
00448 
00449                         if ( nfs->uri.lookup_pos[0] == '\0' )
00450                                 nfs->nfs_state = NFS_READ;
00451                         else
00452                                 nfs->nfs_state--;
00453                 }
00454 
00455                 nfs_step ( nfs );
00456                 goto done;
00457         }
00458 
00459         if ( nfs->nfs_state == NFS_READLINK_SENT ) {
00460                 char                      *path;
00461                 struct nfs_readlink_reply readlink_reply;
00462 
00463                 DBGC ( nfs, "NFS_OPEN %p got READLINK reply\n", nfs );
00464 
00465                 rc = nfs_get_readlink_reply ( &readlink_reply, &reply );
00466                 if ( rc != 0 )
00467                         goto err;
00468 
00469                 if ( readlink_reply.path_len == 0 )
00470                 {
00471                         rc = -EINVAL;
00472                         goto err;
00473                 }
00474 
00475                 if ( ! ( path = strndup ( readlink_reply.path,
00476                                           readlink_reply.path_len ) ) )
00477                 {
00478                         rc = -ENOMEM;
00479                         goto err;
00480                 }
00481 
00482                 nfs_uri_symlink ( &nfs->uri, path );
00483                 free ( path );
00484 
00485                 DBGC ( nfs, "NFS_OPEN %p new path: %s\n", nfs,
00486                        nfs->uri.path );
00487 
00488                 nfs->nfs_state = NFS_LOOKUP;
00489                 nfs_step ( nfs );
00490                 goto done;
00491         }
00492 
00493         if ( nfs->nfs_state == NFS_READ_SENT ) {
00494                 if ( nfs->remaining == 0 ) {
00495                         DBGC ( nfs, "NFS_OPEN %p got READ reply\n", nfs );
00496 
00497                         struct nfs_read_reply read_reply;
00498 
00499                         rc = nfs_get_read_reply ( &read_reply, &reply );
00500                         if ( rc != 0 )
00501                                 goto err;
00502 
00503                         if ( nfs->file_offset == 0 ) {
00504                                 DBGC2 ( nfs, "NFS_OPEN %p size: %llu bytes\n",
00505                                         nfs, read_reply.filesize );
00506 
00507                                 xfer_seek ( &nfs->xfer, read_reply.filesize );
00508                                 xfer_seek ( &nfs->xfer, 0 );
00509                         }
00510 
00511                         nfs->file_offset += read_reply.count;
00512                         nfs->remaining    = read_reply.count;
00513                         nfs->eof          = read_reply.eof;
00514                 }
00515 
00516                 size_t len = iob_len ( io_buf );
00517                 if ( len > nfs->remaining )
00518                         iob_unput ( io_buf, len - nfs->remaining );
00519 
00520                 nfs->remaining -= iob_len ( io_buf );
00521 
00522                 DBGC ( nfs, "NFS_OPEN %p got %zd bytes\n", nfs,
00523                        iob_len ( io_buf ) );
00524 
00525                 rc = xfer_deliver_iob ( &nfs->xfer, iob_disown ( io_buf ) );
00526                 if ( rc != 0 )
00527                         goto err;
00528 
00529                 if ( nfs->remaining == 0 ) {
00530                         if ( ! nfs->eof ) {
00531                                 nfs->nfs_state--;
00532                                 nfs_step ( nfs );
00533                         } else {
00534                                 intf_shutdown ( &nfs->nfs_intf, 0 );
00535                                 nfs->nfs_state++;
00536                                 nfs->mount_state++;
00537                                 nfs_mount_step ( nfs );
00538                         }
00539                 }
00540 
00541                 return 0;
00542         }
00543 
00544         rc = -EPROTO;
00545 err:
00546         nfs_done ( nfs, rc );
00547 done:
00548         free_iob ( io_buf );
00549         return 0;
00550 }
00551 
00552 /*****************************************************************************
00553  * Interfaces
00554  *
00555  */
00556 
00557 static struct interface_operation nfs_xfer_operations[] = {
00558         INTF_OP ( intf_close, struct nfs_request *, nfs_done ),
00559 };
00560 
00561 /** NFS data transfer interface descriptor */
00562 static struct interface_descriptor nfs_xfer_desc =
00563         INTF_DESC ( struct nfs_request, xfer, nfs_xfer_operations );
00564 
00565 static struct interface_operation nfs_pm_operations[] = {
00566         INTF_OP ( intf_close, struct nfs_request *, nfs_done ),
00567         INTF_OP ( xfer_deliver, struct nfs_request *, nfs_pm_deliver ),
00568         INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_pm_step ),
00569 };
00570 
00571 static struct interface_descriptor nfs_pm_desc =
00572         INTF_DESC ( struct nfs_request, pm_intf, nfs_pm_operations );
00573 
00574 static struct interface_operation nfs_mount_operations[] = {
00575         INTF_OP ( intf_close, struct nfs_request *, nfs_done ),
00576         INTF_OP ( xfer_deliver, struct nfs_request *, nfs_mount_deliver ),
00577         INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_mount_step ),
00578 };
00579 
00580 static struct interface_descriptor nfs_mount_desc =
00581         INTF_DESC ( struct nfs_request, mount_intf, nfs_mount_operations );
00582 
00583 static struct interface_operation nfs_operations[] = {
00584         INTF_OP ( intf_close, struct nfs_request *, nfs_done ),
00585         INTF_OP ( xfer_deliver, struct nfs_request *, nfs_deliver ),
00586         INTF_OP ( xfer_window_changed, struct nfs_request *, nfs_step ),
00587 };
00588 
00589 static struct interface_descriptor nfs_desc =
00590         INTF_DESC_PASSTHRU ( struct nfs_request, nfs_intf, nfs_operations,
00591                              xfer );
00592 
00593 /*****************************************************************************
00594  *
00595  * URI opener
00596  *
00597  */
00598 
00599 static int nfs_parse_uri ( struct nfs_request *nfs, const struct uri *uri ) {
00600         int     rc;
00601 
00602         if ( ! uri || ! uri->host || ! uri->path )
00603                 return -EINVAL;
00604 
00605         if ( ( rc = nfs_uri_init ( &nfs->uri, uri ) ) != 0 )
00606                 return rc;
00607 
00608         if ( ! ( nfs->hostname = strdup ( uri->host ) ) ) {
00609                 rc = -ENOMEM;
00610                 goto err_hostname;
00611         }
00612 
00613         DBGC ( nfs, "NFS_OPEN %p URI parsed: (mountpoint=%s, path=%s)\n",
00614                nfs, nfs_uri_mountpoint ( &nfs->uri), nfs->uri.path );
00615 
00616         return 0;
00617 
00618 err_hostname:
00619         nfs_uri_free ( &nfs->uri );
00620         return rc;
00621 }
00622 
00623 /**
00624  * Initiate a NFS connection
00625  *
00626  * @v xfer              Data transfer interface
00627  * @v uri               Uniform Resource Identifier
00628  * @ret rc              Return status code
00629  */
00630 static int nfs_open ( struct interface *xfer, struct uri *uri ) {
00631         int                     rc;
00632         struct nfs_request      *nfs;
00633 
00634         nfs = zalloc ( sizeof ( *nfs ) );
00635         if ( ! nfs )
00636                 return -ENOMEM;
00637 
00638         rc = nfs_parse_uri( nfs, uri );
00639         if ( rc != 0 )
00640                 goto err_uri;
00641 
00642         rc = oncrpc_init_cred_sys ( &nfs->auth_sys );
00643         if ( rc != 0 )
00644                 goto err_cred;
00645 
00646         ref_init ( &nfs->refcnt, nfs_free );
00647         intf_init ( &nfs->xfer, &nfs_xfer_desc, &nfs->refcnt );
00648         intf_init ( &nfs->pm_intf, &nfs_pm_desc, &nfs->refcnt );
00649         intf_init ( &nfs->mount_intf, &nfs_mount_desc, &nfs->refcnt );
00650         intf_init ( &nfs->nfs_intf, &nfs_desc, &nfs->refcnt );
00651 
00652         portmap_init_session ( &nfs->pm_session, &nfs->auth_sys.credential );
00653         mount_init_session ( &nfs->mount_session, &nfs->auth_sys.credential );
00654         nfs_init_session ( &nfs->nfs_session, &nfs->auth_sys.credential );
00655 
00656         DBGC ( nfs, "NFS_OPEN %p connecting to port mapper (%s:%d)...\n", nfs,
00657                nfs->hostname, PORTMAP_PORT );
00658 
00659         rc = nfs_connect ( &nfs->pm_intf, PORTMAP_PORT, nfs->hostname );
00660         if ( rc != 0 )
00661                 goto err_connect;
00662 
00663         /* Attach to parent interface, mortalise self, and return */
00664         intf_plug_plug ( &nfs->xfer, xfer );
00665         ref_put ( &nfs->refcnt );
00666 
00667         return 0;
00668 
00669 err_connect:
00670         free ( nfs->auth_sys.hostname );
00671 err_cred:
00672         nfs_uri_free ( &nfs->uri );
00673         free ( nfs->hostname );
00674 err_uri:
00675         free ( nfs );
00676         return rc;
00677 }
00678 
00679 /** NFS URI opener */
00680 struct uri_opener nfs_uri_opener __uri_opener = {
00681         .scheme = "nfs",
00682         .open   = nfs_open,
00683 };