xref: /third_party/NuttX/fs/nfs/nfs_util.c (revision beacf11b)
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
45static 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
105void 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
114void 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
128int 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
164int 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
172tryagain:
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
246int 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
366int 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
475int 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
564void 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