summaryrefslogblamecommitdiff
path: root/nuttx/fs/nfs/nfs_vfsops.c
blob: 805b489670b0aac2d9ea1936bb886aaf56b05b0e (plain) (tree)
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399













































                                                                              

                         
                       


                       
 


                    
                  

                   


                   
                            
                        

                   

                       

                      



                      




                                                                              


                                                                           


                                                                              
 
                 
 

                       


                                                                             






















                                                                              
                                                        

                                                                              


                                    




                                                                             
              
                                                                              

                         

                                         
                                          
                                           


                                           
                                           
                                          













                                               
                                          
  





                                                                              
                   

                                                                              






                                                                              
 

                                                          

                                 
                   
                        

                         
                     

                             

                  


                     






                                                                       
                                        
                                      

                           
 




                                           
     
                                 

     

                        
                         
 
                                         
      
                                        
                                  
                                 
                                                




                                                                
 

                                                    
 



                                     
                                                                 




                                                                               
 










                                                                      
 
                                                    
                                   
                                    
                                            



                                                                                 
 
                                                                   
 
                             
 










                                                                            
         
                                                 
             
                         
             
         
 
                              
     
      
     
                                                             
         



                                                       
                                 
         



                                      
         
     







                                   
 
 
        



                                                                              
                                                 
 

                       

                

                     


                                                               





                                                              







                              

               
 
      
 



                                                                              
                                                                             
 

                       
                        

                  
                  


                                         


                
           








                                                                



                                                     



                                                 
 








                                                 
     





                                                                         
                                                   

     
                                                

                                        
               









                                                              




                                   

       
                                      



                                  
 
                                                              




                                     


                                              
         


                                                                   
         
          
         
                     
         
 
                 



                      
               

 


                                                                              
 
              
                                                                     
 
                      

                       
                          
                  
                           
                            
                                          
                
                   
          
                           

                                      
                     




                                                               



                                               







                                                 
     








                                                              

     

                      
     



                                 
 





                                          
 




                                                            
 






                                          
 

                                       
 
                                                                        
 








                                              
 







                                                                              
     
 




                                          

                      
               

 
                                                                             
                    
  
                                             

                                                                              
 
                                                                       
 
                
                                
          
                       
                     
                   
                           









                                                             
                    
                         









                                                   
 






                                               
 
                                              
 
                                    
     
                                  
                          
               


                                                                         
     
                                            
     
 
                                             
 


                                 
     
  
                    

                                    
                       
               

     
                      
                   
               

 




                                                                              
 
                                                                                                              
 
                
                  

                               
 

                                                           
                                  

     
                                   

                                         


                                                
         



                                              


          



                                                                         
         
 
                                                                    


                


                       












                                                                      
 
                                           
 
                                    
  

                                    

                        
                          
                 
                                                            

                  


                                                            
 
                                                                         
             
 
                                               
         
          

     
                                                     
 
                                    
     
                                                             





                                                            
                      
     

        
               

 
                                                                             






















































































































































































































































                                                                                          




                                                                              
 
                                                                 

                                
                       
                
                 
                  
                            




                                             
 

                                                                
                                             










                                                   
 



                                        
 
                                                        
     
                                            

     
                                    

                                                              
            



                                 
                                       
                                             
              


                                          
                                                       
                                          
                                                      






                                                              
                                 









                                                             
 

                      
               

 







                                                                              
 














































































































































































































































































































                                                                                    
 
                                             
 
                                           
 





                                 
 



                                                              

            
                                  

     
                                         
                                              

                                                 



                                                                        






                                                 
 

                                                 



                                                                        




                                                 


                              
     
 

                                                 



                                                                              



                                                       


                                    
     
 






                                                              

                                   

                      
               

 




                                                                              
 


                  
                       




                                                         
 







                                                                               




                                                      


                                       
                                            


                                       


                                                          



                                                        
                                    


                                                               



                                     


                                   
          


                              

      


                            









                                                      



                                        

                                          
 
                            



                            
                               


                               









                                                      



                                        

                                          
 
                            



                            
                               


                               








                                                                  


                                              

                                      


                                          

                                  


                                  


                                                                  



                                           

                                                                 



                                          


                                                           


                                    
          


                                            
     
 


                                                           


                                    
          


                                            
     
 
                                          


                                          



                                                           


                                    
          


                                            
     
 


                                                           


                                    
          


                                            
     
 
                                          


                                          




                                       
                                
           
                                                  

           

 
        





                                                                              
 
                                 
 


                       
                
 







                                                               
                           

                           
 








                                                 

                                                           


                                                 
     
                                                 
     
 
               



                      
 
      
/****************************************************************************
 * fs/nfs/nfs_vfsops.c
 *
 *   Copyright (C) 2012 Gregory Nutt. All rights reserved.
 *   Copyright (C) 2012 Jose Pablo Rojas Vargas. All rights reserved.
 *   Author: Jose Pablo Rojas Vargas <jrojas@nx-engineering.com>
 *
 * Leveraged from OpenBSD:
 *
 *   Copyright (c) 1989, 1993, 1995
 *   The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Rick Macklem at The University of Guelph.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>
 
#include <sys/socket.h>
#include <sys/statfs.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <queue.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/fs/dirent.h>
#include <nuttx/fs/fs.h>

#include <net/if.h>
#include <netinet/in.h>

#include "rpc_v2.h"
#include "nfs_proto.h"
#include "nfs_node.h"
#include "nfs.h"
#include "nfs_mount.h"
#include "xdr_subs.h"

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define NFS_DIRHDSIZ         (sizeof (struct nfs_dirent) - (MAXNAMLEN + 1))
#define NFS_DIRENT_OVERHEAD  offsetof(struct nfs_dirent, dirent)

/****************************************************************************
 * Private Type Definitions
 ****************************************************************************/
 
struct nfs_dirent
{
  uint32_t cookie[2];
  struct dirent dirent;
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

static int     nfs_open(FAR struct file *filep, const char *relpath,
                        int oflags, mode_t mode);
static ssize_t nfs_read(FAR struct file *filep, char *buffer, size_t buflen);
static ssize_t nfs_write(FAR struct file *filep, const char *buffer,
                         size_t buflen);
static int     nfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir);
static int     nfs_mount(FAR struct inode *blkdriver, const void *data,
                         void **handle);
static int     nfs_unmount(void *handle, FAR struct inode **blkdriver);
static int     nfs_statfs(struct inode *mountpt, struct statfs *buf);
static int     nfs_remove(struct inode *mountpt, const char *relpath);
static int     nfs_mkdir(struct inode *mountpt, const char *relpath,
                         mode_t mode);
