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