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