static int     nfs_rmdir(struct inode *mountpt, const char *relpath);
static int     nfs_rename(struct inode *mountpt, const char *oldrelpath,
                          const char *newrelpath);
static int     nfs_fsinfo(struct inode *mountpt, const char *relpath,
                          struct stat *buf);

/****************************************************************************
 *  External Public Data  (this belong in a header file)
 ****************************************************************************/

extern uint32_t nfs_true, nfs_false;
extern uint32_t nfs_xdrneg1;
extern nfstype nfsv3_type[8];
extern struct nfsstats nfsstats;
extern int nfs_ticks;
extern uint32_t nfs_procids[NFS_NPROCS];

/****************************************************************************
 * Public Data
 ****************************************************************************/
/* nfs vfs operations. */

const struct mountpt_operations nfs_ops =
{
  nfs_open,                     /* open */
  NULL,                         /* close */
  nfs_read,                     /* read */
  nfs_write,                    /* write */
  NULL,                         /* seek */
  NULL,                         /* ioctl */
  NULL,                         /* sync */

  NULL,                         /* opendir */
  NULL,                         /* closedir */
  nfs_readdir,                  /* readdir */
  NULL,                         /* rewinddir */

  nfs_mount,                    /* bind */
  nfs_unmount,                  /* unbind */
  nfs_statfs,                   /* statfs */

  nfs_remove,                   /* unlink */
  nfs_mkdir,                    /* mkdir */
  nfs_rmdir,                    /* rmdir */
  nfs_rename,                   /* rename */
  nfs_fsinfo                    /* stat */
};

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Public Functions
 ****************************************************************************/

 /****************************************************************************
 * Name: nfs_open
 *
 * Description: if oflags == O_CREAT it creates a file, if not it
 * check to see if the type is ok and that deletion is not in progress.
 ****************************************************************************/
 

static int
nfs_open(FAR struct file *filep, FAR const char *relpath, 
         int oflags, mode_t mode)
{
  struct inode *in;
  struct nfs_fattr *vap;
  struct nfsv3_sattr *sp;
  struct nfsmount *nmp;
  struct nfsnode *np;
  struct CREATE3args *create;
  struct CREATE3resok *resok;
  void *replydata;
  int error = 0;

  /* Sanity checks */

  DEBUGASSERT(filep->f_inode != NULL);

  /* Get the mountpoint inode reference from the file structure and the
   * mountpoint private data from the inode structure
   */

  in = filep->f_inode;
  nmp = (struct nfsmount*)in->i_private;
  np = (struct nfsnode*)filep->f_priv;

  DEBUGASSERT(nmp != NULL);

  /* Check if the mount is still healthy */

  nfs_semtake(nmp);
  error = nfs_checkmount(nmp);
  if (error != 0)
    {
      goto errout_with_semaphore;
    }

  if (oflags == O_CREAT)
    {
      /* Sanity checks */

      DEBUGASSERT(filep->f_priv == NULL);
again:
      nfsstats.rpccnt[NFSPROC_CREATE]++;
      vap = nmp->nm_head->n_fattr;
      sp->sa_modetrue = nfs_true;
      sp->sa_mode = txdr_unsigned(vap->fa_mode);
      sp->sa_uid = nfs_xdrneg1;
      sp->sa_gid = nfs_xdrneg1;
      sp->sa_size = nfs_xdrneg1;
      sp->sa_atimetype = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
      sp->sa_mtimetype = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);

      txdr_nfsv3time(&vap->fa_atime, &sp->sa_atime);
      txdr_nfsv3time(&vap->fa_mtime, &sp->sa_mtime);

      create->how = sp;
      create->where->dir= nmp->nm_fh;
      create->where->name = relpath;

      error = nfs_request(in, NFSPROC_CREATE, create, replydata);
      if (!error)
        {
          /* Create an instance of the file private data to describe the opened
           * file.
           */

          np = (struct nfsnode *)zalloc(sizeof(struct nfsnode));
          if (!np)
            {
              fdbg("Failed to allocate private data\n", error);
              error = -ENOMEM;
              goto errout_with_semaphore;
            }

          /* Initialize the file private data (only need to initialize
           * non-zero elements)
           */

          resok = (struct CREATE3resok *) replydata;
          np->n_open        = true;
          np->nfsv3_type    = NFREG;
          np->n_fhp         = resok->handle;
          np->n_size        = (struct uint64_t *)resok->attributes->nfsv3fa_size;
          np->n_fattr       = resok->attributes;
          np->n_mtime       = (struct timespec*) resok->attributes->nfsv3fa_mtime
          np->n_ctime       = (struct time_t*) resok->attributes->nfsv3fa_ctime

          /* Attach the private date to the struct file instance */

          filep->f_priv = np;

          /* Then insert the new instance into the mountpoint structure.
           * It needs to be there (1) to handle error conditions that effect
           * all files, and (2) to inform the umount logic that we are busy
           * (but a simple reference count could have done that).
           */

          np->n_next = nmp->nm_head;
          nmp->nm_head = np->n_next;
          error = 0;
        }
      else
        {
          if (info_v3 && error == NFSERR_NOTSUPP)
            {
              goto again;
            }
        }

      np->n_flag |= NMODIFIED;
    }
  else
    {
      if (np->nfsv3_type != NFREG && np->nfsv3_type != NFDIR)
        {
          fdbg("open eacces typ=%d\n", np->nfsv3_type);
          return EACCES;
        }

      if (np->n_flag & NMODIFIED)
        {
          if (np->nfsv3_type == NFDIR)
            {
              np->n_direofoffset = 0;
            }
        }
    }

  /* For open/close consistency. */

  NFS_INVALIDATE_ATTRCACHE(np);

errout_with_semaphore:
  nfs_semgive(nmp);
  return error;
}

#ifdef 0
/****************************************************************************
 * Name: nfs_close
 ****************************************************************************/

static int nfs_close(FAR struct file *filep) done
{
  struct nfsmount *nmp;
  struct nfsnode *np;
  int error = 0;

  fvdbg("Closing\n");

  /* Sanity checks */

  DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);

  /* Recover our private data from the struct file instance */

  np = filep->f_priv;
  nmp = filep->f_inode->i_private;

  DEBUGASSERT(nmp != NULL);

  if (np->nfsv3_type == NFREG)
    {
      error = nfs_sync(filep);
      kfree(np);
      filep->f_priv = NULL;
    }

  return error;
}
#endif

/****************************************************************************
 * Name: nfs_read
 ****************************************************************************/

