• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  * fs/nfs/nfs_util.c
3  *
4  *   Copyright (C) 2012-2013, 2017 Gregory Nutt. All rights reserved.
5  *   Author: Gregory Nutt <gnutt@nuttx.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name NuttX nor the names of its contributors may be
18  *    used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  ****************************************************************************/
35 
36 /****************************************************************************
37  * Included Files
38  ****************************************************************************/
39 
40 #include <sys/types.h>
41 #include <sys/time.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <assert.h>
45 #include <pthread.h>
46 #include "vfs_config.h"
47 #include "dirent.h"
48 #include "rpc.h"
49 #include "nfs.h"
50 #include "nfs_node.h"
51 #include "xdr_subs.h"
52 #include "nfs.h"
53 #undef  OK
54 #define OK 0
55 
56 /****************************************************************************
57  * Private Functions
58  ****************************************************************************/
59 
nfs_pathsegment(const char ** path,char * buffer,char * terminator)60 static inline int nfs_pathsegment(const char **path, char *buffer,
61                                   char *terminator)
62 {
63   const char *src = *path;
64   char *dest = buffer;
65   int nbytes = 0;
66   char ch;
67 
68   /* Loop until the name is successfully parsed or an error occurs */
69 
70   for (; ; )
71     {
72       /* Get the next byte from the path */
73 
74       ch = *src++;
75 
76       /* Check if this the last byte in this segment name */
77 
78       if (ch == '\0' || ch == '/')
79         {
80           /* This logic just supports "//" sequences in the path name */
81 
82           if (ch == '\0' || nbytes > 0)
83             {
84               /* NULL terminate the parsed path segment */
85 
86               *dest        = '\0';
87 
88               /* Return next path and the terminating character */
89 
90               *terminator = ch;
91               *path       = src;
92               return OK;
93             }
94 
95           /* Just skip over any leading '/' characters */
96         }
97       else if (nbytes >= NAME_MAX)
98         {
99           nfs_debug_error("File name segment is too long: %d\n", *path);
100           return ENAMETOOLONG;
101         }
102       else
103         {
104           /* Save next character in the accumulated name */
105 
106           *dest++ = ch;
107           nbytes++;
108         }
109     }
110 }
111 
112 /****************************************************************************
113  * Public Functions
114  ****************************************************************************/
115 
116 /****************************************************************************
117  * Name: nfs_mux_take
118  ****************************************************************************/
119 
nfs_mux_take(struct nfsmount * nmp)120 void nfs_mux_take(struct nfsmount *nmp)
121 {
122   (void)pthread_mutex_lock(&nmp->nm_mux);
123 }
124 
125 /****************************************************************************
126  * Name: nfs_mux_release
127  ****************************************************************************/
128 
nfs_mux_release(struct nfsmount * nmp)129 void nfs_mux_release(struct nfsmount *nmp)
130 {
131   (void)pthread_mutex_unlock(&nmp->nm_mux);
132 }
133 
134 /****************************************************************************
135  * Name: nfs_checkmount
136  *
137  * Description: Check if the mountpoint is still valid.
138  *
139  *   The caller should hold the mountpoint semaphore
140  *
141  ****************************************************************************/
142 
nfs_checkmount(struct nfsmount * nmp)143 int nfs_checkmount(struct nfsmount *nmp)
144 {
145   struct nfsnode *file;
146 
147   /* If the nm_mounted flag is false, then we have already handled the loss
148    * of the mount.
149    */
150 
151   DEBUGASSERT(nmp);
152   if (!nmp->nm_mounted)
153     {
154       /* Make sure that this is flagged in every opened file */
155 
156       for (file = nmp->nm_head; file; file = file->n_next)
157         {
158           file->n_flags &= ~NFSNODE_OPEN;
159         }
160 
161       return ENODEV;
162     }
163 
164   return 0;
165 }
166 
167 /****************************************************************************
168  * Name: nfs_request
169  *
170  * Description:
171  *   Perform the NFS request. On successful receipt, it verifies the NFS level of the
172  *   returned values.
173  *
174  * Returned Value:
175  *   Zero on success; a positive errno value on failure.
176  *
177  ****************************************************************************/
178 
nfs_request(struct nfsmount * nmp,int procnum,void * request,size_t reqlen,void * response,size_t resplen)179 int nfs_request(struct nfsmount *nmp, int procnum,
180                 void *request, size_t reqlen,
181                 void *response, size_t resplen)
182 {
183   struct rpcclnt *clnt = nmp->nm_rpcclnt;
184   struct nfs_reply_header replyh;
185   int error;
186 
187 tryagain:
188   error = rpcclnt_request(clnt, procnum, NFS_PROG, NFS_VER3,
189                           request, reqlen, response, resplen);
190   if (error != 0)
191     {
192       nfs_error("rpcclnt_request failed: %d\n", error);
193       return error;
194     }
195 
196   memcpy(&replyh, response, sizeof(struct nfs_reply_header));
197 
198   if (replyh.nfs_status != 0)
199     {
200       /* NFS_ERRORS are the same as NuttX errno values */
201 
202       error = fxdr_unsigned(uint32_t, replyh.nfs_status);
203       return error;
204     }
205 
206   if (replyh.rpc_verfi.authtype != 0)
207     {
208       error = fxdr_unsigned(int, replyh.rpc_verfi.authtype);
209 
210       if (error == EAGAIN)
211         {
212           error = 0;
213           goto tryagain;
214         }
215 
216       nfs_debug_error("NFS error %d from server\n", error);
217       return error;
218     }
219 
220   return OK;
221 }
222 
223 /****************************************************************************
224  * Name: nfs_lookup
225  *
226  * Description:
227  *   Given a directory file handle, and the path to file in the directory,
228  *   return the file handle of the path and attributes of both the file and
229  *   the directory containing the file.
230  *
231  *   NOTE:  The LOOKUP call differs from other RPC messages in that the
232  *   call message is variable length, depending upon the size of the path
233  *   name.
234  *
235  ****************************************************************************/
236 
nfs_lookup(struct nfsmount * nmp,const char * filename,struct file_handle * fhandle,struct nfs_fattr * obj_attributes,struct nfs_fattr * dir_attributes)237 int nfs_lookup(struct nfsmount *nmp, const char *filename,
238                struct file_handle *fhandle,
239                struct nfs_fattr *obj_attributes,
240                struct nfs_fattr *dir_attributes)
241 {
242   uint32_t *ptr = NULL;
243   uint32_t value;
244   int reqlen;
245   int namelen;
246   int error = 0;
247 
248   DEBUGASSERT(nmp && filename && fhandle);
249 
250   /* Get the length of the string to be sent */
251 
252   namelen = strlen(filename);
253   if (namelen > NAME_MAX)
254     {
255       nfs_debug_error("Length of the string is too long: %d\n", namelen);
256       return E2BIG;
257     }
258 
259   /* Initialize the request */
260 
261   ptr     = (uint32_t *)&nmp->nm_msgbuffer.lookup.lookup;
262   reqlen  = 0;
263 
264   /* Copy the variable length, directory file handle */
265 
266   *ptr++  = txdr_unsigned(fhandle->length);
267   reqlen += sizeof(uint32_t);
268 
269   memcpy(ptr, &fhandle->handle, fhandle->length);
270   reqlen += fhandle->length;
271   ptr    += uint32_increment(fhandle->length);
272 
273   /* Copy the variable-length file name */
274 
275   *ptr++  = txdr_unsigned(namelen);
276   reqlen += sizeof(uint32_t);
277 
278   memcpy(ptr, filename, namelen);
279   reqlen += uint32_alignup(namelen);
280 
281   /* Request LOOKUP from the server */
282 
283   nfs_statistics(NFSPROC_LOOKUP);
284   error = nfs_request(nmp, NFSPROC_LOOKUP,
285                       (void *)&nmp->nm_msgbuffer.lookup, reqlen,
286                       (void *)nmp->nm_iobuffer, nmp->nm_buflen);
287 
288   if (error)
289     {
290       nfs_debug_error("nfs_request failed: %d\n", error);
291       return error;
292     }
293 
294   /* Return the data to the caller's buffers.  NOTE:  Here we ignore the
295    * the exact layout of the rpc_reply_lookup structure.  File handles
296    * may differ in size whereas struct rpc_reply_lookup uses a fixed size.
297    */
298 
299   ptr = (uint32_t *)&((struct rpc_reply_lookup *)nmp->nm_iobuffer)->lookup;
300 
301   /* Get the length of the file handle */
302 
303   value = *ptr++;
304   value = fxdr_unsigned(uint32_t, value);
305   if (value > NFSX_V3FHMAX)
306     {
307       nfs_debug_error("Bad file handle length: %d\n", value);
308       return EIO;
309     }
310 
311   /* Return the file handle */
312 
313   fhandle->length = value;
314   memcpy(&fhandle->handle, ptr, value);
315   ptr += uint32_increment(value);
316 
317   /* Check if there are object attributes and, if so, copy them to the user
318    * buffer
319    */
320 
321   value = *ptr++;
322   if (value)
323     {
324       if (obj_attributes)
325         {
326           memcpy(obj_attributes, ptr, sizeof(struct nfs_fattr));
327         }
328 
329       ptr += uint32_increment(sizeof(struct nfs_fattr));
330     }
331 
332   /* Check if there are directory attributes and, if so, copy them to the
333    * user buffer
334    */
335 
336   value = *ptr++;
337   if (value && dir_attributes)
338     {
339       memcpy(dir_attributes, ptr, sizeof(struct nfs_fattr));
340     }
341 
342   return OK;
343 }
344 
345 /****************************************************************************
346  * Name: nfs_findnode
347  *
348  * Description:
349  *   Given a path to something that may or may not be in the file system,
350  *   return the handle of the directory entry of the requested object.
351  *
352  * Returned Value:
353  *   Zero on success; a positive errno value on failure.
354  *
355  ****************************************************************************/
356 
nfs_findnode(struct nfsmount * nmp,const char * relpath,struct file_handle * fhandle,struct nfs_fattr * obj_attributes,struct nfs_fattr * dir_attributes)357 int nfs_findnode(struct nfsmount *nmp, const char *relpath,
358                  struct file_handle *fhandle,
359                  struct nfs_fattr *obj_attributes,
360                  struct nfs_fattr *dir_attributes)
361 {
362   const char *path = relpath;
363   char            buffer[NAME_MAX + 1];
364   char            terminator;
365   uint32_t         tmp;
366   int             error;
367 
368   /* Start with the file handle of the root directory.  */
369 
370   fhandle->length = nmp->nm_fhsize;
371   memcpy(&fhandle->handle, &nmp->nm_fh, nmp->nm_fhsize);
372 
373   /* If no path was provided, then the root directory must be exactly what
374    * the caller is looking for.
375    */
376 
377   if (*path == '\0' || strlen(path) == 0)
378     {
379       /* Return the root directory attributes */
380 
381       if (obj_attributes)
382         {
383           memcpy(obj_attributes, &nmp->nm_fattr, sizeof(struct nfs_fattr));
384         }
385 
386       if (dir_attributes)
387         {
388           memcpy(dir_attributes, &nmp->nm_fattr, sizeof(struct nfs_fattr));
389         }
390 
391       return OK;
392     }
393 
394   /* This is not the root directory. Loop until the directory entry corresponding
395    * to the path is found.
396    */
397 
398   for (; ; )
399     {
400       /* Extract the next path segment name. */
401 
402       error = nfs_pathsegment(&path, buffer, &terminator);
403       if (error != OK)
404         {
405           /* The filename segment contains is too long. */
406 
407           nfs_debug_error("nfs_pathsegment of \"%s\" failed after \"%s\": %d\n",
408                relpath, buffer, error);
409           return error;
410         }
411 
412       /* Look-up this path segment */
413 
414       error = nfs_lookup(nmp, buffer, fhandle, obj_attributes, dir_attributes);
415       if (error != OK)
416         {
417           nfs_debug_error("nfs_lookup of \"%s\" failed at \"%s\": %d\n",
418                 relpath, buffer, error);
419           return error;
420         }
421 
422       /* If the terminator character in the path was the end of the string
423        * then we have successfully found the directory entry that describes
424        * the path.
425        */
426 
427       if (!terminator)
428         {
429           /* Return success meaning that the description the matching
430            * directory entry is in fhandle, obj_attributes, and dir_attributes.
431            */
432 
433           return OK;
434         }
435 
436       /* No.. then we have found one of the intermediate directories on
437        * the way to the final path target.  In this case, make sure
438        * the thing that we found is, indeed, a directory.
439        */
440 
441       tmp = fxdr_unsigned(uint32_t, obj_attributes->fa_type);
442       if (tmp != NFDIR)
443         {
444           /* Ooops.. we found something else */
445 
446           nfs_debug_error("Intermediate segment \"%s\" of \'%s\" is not a directory\n",
447                buffer, path);
448           return ENOTDIR;
449         }
450     }
451 }
452 
453 /****************************************************************************
454  * Name: nfs_finddir
455  *
456  * Description:
457  *   Given a path to something that may or may not be in the file system,
458  *   return the handle of the entry of the directory containing the requested
459  *   object.
460  *
461  * Returned Value:
462  *   Zero on success; a positive errno value on failure.
463  *
464  ****************************************************************************/
465 
nfs_finddir(struct nfsmount * nmp,const char * relpath,struct file_handle * fhandle,struct nfs_fattr * attributes,char * filename)466 int nfs_finddir(struct nfsmount *nmp, const char *relpath,
467                 struct file_handle *fhandle,
468                 struct nfs_fattr *attributes, char *filename)
469 {
470   const char  *path = relpath;
471   uint32_t         tmp;
472   char             terminator;
473   int              error;
474 
475   /* Verify that a path was provided */
476 
477   if (*path == '\0' || strlen(path) == 0)
478     {
479       /* Return the root directory attributes */
480 
481       return ENOENT;
482     }
483 
484   /* Start with the file handle of the root directory.  */
485 
486   fhandle->length = nmp->nm_fhsize;
487   memcpy(&fhandle->handle, &nmp->nm_fh, nmp->nm_fhsize);
488   memcpy(attributes, &nmp->nm_fattr, sizeof(struct nfs_fattr));
489 
490   /* Loop until the directory entry containing the path is found. */
491 
492   for (; ; )
493     {
494       /* Extract the next path segment name. */
495 
496       error = nfs_pathsegment(&path, filename, &terminator);
497       if (error != OK)
498         {
499           /* The filename segment contains is too long. */
500 
501           nfs_debug_error("nfs_pathsegment of \"%s\" failed after \"%s\": %d\n",
502                relpath, filename, error);
503           return error;
504         }
505 
506       /* If the terminator character in the path was the end of the string
507        * then we have successfully found the directory that contains the name
508        * of interest.
509        */
510 
511       if (!terminator)
512         {
513           /* Return success meaning that the description of the directory
514            * containing the object is in fhandle and attributes.
515            */
516 
517           return OK;
518         }
519 
520       /* Look-up the next path segment */
521 
522       error = nfs_lookup(nmp, filename, fhandle, attributes, NULL);
523       if (error != OK)
524         {
525           nfs_debug_error("fs_lookup of \"%s\" failed at \"%s\": %d\n",
526                 relpath, filename, error);
527           return error;
528         }
529 
530       /* Make sure the thing that we found is, indeed, a directory. */
531 
532       tmp = fxdr_unsigned(uint32_t, attributes->fa_type);
533       if (tmp != NFDIR)
534         {
535           /* Ooops.. we found something else */
536 
537           nfs_debug_error("Intermediate segment \"%s\" of \'%s\" is not a directory\n",
538                filename, path);
539           return ENOTDIR;
540         }
541     }
542 }
543 
544 /****************************************************************************
545  * Name: nfs_attrupdate
546  *
547  * Description:
548  *   Update file attributes on write or after the file is modified.
549  *
550  * Returned Value:
551  *   None.
552  *
553  ****************************************************************************/
554 
nfs_attrupdate(struct nfsnode * np,struct nfs_fattr * attributes)555 void nfs_attrupdate(struct nfsnode *np, struct nfs_fattr *attributes)
556 {
557   struct timespec ts;
558 
559   /* Save a few of the files attribute values in file structure (host order) */
560 
561   np->n_type   = fxdr_unsigned(uint8_t, attributes->fa_type);
562   np->n_mode   = fxdr_unsigned(uint16_t, attributes->fa_mode);
563   np->n_size   = fxdr_hyper(&attributes->fa_size);
564 
565   fxdr_nfsv3time(&attributes->fa_mtime, &ts);
566   np->n_timestamp.tv_sec = ts.tv_sec;
567   np->n_timestamp.tv_nsec = ts.tv_nsec;
568 
569   fxdr_nfsv3time(&attributes->fa_ctime, &ts);
570   np->n_ctime  = ts.tv_sec;
571 }
572