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