static ssize_t nfs_read(FAR struct file *filep, char *buffer, size_t buflen) 
{
  struct nfsmount *nmp;
  struct nfsnode *np;
  unsigned int readsize;
  int bytesleft;
  uint64_t offset;
  void *datareply;
  struct READ3args *read;
  struct READ3resok *resok;
  uint8_t *userbuffer = (uint8_t*)buffer;
  int info_v3;
  int error = 0;
  int len;
  bool eof;

  fvdbg("Read %d bytes from offset %d\n", buflen, filep->f_pos);

  /* Sanity checks */

  DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);

  /* Recover our private data from the struct file instance */

  np = (struct nfsnode*) filep->f_priv;
  nmp = (struct nfsmount*) filep->f_inode->i_private;
  eof = false;
  offset = 0;

  DEBUGASSERT(nmp != NULL);

  /* Make sure that the mount is still healthy */

  nfafs_semtake(nmp);
  error = nfs_checkmount(nmp);
  if (error != 0)
    {
      fdbg("nfs_checkmount failed: %d\n", error);
      goto errout_with_semaphore;
    }

  if (np->nfsv3_type != NFREG)
    {
      fdbg("read eacces typ=%d\n", np->nfsv3_type);
      return EACCES;
    }

  if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3)
    {
      (void)nfs_fsinfo(filep->f_inode, NULL, NULL);
    }

  /* Get the number of bytes left in the file */

  bytesleft = np->n_size - filep->f_pos;
  readsize = 0;

  /* Truncate read count so that it does not exceed the number
   * of bytes left in the file.
   */

  if (buflen > bytesleft)
    {
      buflen = bytesleft;
    }

    len = nmp->nm_rsize;
    if (len < buflen)
      {
        error = EFBIG
        goto errout_with_semaphore;
      }

      nfsstats.rpccnt[NFSPROC_READ]++;
again:
      read->file = np->nfsv3_type;
      read->count = buflen;
      read->offset = offset;

      error = nfs_request(nmp, NFSPROC_READ, read, datareply);
      if (error)
        {
          goto errout_with_semaphore;
        }

      resok = (struct READ3resok *) datareply;
      eof = resok->eof;
      if (eof == true)
        {
          readsize = resok->count;
          np->n_fattr = resok->file_attributes;
          memcpy(userbuffer, resok->data, (struct size_t)readsize);
        }
      else
        {
          goto again;
        }

nfs_semgive(nmp);
  return readsize;

errout_with_semaphore:
  nfs_semgive(nmp);
  return error;
}

/****************************************************************************
 * Name: nfs_write
 ****************************************************************************/

static ssize_t
nfs_write(FAR struct file *filep, const char *buffer, size_t buflen) 
{
  struct inode *inode;
  struct nfsmount *nmp;
  struct nfsnode *np;
  unsigned int  writesize;
  void *datareply;
  struct WRITE3args *write;
  struct WRITE3resok *resok;
  uint8_t  *userbuffer = (uint8_t*)buffer;
  int error = 0;
  uint64_t  offset;
  int len;
  struct stable_how commit;
  int committed = NFSV3WRITE_FILESYNC;

  /* Sanity checks */

  DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);

  /* Recover our private data from the struct file instance */

  np     = (struct nfsnode *)filep->f_priv;
  inode  = filep->f_inode;
  nmp    = (struct nfsmount *)inode->i_private;
  offset = 0;

  DEBUGASSERT(nmp != NULL);

  /* Make sure that the mount is still healthy */

  nfs_semtake();
  error = nfs_checkmount(nmp);
  if (error != 0)
    {
      goto errout_with_semaphore;
    }

  /* Check if the file size would exceed the range of off_t */

  if (np->n_size + buflen < np->n_size)
    {
      ret = -EFBIG;
      goto errout_with_semaphore;
    }

  len = nmp->nm_wsize;
  if (len < buflen)
    {
      error = -EFBIG
      goto errout_with_semaphore;
    }
  writesize = 0;

  nfsstats.rpccnt[NFSPROC_WRITE]++;
  write->file = np->nfsv3_type;
  write->offset = offset;
  write->count = buflen;
  write->stable = committed;
  memcpy(write->data, userbuffer, buflen);

  error = nfs_request(nmp, NFSPROC_WRITE, write, datareply);
  if (error)
    {
      goto errout_with_semaphore;
    }

  resok = (struct WRITE3resok *)datareply;
  writesize = resok->count;
  if (writesize == 0)
    {
       error = -NFSERR_IO;
       goto errout_with_semaphore;
    }

  commit = resok->committed;
  np->n_fattr = resok->file_wcc->after;

  /* Return the lowest committment level obtained by any of the RPCs. */

  if (committed == NFSV3WRITE_FILESYNC)
    {
      committed = commit;
    }
  else if (committed == NFSV3WRITE_DATASYNC &&
           commit == NFSV3WRITE_UNSTABLE)
    {
       committed = commit;
    }

  if ((nmp->nm_flag & NFSMNT_HASWRITEVERF) == 0)
    {
      bcopy((void) resok->verf, (void) nmp->nm_verf, NFSX_V3WRITEVERF);
      nmp->nm_flag |= NFSMNT_HASWRITEVERF;
    }
  else if (strncmp((char) resok->verf, (char) nmp->nm_verf, NFSX_V3WRITEVERF))
    {
      bcopy((void) resok->verf, (void) nmp->nm_verf, NFSX_V3WRITEVERF);
    }

  np->n_mtime = np->n_fattr.nfsv3fa_mtime;

  nfs_semgive(nmp);
  return writesize;

errout_with_semaphore:
  nfs_semgive(nmp);
  return error;
}

/****************************************************************************
 * Name: nfs_readdir
 *
 * Description: Read the next directory entry
 *
 ****************************************************************************/

