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