static int nfs_readdir(struct inode *mountpt, struct fs_dirent_s *dir) 
{
  int error = 0;
  unsigned long *cookies = NULL;
  int cnt;
  struct nfsmount *nmp;
  struct nfsnode *np;
  bool eof = false;
  //struct nfs_dirent *ndp;

  fvdbg("Entry\n");

  /* Sanity checks */

  DEBUGASSERT(mountpt != NULL && mountpt->i_private != NULL);

  /* Recover our private data from the inode instance */

  nmp = mountpt->i_private;
  np  = np->nm_head;
  dir->fd_root = mountpt;

  /* Make sure that the mount is still healthy */

  nfs_semtake(nmp);
  error = nfs_checkmount(nmp);
  if (error != 0)
    {
      fdbg("romfs_checkmount failed: %d\n", error);
      goto errout_with_semaphore;
    }

  if (np->nfsv3_type != NFDIR)
    {
      error = EPERM;
      goto errout_with_semaphore;
    }

  dir->u.nfs.nd_direoffset = np->nd_direoffset;

  /* First, check for hit on the EOF offset */

  if (dir->u.nfs.nd_direoffset != 0)
    {
      nfsstats.direofcache_hits++;
      //np->n_open = true;
      return 0;
    }

  if ((nmp->nm_flag & (NFSMNT_NFSV3 | NFSMNT_GOTFSINFO)) == NFSMNT_NFSV3)
    {
      (void)nfs_fsinfo(mountpt, NULL, NULL);
    }

  error = nfs_readdirrpc(nmp, np, &eof, dir);

  if (error == NFSERR_BAD_COOKIE)
    {
      error = EINVAL;
    }
  
  if (!error && eof)
    {
      nfsstats.direofcache_misses++;
      nfs_semgive(nmp);
      return 0;
    }

errout_with_semaphore:
  nfs_semgive(nmp);
  return error;
}

/****************************************************************************
 * Name: nfs_readdirrpc
 *
 * Description: The function below stuff the cookies in after the name.
 ****************************************************************************/

static int nfs_readdirrpc(struct nfsmount *nmp, struct nfsnode *np, bool *end_of_directory, fs_dirent_s *dir) 
{
  int error = 0;
  void *datareply;
  struct READDIR3args *readdir;
  struct READDIR3resok *resok;

  /* Loop around doing readdir rpc's of size nm_readdirsize
   * truncated to a multiple of NFS_READDIRBLKSIZ.
   * The stopping criteria is EOF.
   */

  while (end_of_directory == false)
    {
      nfsstats.rpccnt[NFSPROC_READDIR]++;
      readdir->dir = np->n_fhp;
      readdir->count = nmp->nm_readdirsiz;
      if (nfsstats.rpccnt[NFSPROC_READDIR] == 1)
        {
          readdir->cookie.nfsuquad[0] = 0;
          readdir->cookie.nfsuquad[1] = 0;
          readdir->cookieverf.nfsuquad[0] = 0;
          readdir->cookieverf.nfsuquad[1] = 0;
        }
      else
        {
          readdir->cookie.nfsuquad[0] = dir->u.nfs.cookie[0];
          readdir->cookie.nfsuquad[1] = dir->u.nfs.cookie[1];
          readdir->cookieverf.nfsuquad[0] = np->n_cookieverf.nfsuquad[0];
          readdir->cookieverf.nfsuquad[1] = np->n_cookieverf.nfsuquad[1];
        }

      error = nfs_request(nmp, NFSPROC_READDIR, readdir, datareply);

      if (error)
        {
          goto nfsmout;
        }

     resok = (void READDIR3resok*) datareply;
     np->n_fattr = resok->dir_attributes;
     np->nd_cookieverf.nfsuquad[0] = resok->cookieverf.nfsuquad[0];
     np->nd_cookieverf.nfsuquad[1] = resok->cookieverf.nfsuquad[1];
     dir->fd_dir->d_type = resok->reply->entries->fileid;
     dir->fd_dir->d_name = resok->reply->entries->name;
     dir->u.nfs.cookie[0] = resok->reply->entries->cookie.nfsuquad[0];
     dir->u.nfs.cookie[1] = resok->reply->entries->cookie.nfsuquad[1];
     
     if(resok->reply->eof == true)
      {
        end_of_directory = true;        
      }

     //more_dirs = fxdr_unsigned(int, *dp);

      /* loop thru the dir entries*/
/*
      while (more_dirs && bigenough)
        {
          if (bigenough)
            {
              if (info_v3)
                {
                  dir->u.nfs.cookie[0] = cookie.nfsuquad[0];
                }
              else
                {
                  dir->u.nfs.cookie[0] = ndp->cookie[0] = 0;
                }

              dir->u.nfs.cookie[1] = ndp->cookie[1] = cookie.nfsuquad[1];
            }

          more_dirs = fxdr_unsigned(int, *ndp);
        }
        */
    }

  /* We are now either at the end of the directory */

  if (resok->reply->entries == NULL)
    {
      np->n_direofoffset = fxdr_hyper(&dir->u.nfs.cookie[0]);

      /* We signal the end of the directory by returning the
       * special error -ENOENT
       */

      fdbg("End of directory\n");
      error = -ENOENT;
    }

nfsmout:
  return error;
}

/****************************************************************************
 * Name: nfs_mount
 *
 * Description: This implements a portion of the mount operation. This
 *  function allocates and initializes the mountpoint private data and
 *  binds the blockdriver inode to the filesystem private data.  The final
 *  binding of the private data (containing the blockdriver) to the
 *  mountpoint is performed by mount().
 *
 ****************************************************************************/

static int nfs_mount(struct inode *blkdriver, void *data, void **handle) 
{
  int error;
  struct nfs_args args;
  struct sockaddr *nam;
  nfsfh_t nfh[NFSX_V3FHMAX];

  bcopy(data, &args, sizeof(args.version));
  if (args.version == 3)
    {
      bcopy(data, &args, sizeof(struct nfs_args3));
      args.flags &= ~(NFSMNT_INTERNAL | NFSMNT_NOAC);
    }
  else if (args.version == NFS_ARGSVERSION)
    {
      error = copyin(data, &args, sizeof(struct nfs_args));
      args.flags &= ~NFSMNT_NOAC;
    }
  else
    {
      return EPROGMISMATCH;
    }

  if ((args.flags & (NFSMNT_NFSV3 | NFSMNT_RDIRPLUS)) == NFSMNT_RDIRPLUS)
    {
      return EINVAL;
    }

  if (blkdriver->mnt_flag & MNT_UPDATE)
    {
      struct nfsmount *nmp = (struct nfsmount*)blkdriver->i_private;

      if (nmp == NULL)
        {
          return EIO;
        }

      /* When doing an update, we can't change from or to v3. */

      args.flags = (args.flags & ~(NFSMNT_NFSV3)) |
        (nmp->nm_flag & (NFSMNT_NFSV3));
      nfs_decode_args(nmp, &args);
      return 0;
    }

  if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX)
    {
      return EINVAL;
    }

  bcopy(args.fh, nfh, args.fhsize);
  bcopy(args.addr, nam, sizeof(args.addr));
  args.fh = nfh;
  error = mountnfs(&args, blkdriver, nam);
  return error;
}

/****************************************************************************
 * Name: mountnfs
 *
 * Description: Common code for nfs_mount.
 *
 ****************************************************************************/

static int mountnfs(struct nfs_args *argp, struct inode *blkdriver,
             struct sockaddr *nam, void **handle) 
{
  struct nfsmount *nmp;
  int error;

  if (blkdriver->mnt_flag & MNT_UPDATE)
    {
      nmp = (struct nfsmount*)blkdriver->i_private;

      /* update paths, file handles, etc, here XXX */

      return 0;
    }
  else
    {
      /* Open the block driver */

      if (!blkdriver || !blkdriver->u.i_bops)
        {
          fdbg("No block driver/ops\n");
          return -ENODEV;
        }

      if (blkdriver->u.i_bops->open &&
          blkdriver->u.i_bops->open(blkdriver) != OK)
        {
          fdbg("No open method\n");
          return -ENODEV;
        }

      /* Create an instance of the mountpt state structure */

      nmp = (struct nfsmount*)zalloc(sizeof(struct nfmount));
      if (!nmp)
        {
          fdbg("Failed to allocate mountpoint structure\n");
          return -ENOMEM;
        }

      /* Initialize the allocated mountpt state structure.  The filesystem is
       * responsible for one reference ont the blkdriver inode and does not
       * have to addref() here (but does have to release in ubind().
       */

      sem_init(&rm->rm_sem, 0, 0);     /* Initialize the semaphore that controls access */

    //vfs_getnewfsid(mp);
      nmp->nm_blkdriver = blkdriver;          /* Save the block driver reference */
      nmp->nm_timeo = NFS_TIMEO;
      nmp->nm_retry = NFS_RETRANS;
      nmp->nm_wsize = NFS_WSIZE;
      nmp->nm_rsize = NFS_RSIZE;
      nmp->nm_readdirsize = NFS_READDIRSIZE;
      nmp->nm_numgrps = NFS_MAXGRPS;
      nmp->nm_readahead = NFS_DEFRAHEAD;
      nmp->nm_fhsize = argp->fhsize;
      nmp->nm_acregmin = NFS_MINATTRTIMO;
      nmp->nm_acregmax = NFS_MAXATTRTIMO;
      nmp->nm_acdirmin = NFS_MINATTRTIMO;
      nmp->nm_acdirmax = NFS_MAXATTRTIMO;
      memmove(nmp->nm_fh, argp->fh, argp->fhsize);
    //strncpy(&mp->mnt_stat.f_fstypename[0], mp->mnt_vfc->vfc_name, MFSNAMELEN);
    //memmove(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
     // bcopy(pth, nmp->nm_mntonname, 90);
    //memmove(argp, &mp->mnt_stat.mount_info.nfs_args, sizeof(*argp));
      nmp->nm_nam = nam;
      nfs_decode_args(nmp, argp);

      /* Set up the sockets and per-host congestion */

      nmp->nm_sotype = argp->sotype;
      nmp->nm_soproto = argp->proto;

      /* For Connection based sockets (TCP,...) defer the connect until
       * the first request, in case the server is not responding.
       */

      if (nmp->nm_sotype == SOCK_DGRAM && (error = nfs_connect(nmp)))
        {
          goto bad;
        }

      /* Mounted! */

      nmp->nfs_mounted = true;
      nfs_init();
      *handle = blkdriver->i_private = &nmp;
      nfs_semgive(nmp);

      return 0;
  }

bad:
  nfs_disconnect(nmp);
  sem_destroy(&nmp->nm_sem);
  kfree(nmp);
  return error;
}

/****************************************************************************
 * Name: nfs_unmount
 *
 * Description: This implements the filesystem portion of the umount
 *   operation.
 *
 ****************************************************************************/

int nfs_unmount(struct inode *blkdriver, void *handle) 
{
  struct nfsmount *nmp = (struct nfsmount*) handle ;
  int error;

  fvdbg("Entry\n");

  if (!nmp)
    {
      return -EINVAL;
    }

  nfs_semtake(nmp)
  if (nmp->nm_head)
    {
      /* We cannot unmount now.. there are open files */

      error = -EBUSY;
    }
  else
    {
       /* Unmount ... close the block driver */

      if (nmp->nm_blkdriver)
        {
          struct inode *inode = nmp->nm_blkdriver;
          if (inode)
            {
              if (inode->u.i_bops && inode->u.i_bops->close)
                {
                  (void)inode->u.i_bops->close(inode);
                }

              /* We hold a reference to the block driver but should
               * not but mucking with inodes in this context.  So, we will just return
               * our contained reference to the block driver inode and let the umount
               * logic dispose of it.
               */

              if (blkdriver)
                {
                  *blkdriver = inode;
                }
            }
        }

      /* Release the mountpoint private data */

      if (blkdriver->i_private)
        {
          kfree(blkdriver->nm_buffer);
        }

      nfs_disconnect(nmp);
      sem_destroy(&nmp->nm_sem);
      kfree(nmp);

      return 0;
    }

  nfs_semgive(nmp)
  return 0;
}

/****************************************************************************
 * Name: nfs_statfs
 *
 * Description: Return filesystem statistics
 *
 ****************************************************************************/

static int nfs_statfs(struct inode *mountpt, struct statfs *sbp) 
{
  struct nfs_statfs *sfp = NULL;
  struct nfsmount *nmp;
  int error = 0;
  uint64_t tquad;
  void *datareply;
  struct FSSTAT3args *fsstat
  int info_v3;

  /* Sanity checks */

  DEBUGASSERT(mountpt && mountpt->i_private);

  /* Get the mountpoint private data from the inode structure */

  nmp = (struct nfsmount*)mountpt->i_private;
  info_v3 = (nmp->nm_flag & NFSMNT_NFSV3);

  /* Check if the mount is still healthy */

  nfs_semtake(nmp);
  error = nfs_checkmount(nmp);
  if (error < 0)
    {
      fdbg("romfs_checkmount failed: %d\n", error);
      goto errout_with_semaphore;
    }

  /* Fill in the statfs info */

  memset(sbp, 0, sizeof(struct statfs));
  sbp->f_type = NFS_SUPER_MAGIC;

  if (info_v3 && (nmp->nm_flag & NFSMNT_GOTFSINFO) == 0)
    {
      (void)nfs_fsinfo(mountpt, NULL, NULL);
    }

  nfsstats.rpccnt[NFSPROC_FSSTAT]++;
  fsstat->fsroot = nmp->nm_fh;
  error = nfs_request(nmp, NFSPROC_FSSTAT, fsstat, datareply);
  if (error)
    {
      goto errout_with_semaphore;
    }

  sfp = (struct nfs_statfs *)datareply;
  nmp->nm_head->n_fattr = sfp->obj_attributes
  if (info_v3)
    {
      sbp->f_bsize = NFS_FABLKSIZE;
      tquad = fxdr_hyper(&sfp->sf_tbytes);
      sbp->f_blocks = tquad / (uint64_t) NFS_FABLKSIZE;
      tquad = fxdr_hyper(&sfp->sf_fbytes);
      sbp->f_bfree = tquad / (uint64_t) NFS_FABLKSIZE;
      tquad = fxdr_hyper(&sfp->sf_abytes);
      sbp->f_bavail = (quad_t) tquad / (quad_t) NFS_FABLKSIZE;

      tquad = fxdr_hyper(&sfp->sf_tfiles);
      sbp->f_files = tquad;
      tquad = fxdr_hyper(&sfp->sf_ffiles);
      sbp->f_ffree = tquad;
      sbp->f_namelen = MAXNAMLEN;
    }
  else
    {
      sbp->f_bsize = fxdr_unsigned(int32_t, sfp->sf_bsize);
      sbp->f_blocks = fxdr_unsigned(int32_t, sfp->sf_blocks);
      sbp->f_bfree = fxdr_unsigned(int32_t, sfp->sf_bfree);
      sbp->f_bavail = fxdr_unsigned(int32_t, sfp->sf_bavail);
      sbp->f_files = 0;
      sbp->f_ffree = 0;
    }

errout_with_semaphore:
  nfs_semgive(nmp);
  return error;
}

/****************************************************************************
 * Name: nfs_remove
 *
 * Description: Remove a file
 *
 ****************************************************************************/

static int nfs_remove(struct inode *mountpt, const char *relpath)
{
  struct nsfmount *nmp;
  struct nfsnode *np;
  void *datreply;
  struct REMOVE3args *remove;
  struct REMOVE3resok *resok;
  int error = 0;

  /* Sanity checks */

  DEBUGASSERT(mountpt && mountpt->i_private);

  /* Get the mountpoint private data from the inode structure */

  nmp = (struct nfsmount *)mountpt->i_private;
  np = nmp->nm_head;

  /* Check if the mount is still healthy */

  nfs_semtake(nmp);
  error = fat_checkmount(nmp);
  if (error == 0)
    {
      /* If the file is open, the correct behavior is to remove the file
       * name, but to keep the file cluster chain in place until the last
       * open reference to the file is closed.
       */

      /* Remove the file */

      if (np->n_type != NFREG)
        {
          error = EPERM;
          goto errout_with_semaphore;
        }

      /* Do the rpc */

      nfsstats.rpccnt[NFSPROC_REMOVE]++;
      remove->object->dir = np->n_fhp;
      remove->object->name = relpath;

      error = nfs_request(nmp, NFSPROC_REMOVE, remove, datareply);

      /* Kludge City: If the first reply to the remove rpc is lost..
       *   the reply to the retransmitted request will be ENOENT
       *   since the file was in fact removed
       *   Therefore, we cheat and return success.
       */

      if (error == ENOENT)
        {
          error = 0;
        }

      if (error)
        {
          goto errout_with_semaphore;
        }

      resok = (struct REMOVE3resok *)datareply;
      np->n_fattr = resok->dir_wcc->after;
      np->n_flag |= NMODIFIED;
    }
   NFS_INVALIDATE_ATTRCACHE(np);

errout_with_semaphore:
   nfs_semgive(nmp);
   return error;
}

/****************************************************************************
 * Name: nfs_mkdir
 *
 * Description: Create a directory
 *
 ****************************************************************************/

static int nfs_mkdir(struct inode *mountpt, const char *relpath, mode_t mode)
{
  struct nfsv3_sattr *vap;
  struct nfsv3_sattr *sp;
  struct nfsmount *nmp;
  struct nfsnode *np;
  struct MKDIR3args *mkdir;
  struct MKDIR3resok *resok;
  void *datareply;
  int len;
  int error = 0;

  /* Sanity checks */

  DEBUGASSERT(mountpt && mountpt->i_private);

  /* Get the mountpoint private data from the inode structure */

  nmp = (struct nfsmount*) mountpt->i_private;
  np = nmp->nm_head;
  vap = np->n_fattr;

  /* Check if the mount is still healthy */

  nfs_semtake(nmp);
  error = nfs_checkmount(nmp);
  if (error != 0)
    {
      goto errout_with_semaphore;
    }

  nfsstats.rpccnt[NFSPROC_MKDIR]++;
  mkdir->where->dir = nmp->nm_fh;
  mkdir->where->name = relpath;

  sp->sa_modetrue = nfs_true;
  sp->sa_mode = txdr_unsigned(vap->sa_mode);
  sp->sa_uid = nfs_xdrneg1;
  sp->sa_gid = nfs_xdrneg1;
  sp->sa_size = nfs_xdrneg1;
  sp->sa_atimetype = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);
  sp->sa_mtimetype = txdr_unsigned(NFSV3SATTRTIME_TOCLIENT);

  txdr_nfsv3time(&vap->sa_atime, &sp->sa_atime);
  txdr_nfsv3time(&vap->sa_mtime, &sp->sa_mtime);

  mkdir->attributes = sp;

  error = nfs_request(nmp, NFSPROC_MKDIR, mkdir, datareply);
  if (error)
    {
      goto errout_with_semaphore;
    }

  resok = (struct MKDIR3resok *) datareply;
  np->nfsv3_type = NDIR;
  np->n_fhp = resok->handle;
  np->n_fattr = resok->obj_attributes; 
  nmp->n_flag |= NMODIFIED;
  NFS_INVALIDATE_ATTRCACHE(np);

errout_with_semaphore:
  nfs_semgive(nmp);
  return error;
}

/****************************************************************************
 * Name: nfs_rmdir
 *
 * Description: Remove a directory
 *
 ****************************************************************************/

static int nfs_rmdir(struct inode *mountpt, const char *relpath)
{
  struct nfsmount *nmp;
  struct nfsnode *np;
  struct RMDIR3args *rmdir;
  struct RMDIR3resok *resok;
  void *datareply;
  int error = 0;

  /* Sanity checks */

  DEBUGASSERT(mountpt && mountpt->i_private);

  /* Get the mountpoint private data from the inode structure */

  nmp = (struct nfsmount *)mountpt->i_private;
  np = nmp->nm_head;

  /* Check if the mount is still healthy */

  nfs_semtake(nmp);
  error = fat_checkmount(nmp);
  if (error == 0)
    {
      /* Remove the directory */

      if (np->n_type != NDIR)
        {
          error = EPERM;
          goto errout_with_semaphore;
        }

      /* Do the rpc */
      
      nfsstats.rpccnt[NFSPROC_RMDIR]++;
      rmdir->object->dir = np->n_fhp;
      rmdir->object->name = relpath;
      error = nfs_request(dvp, NFSPROC_RMDIR, rmdir, datareply);
  
      if (error == ENOENT)
        {
          error = 0;
        }

      if (error)
        {
          goto errout_with_semaphore;
        }

      resok = (struct REMOVE3resok *)datareply;
      np->n_fattr = resok->dir_wcc->after;
      np->n_flag |= NMODIFIED;
    }
   NFS_INVALIDATE_ATTRCACHE(np);

errout_with_semaphore:
   nfs_semgive(nmp);
   return error;
}

/****************************************************************************
 * Name: nfs_rename
 *
 * Description: Rename a file or directory
 *
 ****************************************************************************/

static int nfs_rename(struct inode *mountpt, const char *oldrelpath, 
                          const char *newrelpath)
{
  struct nsfmount *nmp;
  struct nfsnode *np;
  void *datreply;
  struct RENAME3args *rename;
  struct RENAME3resok *resok;
  int error = 0;

  /* Sanity checks */

  DEBUGASSERT(mountpt && mountpt->i_private);

  /* Get the mountpoint private data from the inode structure */

  nmp = (struct nfsmount *)mountpt->i_private;
  np = nmp->nm_head;

  /* Check if the mount is still healthy */

  nfs_semtake(nmp);
  error = nfs_checkmount(nmp);
  if (error != 0)
    {
      goto errout_with_semaphore;
    }

  if (np->nfsv3_type != NFREG && np->nfsv3_type != NFDIR)
    {
      fdbg("open eacces typ=%d\n", np->nfsv3_type);
      error= -EACCES;
      goto errout_with_semaphore;
    }

  nfsstats.rpccnt[NFSPROC_RENAME]++;
  rename->from->dir = np->n_fhp;
  rename->from->name = oldrelpath;
  rename->to->dir = np->n_fhp;
  rename->to->name = newrelpath;

  error = nfs_request(fdvp, NFSPROC_RENAME, rename, datareply);

  /* Kludge: Map ENOENT => 0 assuming that it is a reply to a retry. */

  if (error == ENOENT)
    {
      error = 0;
    }

  if (error)
    {
      goto errout_with_semaphore;
    }

  resok = (struct RENAME3resok *) datareply;
  np->n_fattr = resok->todir_wcc->after;
  np->n_flag |= NMODIFIED;
  NFS_INVALIDATE_ATTRCACHE(np);

errout_with_semaphore:
   nfs_semgive(nmp);
   return error;
}

/****************************************************************************
 * Name: nfs_fsinfo
 *
 * Description: Return information about a file or directory
 *
 ****************************************************************************/

static int nfs_fsinfo(struct inode *mountpt, const char *relpath, struct stat *buf) 
{
  struct nfsv3_fsinfo *fsp;
  struct FSINFOargs *fsinfo;
  struct nfsmount *nmp;
  uint32_t pref, max;
  int error = 0;
  void *datareply;

  /* Sanity checks */

  DEBUGASSERT(mountpt && mountpt->i_private);

  /* Get the mountpoint private data from the inode structure */

  nmp = (struct nfsmount*)mountpt->i_private;

  /* Check if the mount is still healthy */

  nfs_semtake(nmp);
  error = nfd_checkmount(nmp);
  if (error != 0)
    {
      goto errout_with_semaphore;
    }

  memset(buf, 0, sizeof(struct stat));
  nfsstats.rpccnt[NFSPROC_FSINFO]++;
  fsinfo->fsroot = nmp->nm_fh;
  error = nfs_request(nmp, NFSPROC_FSINFO, fsinfo, datareply);
  if (error)
    {
      goto errout_with_semaphoret;
    }

  fsp = (struct nfsv3_fsinfo *)datareply;
  nmp->nm_head->n_fattr = fsp->obj_attributes;
  pref = fxdr_unsigned(uint32_t, fsp->fs_wtpref);
  if (pref < nmp->nm_wsize)
    {
      nmp->nm_wsize = (pref + NFS_FABLKSIZE - 1) & ~(NFS_FABLKSIZE - 1);
    }

  max = fxdr_unsigned(uint32_t, fsp->fs_wtmax);
  if (max < nmp->nm_wsize)
    {
      nmp->nm_wsize = max & ~(NFS_FABLKSIZE - 1);
      if (nmp->nm_wsize == 0)
        nmp->nm_wsize = max;
    }

  pref = fxdr_unsigned(uint32_t, fsp->fs_rtpref);
  if (pref < nmp->nm_rsize)
    {
      nmp->nm_rsize = (pref + NFS_FABLKSIZE - 1) & ~(NFS_FABLKSIZE - 1);
    }

  max = fxdr_unsigned(uint32_t, fsp->fs_rtmax);
  if (max < nmp->nm_rsize)
    {
      nmp->nm_rsize = max & ~(NFS_FABLKSIZE - 1);
      if (nmp->nm_rsize == 0)
        {
          nmp->nm_rsize = max;
        }
    }

  pref = fxdr_unsigned(uint32_t, fsp->fs_dtpref);
  if (pref < nmp->nm_readdirsize)
    {
      nmp->nm_readdirsize = (pref + NFS_DIRBLKSIZ - 1) & ~(NFS_DIRBLKSIZ - 1);
    }

  if (max < nmp->nm_readdirsize)
    {
      nmp->nm_readdirsize = max & ~(NFS_DIRBLKSIZ - 1);
      if (nmp->nm_readdirsize == 0)
        {
          nmp->nm_readdirsize = max;
        }
    }

  buf->st_mode = fxdr_hyper(&fsp->obj_attributes->fa_mode);
  buf->st_size = fxdr_hyper(&fsp->obj_attributes->fa3_size);
  buf->st_blksize = 0;
  buf->st_blocks = 0;
  buf->st_mtime = fxdr_hyper(&fsp->obj_attributes->fa3_mtime);
  buf->st_atime = fxdr_hyper(&fsp->obj_attributes->fa3_atime);
  buf->st_ctime = fxdr_hyper(&fsp->obj_attributes->fa3_ctime);
  nmp->nm_flag |= NFSMNT_GOTFSINFO;

errout_with_semaphore:
  nfs_semgive(nmp);
  return error;
}

/****************************************************************************
 * Name: nfs_decode_args
 ****************************************************************************/

void nfs_decode_args(struct nfsmount *nmp, struct nfs_args *argp) 
{
  int adjsock = 0;
  int maxio;

#ifdef CONFIG_NFS_TCPIP
  /* Re-bind if rsrvd port requested and wasn't on one */

  adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT)
    && (argp->flags & NFSMNT_RESVPORT);
#endif

  /* Also re-bind if we're switching to/from a connected UDP socket */

  adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) != (argp->flags & NFSMNT_NOCONN));

  /* Update flags atomically.  Don't change the lock bits. */

  nmp->nm_flag =
    (argp->flags & ~NFSMNT_INTERNAL) | (nmp->nm_flag & NFSMNT_INTERNAL);

  if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0)
    {
      nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
      if (nmp->nm_timeo < NFS_MINTIMEO)
        {
          nmp->nm_timeo = NFS_MINTIMEO;
        }
      else if (nmp->nm_timeo > NFS_MAXTIMEO)
        {
          nmp->nm_timeo = NFS_MAXTIMEO;
        }
    }

  if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1)
    {
      nmp->nm_retry = MIN(argp->retrans, NFS_MAXREXMIT);
    }

  if (!(nmp->nm_flag & NFSMNT_SOFT))
    {
      nmp->nm_retry = NFS_MAXREXMIT + 1;  /* past clip limit */
    }

  if (argp->flags & NFSMNT_NFSV3)
    {
      if (argp->sotype == SOCK_DGRAM)
        {
          maxio = NFS_MAXDGRAMDATA;
        }
      else
        {
          maxio = NFS_MAXDATA;
        }
    }
  else
    {
      maxio = NFS_V2MAXDATA;
    }

  if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0)
    {
      int osize = nmp->nm_wsize;
      nmp->nm_wsize = argp->wsize;

      /* Round down to multiple of blocksize */

      nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
      if (nmp->nm_wsize <= 0)
        {
          nmp->nm_wsize = NFS_FABLKSIZE;
        }

      adjsock |= (nmp->nm_wsize != osize);
    }

  if (nmp->nm_wsize > maxio)
    {
      nmp->nm_wsize = maxio;
    }

  if (nmp->nm_wsize > MAXBSIZE)
    {
      nmp->nm_wsize = MAXBSIZE;
    }

  if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0)
    {
      int osize = nmp->nm_rsize;
      nmp->nm_rsize = argp->rsize;

      /* Round down to multiple of blocksize */

      nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
      if (nmp->nm_rsize <= 0)
        {
          nmp->nm_rsize = NFS_FABLKSIZE;
        }

      adjsock |= (nmp->nm_rsize != osize);
    }

  if (nmp->nm_rsize > maxio)
    {
      nmp->nm_rsize = maxio;
    }

  if (nmp->nm_rsize > MAXBSIZE)
    {
      nmp->nm_rsize = MAXBSIZE;
    }

  if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0)
    {
      nmp->nm_readdirsize = argp->readdirsize;

      /* Round down to multiple of blocksize */

      nmp->nm_readdirsize &= ~(NFS_DIRBLKSIZ - 1);
      if (nmp->nm_readdirsize < NFS_DIRBLKSIZ)
        {
          nmp->nm_readdirsize = NFS_DIRBLKSIZ;
        }
    }
  else if (argp->flags & NFSMNT_RSIZE)
    {
      nmp->nm_readdirsize = nmp->nm_rsize;
    }

  if (nmp->nm_readdirsize > maxio)
    {
      nmp->nm_readdirsize = maxio;
    }

  if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
      argp->maxgrouplist <= NFS_MAXGRPS)
    {
      nmp->nm_numgrps = argp->maxgrouplist;
    }

  if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 &&
      argp->readahead <= NFS_MAXRAHEAD)
    {
      nmp->nm_readahead = argp->readahead;
    }

  if (argp->flags & NFSMNT_ACREGMIN && argp->acregmin >= 0)
    {
      if (argp->acregmin > 0xffff)
        {
          nmp->nm_acregmin = 0xffff;
        }
      else
        {
          nmp->nm_acregmin = argp->acregmin;
        }
    }

  if (argp->flags & NFSMNT_ACREGMAX && argp->acregmax >= 0)
    {
      if (argp->acregmax > 0xffff)
        {
          nmp->nm_acregmax = 0xffff;
        }
      else
        {
          nmp->nm_acregmax = argp->acregmax;
        }
    }

  if (nmp->nm_acregmin > nmp->nm_acregmax)
    {
      nmp->nm_acregmin = nmp->nm_acregmax;
    }

  if (argp->flags & NFSMNT_ACDIRMIN && argp->acdirmin >= 0)
    {
      if (argp->acdirmin > 0xffff)
        {
          nmp->nm_acdirmin = 0xffff;
        }
      else
        {
          nmp->nm_acdirmin = argp->acdirmin;
        }
    }

  if (argp->flags & NFSMNT_ACDIRMAX && argp->acdirmax >= 0)
    {
      if (argp->acdirmax > 0xffff)
        {
          nmp->nm_acdirmax = 0xffff;
        }
      else
        {
          nmp->nm_acdirmax = argp->acdirmax;
        }
    }

  if (nmp->nm_acdirmin > nmp->nm_acdirmax)
    {
      nmp->nm_acdirmin = nmp->nm_acdirmax;
    }

  if (nmp->nm_so && adjsock)
    {
      nfs_disconnect(nmp);
      if (nmp->nm_sotype == SOCK_DGRAM)
        while (nfs_connect(nmp))
          {
            nvdbg("nfs_args: retrying connect\n");
          }
    }
}

#ifdef 0
/****************************************************************************
 * Name: nfs_sync
 *
 * Description: Flush out the buffer cache
 *
 ****************************************************************************/

int nfs_sync(struct file *filep) 
{
  struct inode *inode;
  struct nfsmount *nmp;
  struct nfsnode *np;
  int error = 0;

  /* Sanity checks */

  DEBUGASSERT(filep->f_priv != NULL && filep->f_inode != NULL);

  /* Recover our private data from the struct file instance */

  np    = filep->f_priv;
  inode = filep->f_inode;
  nmp   = inode->i_private;

  DEBUGASSERT(nmp != NULL);

  /* Make sure that the mount is still healthy */

  nfs_semtake(nmp);
  error = nfs_checkmount(nmp);
  if (error != 0)
    {
      goto errout_with_semaphore;
    }

  /* Force stale buffer cache information to be flushed. */

  /* Check if the has been modified in any way */

  if ((np->n_flag & NMODIFIED) != 0)
    {
      //error = VOP_FSYNC(vp, cred, waitfor, p); 
    }

  return error;

errout_with_semaphore:
  nfs_semgive(nmp);
  return error;
}
#endif