1 /* 2 * Copyright 2012 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Paweł Dziepak, pdziepak@quarnos.org 7 */ 8 9 10 #include "ReplyInterpreter.h" 11 12 #include <string.h> 13 14 #include <util/kernel_cpp.h> 15 16 #include "Cookie.h" 17 18 19 FSLocation::~FSLocation() 20 { 21 free(const_cast<char*>(fRootPath)); 22 for (uint32 i = 0; i < fCount; i++) 23 free(const_cast<char*>(fLocations[i])); 24 delete[] fLocations; 25 } 26 27 28 FSLocations::~FSLocations() 29 { 30 free(const_cast<char*>(fRootPath)); 31 delete[] fLocations; 32 } 33 34 35 AttrValue::AttrValue() 36 : 37 fFreePointer(false) 38 { 39 } 40 41 42 AttrValue::~AttrValue() 43 { 44 if (fFreePointer) 45 free(fData.fPointer); 46 if (fAttribute == FATTR4_FS_LOCATIONS) 47 delete fData.fLocations; 48 } 49 50 51 DirEntry::DirEntry() 52 : 53 fName(NULL), 54 fAttrs(NULL), 55 fAttrCount(0) 56 { 57 } 58 59 60 DirEntry::~DirEntry() 61 { 62 free(const_cast<char*>(fName)); 63 delete[] fAttrs; 64 } 65 66 67 ReplyInterpreter::ReplyInterpreter(RPC::Reply* reply) 68 : 69 fNFS4Error(NFS4_OK), 70 fDecodeError(false), 71 fReply(reply) 72 { 73 if (reply != NULL) 74 _ParseHeader(); 75 } 76 77 78 ReplyInterpreter::~ReplyInterpreter() 79 { 80 delete fReply; 81 } 82 83 84 void 85 ReplyInterpreter::_ParseHeader() 86 { 87 fNFS4Error = fReply->Stream().GetUInt(); 88 fReply->Stream().GetOpaque(NULL); 89 fReply->Stream().GetUInt(); 90 } 91 92 93 status_t 94 ReplyInterpreter::Access(uint32* supported, uint32* allowed) 95 { 96 status_t res = _OperationError(OpAccess); 97 if (res != B_OK) 98 return res; 99 100 uint32 support = fReply->Stream().GetUInt(); 101 uint32 allow = fReply->Stream().GetUInt(); 102 103 if (supported != NULL) 104 *supported = support; 105 if (allowed != NULL) 106 *allowed = allow; 107 108 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 109 } 110 111 112 status_t 113 ReplyInterpreter::Close() 114 { 115 status_t res = _OperationError(OpClose); 116 if (res != B_OK) 117 return res; 118 119 fReply->Stream().GetUInt(); 120 fReply->Stream().GetUInt(); 121 fReply->Stream().GetUInt(); 122 fReply->Stream().GetUInt(); 123 124 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 125 } 126 127 128 status_t 129 ReplyInterpreter::Create() 130 { 131 status_t res = _OperationError(OpCreate); 132 if (res != B_OK) 133 return res; 134 135 fReply->Stream().GetBoolean(); 136 fReply->Stream().GetUHyper(); 137 fReply->Stream().GetUHyper(); 138 uint32 count = fReply->Stream().GetUInt(); 139 for (uint32 i; i < count; i++) 140 fReply->Stream().GetUInt(); 141 142 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 143 } 144 145 146 147 // Bit Twiddling Hacks 148 // http://graphics.stanford.edu/~seander/bithacks.html 149 static inline uint32 sCountBits(uint32 v) 150 { 151 v = v - ((v >> 1) & 0x55555555); 152 v = (v & 0x33333333) + ((v >> 2) & 0x33333333); 153 return ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; 154 } 155 156 157 status_t 158 ReplyInterpreter::GetAttr(AttrValue** attrs, uint32* count) 159 { 160 status_t res = _OperationError(OpGetAttr); 161 if (res != B_OK) 162 return res; 163 164 return _DecodeAttrs(fReply->Stream(), attrs, count); 165 } 166 167 168 status_t 169 ReplyInterpreter::GetFH(FileHandle* fh) 170 { 171 status_t res = _OperationError(OpGetFH); 172 if (res != B_OK) 173 return res; 174 175 uint32 size; 176 const void* ptr = fReply->Stream().GetOpaque(&size); 177 if (ptr == NULL || size > NFS4_FHSIZE) 178 return B_BAD_VALUE; 179 180 if (fh != NULL) { 181 fh->fSize = size; 182 memcpy(fh->fData, ptr, size); 183 } 184 185 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 186 } 187 188 189 status_t 190 ReplyInterpreter::Link() 191 { 192 status_t res = _OperationError(OpLink); 193 if (res != B_OK) 194 return res; 195 196 fReply->Stream().GetBoolean(); 197 fReply->Stream().GetUHyper(); 198 fReply->Stream().GetUHyper(); 199 200 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 201 } 202 203 204 status_t 205 ReplyInterpreter::Lock(LockInfo* linfo) 206 { 207 status_t res = _OperationError(OpLock); 208 if (res != B_OK) 209 return res; 210 211 linfo->fOwner->fStateSeq = fReply->Stream().GetUInt(); 212 linfo->fOwner->fStateId[0] = fReply->Stream().GetUInt(); 213 linfo->fOwner->fStateId[1] = fReply->Stream().GetUInt(); 214 linfo->fOwner->fStateId[2] = fReply->Stream().GetUInt(); 215 216 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 217 } 218 219 220 status_t 221 ReplyInterpreter::LockT(uint64* pos, uint64* len, LockType* type) 222 { 223 status_t res = _OperationError(OpLockU); 224 if (res != B_WOULD_BLOCK || NFS4Error() != NFS4ERR_DENIED) 225 return res; 226 227 *pos = fReply->Stream().GetUHyper(); 228 *len = fReply->Stream().GetUHyper(); 229 *type = static_cast<LockType>(fReply->Stream().GetInt()); 230 231 fReply->Stream().GetUHyper(); 232 fReply->Stream().GetOpaque(NULL); 233 234 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 235 } 236 237 238 status_t 239 ReplyInterpreter::LockU(LockInfo* linfo) 240 { 241 status_t res = _OperationError(OpLockU); 242 if (res != B_OK) 243 return res; 244 245 linfo->fOwner->fStateSeq = fReply->Stream().GetUInt(); 246 linfo->fOwner->fStateId[0] = fReply->Stream().GetUInt(); 247 linfo->fOwner->fStateId[1] = fReply->Stream().GetUInt(); 248 linfo->fOwner->fStateId[2] = fReply->Stream().GetUInt(); 249 250 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 251 } 252 253 254 status_t 255 ReplyInterpreter::Open(uint32* id, uint32* seq, bool* confirm) 256 { 257 status_t res = _OperationError(OpOpen); 258 if (res != B_OK) 259 return res; 260 261 *seq = fReply->Stream().GetUInt(); 262 id[0] = fReply->Stream().GetUInt(); 263 id[1] = fReply->Stream().GetUInt(); 264 id[2] = fReply->Stream().GetUInt(); 265 266 // change info 267 fReply->Stream().GetBoolean(); 268 fReply->Stream().GetUHyper(); 269 fReply->Stream().GetUHyper(); 270 271 uint32 flags = fReply->Stream().GetUInt(); 272 *confirm = (flags & OPEN4_RESULT_CONFIRM) == OPEN4_RESULT_CONFIRM; 273 274 // attrmask 275 uint32 bcount = fReply->Stream().GetUInt(); 276 for (uint32 i = 0; i < bcount; i++) 277 fReply->Stream().GetUInt(); 278 279 // delegation info 280 fReply->Stream().GetUInt(); 281 282 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 283 } 284 285 286 status_t 287 ReplyInterpreter::OpenConfirm(uint32* stateSeq) 288 { 289 status_t res = _OperationError(OpOpenConfirm); 290 if (res != B_OK) 291 return res; 292 293 *stateSeq = fReply->Stream().GetUInt(); 294 fReply->Stream().GetUInt(); 295 fReply->Stream().GetUInt(); 296 fReply->Stream().GetUInt(); 297 298 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 299 } 300 301 302 status_t 303 ReplyInterpreter::Read(void* buffer, uint32* size, bool* eof) 304 { 305 status_t res = _OperationError(OpRead); 306 if (res != B_OK) 307 return res; 308 309 *eof = fReply->Stream().GetBoolean(); 310 const void* ptr = fReply->Stream().GetOpaque(size); 311 memcpy(buffer, ptr, *size); 312 313 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 314 } 315 316 317 status_t 318 ReplyInterpreter::ReadDir(uint64* cookie, uint64* cookieVerf, 319 DirEntry** dirents, uint32* _count, bool* eof) 320 { 321 status_t res = _OperationError(OpReadDir); 322 if (res != B_OK) 323 return res; 324 325 *cookieVerf = fReply->Stream().GetUHyper(); 326 327 bool isNext; 328 uint32 count = 0; 329 DirEntry* entries = new(std::nothrow) DirEntry[*_count]; 330 if (entries == NULL) 331 return B_NO_MEMORY; 332 333 isNext = fReply->Stream().GetBoolean(); 334 while (isNext && count < *_count) { 335 *cookie = fReply->Stream().GetUHyper(); 336 337 entries[count].fName = fReply->Stream().GetString(); 338 _DecodeAttrs(fReply->Stream(), &entries[count].fAttrs, 339 &entries[count].fAttrCount); 340 341 count++; 342 343 isNext = fReply->Stream().GetBoolean(); 344 } 345 if (!isNext) 346 *eof = fReply->Stream().GetBoolean(); 347 else 348 *eof = false; 349 350 *_count = count; 351 *dirents = entries; 352 353 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 354 } 355 356 357 status_t 358 ReplyInterpreter::ReadLink(void* buffer, uint32* size, uint32 maxSize) 359 { 360 status_t res = _OperationError(OpReadLink); 361 if (res != B_OK) 362 return res; 363 364 const void* ptr = fReply->Stream().GetOpaque(size); 365 memcpy(buffer, ptr, min_c(*size, maxSize)); 366 367 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 368 } 369 370 371 status_t 372 ReplyInterpreter::Remove() 373 { 374 status_t res = _OperationError(OpRemove); 375 if (res != B_OK) 376 return res; 377 378 fReply->Stream().GetBoolean(); 379 fReply->Stream().GetUHyper(); 380 fReply->Stream().GetUHyper(); 381 382 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 383 } 384 385 386 status_t 387 ReplyInterpreter::Rename() 388 { 389 status_t res = _OperationError(OpRename); 390 if (res != B_OK) 391 return res; 392 393 fReply->Stream().GetBoolean(); 394 fReply->Stream().GetUHyper(); 395 fReply->Stream().GetUHyper(); 396 397 fReply->Stream().GetBoolean(); 398 fReply->Stream().GetUHyper(); 399 fReply->Stream().GetUHyper(); 400 401 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 402 } 403 404 405 status_t 406 ReplyInterpreter::SetAttr() 407 { 408 status_t res = _OperationError(OpSetAttr); 409 if (res != B_OK) 410 return res; 411 412 uint32 bcount = fReply->Stream().GetUInt(); 413 for (uint32 i = 0; i < bcount; i++) 414 fReply->Stream().GetUInt(); 415 416 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 417 } 418 419 420 status_t 421 ReplyInterpreter::SetClientID(uint64* clientid, uint64* verifier) 422 { 423 status_t res = _OperationError(OpSetClientID); 424 if (res != B_OK) 425 return res; 426 427 *clientid = fReply->Stream().GetUHyper(); 428 *verifier = fReply->Stream().GetUHyper(); 429 430 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 431 } 432 433 434 status_t 435 ReplyInterpreter::Write(uint32* size) 436 { 437 status_t res = _OperationError(OpWrite); 438 if (res != B_OK) 439 return res; 440 441 *size = fReply->Stream().GetUInt(); 442 fReply->Stream().GetInt(); 443 fReply->Stream().GetUHyper(); 444 445 return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK; 446 } 447 448 449 static const char* 450 sFlattenPathname(XDR::ReadStream& str) 451 { 452 uint32 count = str.GetUInt(); 453 char* pathname = NULL; 454 uint32 size = 0; 455 for (uint32 i = 0; i < count; i++) { 456 const char* path = str.GetString(); 457 size += strlen(path) + 1; 458 if (pathname == NULL) { 459 pathname = reinterpret_cast<char*>(malloc(strlen(path + 1))); 460 pathname[0] = '\0'; 461 } else { 462 *pathname++ = '/'; 463 pathname = reinterpret_cast<char*>(realloc(pathname, size)); 464 } 465 strcat(pathname, path); 466 free(const_cast<char*>(path)); 467 } 468 469 return pathname; 470 } 471 472 473 status_t 474 ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs, 475 uint32* count) 476 { 477 uint32 bcount = fReply->Stream().GetUInt(); 478 uint32 *bitmap = new(std::nothrow) uint32[bcount]; 479 if (bitmap == NULL) 480 return B_NO_MEMORY; 481 482 uint32 attr_count = 0; 483 for (uint32 i = 0; i < bcount; i++) { 484 bitmap[i] = str.GetUInt(); 485 attr_count += sCountBits(bitmap[i]); 486 } 487 488 if (attr_count == 0) { 489 *attrs = NULL; 490 *count = 0; 491 return B_OK; 492 } else if (attr_count > FATTR4_MAXIMUM_ATTR_ID) 493 return B_BAD_VALUE; 494 495 uint32 size; 496 const void* ptr = str.GetOpaque(&size); 497 XDR::ReadStream stream(const_cast<void*>(ptr), size); 498 499 AttrValue* values = new(std::nothrow) AttrValue[attr_count]; 500 if (values == NULL) { 501 delete[] bitmap; 502 return B_NO_MEMORY; 503 } 504 505 uint32 current = 0; 506 507 if (sIsAttrSet(FATTR4_SUPPORTED_ATTRS, bitmap, bcount)) { 508 values[current].fAttribute = FATTR4_SUPPORTED_ATTRS; 509 uint32 count = stream.GetInt(); 510 uint32 i; 511 // two uint32 are enough for NFS4, not for NFS4.1 512 for (i = 0; i < min_c(count, 2); i++) 513 ((uint32*)&values[current].fData.fValue64)[i] = stream.GetUInt(); 514 for (; i < count; i++) 515 stream.GetUInt(); 516 current++; 517 } 518 519 if (sIsAttrSet(FATTR4_TYPE, bitmap, bcount)) { 520 values[current].fAttribute = FATTR4_TYPE; 521 values[current].fData.fValue32 = stream.GetInt(); 522 current++; 523 } 524 525 if (sIsAttrSet(FATTR4_FH_EXPIRE_TYPE, bitmap, bcount)) { 526 values[current].fAttribute = FATTR4_FH_EXPIRE_TYPE; 527 values[current].fData.fValue32 = stream.GetUInt(); 528 current++; 529 } 530 531 if (sIsAttrSet(FATTR4_CHANGE, bitmap, bcount)) { 532 values[current].fAttribute = FATTR4_CHANGE; 533 values[current].fData.fValue64 = stream.GetUHyper(); 534 current++; 535 } 536 537 if (sIsAttrSet(FATTR4_SIZE, bitmap, bcount)) { 538 values[current].fAttribute = FATTR4_SIZE; 539 values[current].fData.fValue64 = stream.GetUHyper(); 540 current++; 541 } 542 543 if (sIsAttrSet(FATTR4_FSID, bitmap, bcount)) { 544 values[current].fAttribute = FATTR4_FSID; 545 values[current].fFreePointer = true; 546 547 FileSystemId fsid; 548 fsid.fMajor = stream.GetUHyper(); 549 fsid.fMinor = stream.GetUHyper(); 550 551 values[current].fData.fPointer = malloc(sizeof(fsid)); 552 memcpy(values[current].fData.fPointer, &fsid, sizeof(fsid)); 553 current++; 554 } 555 556 if (sIsAttrSet(FATTR4_LEASE_TIME, bitmap, bcount)) { 557 values[current].fAttribute = FATTR4_LEASE_TIME; 558 values[current].fData.fValue32 = stream.GetUInt(); 559 current++; 560 } 561 562 if (sIsAttrSet(FATTR4_FILEID, bitmap, bcount)) { 563 values[current].fAttribute = FATTR4_FILEID; 564 values[current].fData.fValue64 = stream.GetUHyper(); 565 current++; 566 } 567 568 if (sIsAttrSet(FATTR4_FILES_FREE, bitmap, bcount)) { 569 values[current].fAttribute = FATTR4_FILES_FREE; 570 values[current].fData.fValue64 = stream.GetUHyper(); 571 current++; 572 } 573 574 if (sIsAttrSet(FATTR4_FILES_TOTAL, bitmap, bcount)) { 575 values[current].fAttribute = FATTR4_FILES_TOTAL; 576 values[current].fData.fValue64 = stream.GetUHyper(); 577 current++; 578 } 579 580 if (sIsAttrSet(FATTR4_FS_LOCATIONS, bitmap, bcount)) { 581 values[current].fAttribute = FATTR4_FS_LOCATIONS; 582 583 FSLocations* locs = new FSLocations; 584 locs->fRootPath = sFlattenPathname(stream); 585 locs->fCount = stream.GetUInt(); 586 locs->fLocations = new FSLocation[locs->fCount]; 587 for (uint32 i = 0; i < locs->fCount; i++) { 588 locs->fLocations[i].fRootPath = sFlattenPathname(stream); 589 locs->fLocations[i].fCount = stream.GetUInt(); 590 locs->fLocations[i].fLocations = 591 new const char*[locs->fLocations[i].fCount]; 592 for (uint32 j = 0; j < locs->fLocations[i].fCount; j++) 593 locs->fLocations[i].fLocations[j] = stream.GetString(); 594 } 595 values[current].fData.fLocations = locs; 596 current++; 597 } 598 599 if (sIsAttrSet(FATTR4_MAXREAD, bitmap, bcount)) { 600 values[current].fAttribute = FATTR4_MAXREAD; 601 values[current].fData.fValue64 = stream.GetUHyper(); 602 current++; 603 } 604 605 if (sIsAttrSet(FATTR4_MAXWRITE, bitmap, bcount)) { 606 values[current].fAttribute = FATTR4_MAXWRITE; 607 values[current].fData.fValue64 = stream.GetUHyper(); 608 current++; 609 } 610 611 if (sIsAttrSet(FATTR4_MODE, bitmap, bcount)) { 612 values[current].fAttribute = FATTR4_MODE; 613 values[current].fData.fValue32 = stream.GetUInt(); 614 current++; 615 } 616 617 if (sIsAttrSet(FATTR4_NUMLINKS, bitmap, bcount)) { 618 values[current].fAttribute = FATTR4_NUMLINKS; 619 values[current].fData.fValue32 = stream.GetUInt(); 620 current++; 621 } 622 623 if (sIsAttrSet(FATTR4_OWNER, bitmap, bcount)) { 624 values[current].fAttribute = FATTR4_OWNER; 625 values[current].fFreePointer = true; 626 values[current].fData.fPointer = stream.GetString(); 627 current++; 628 } 629 630 if (sIsAttrSet(FATTR4_OWNER_GROUP, bitmap, bcount)) { 631 values[current].fAttribute = FATTR4_OWNER_GROUP; 632 values[current].fFreePointer = true; 633 values[current].fData.fPointer = stream.GetString(); 634 current++; 635 } 636 637 if (sIsAttrSet(FATTR4_SPACE_FREE, bitmap, bcount)) { 638 values[current].fAttribute = FATTR4_SPACE_FREE; 639 values[current].fData.fValue64 = stream.GetUHyper(); 640 current++; 641 } 642 643 if (sIsAttrSet(FATTR4_SPACE_TOTAL, bitmap, bcount)) { 644 values[current].fAttribute = FATTR4_SPACE_TOTAL; 645 values[current].fData.fValue64 = stream.GetUHyper(); 646 current++; 647 } 648 649 if (sIsAttrSet(FATTR4_TIME_ACCESS, bitmap, bcount)) { 650 values[current].fAttribute = FATTR4_TIME_ACCESS; 651 values[current].fFreePointer = true; 652 653 struct timespec ts; 654 ts.tv_sec = static_cast<time_t>(stream.GetHyper()); 655 ts.tv_nsec = static_cast<long>(stream.GetUInt()); 656 657 values[current].fData.fPointer = malloc(sizeof(ts)); 658 memcpy(values[current].fData.fPointer, &ts, sizeof(ts)); 659 current++; 660 } 661 662 if (sIsAttrSet(FATTR4_TIME_CREATE, bitmap, bcount)) { 663 values[current].fAttribute = FATTR4_TIME_CREATE; 664 values[current].fFreePointer = true; 665 666 struct timespec ts; 667 ts.tv_sec = static_cast<time_t>(stream.GetHyper()); 668 ts.tv_nsec = static_cast<long>(stream.GetUInt()); 669 670 values[current].fData.fPointer = malloc(sizeof(ts)); 671 memcpy(values[current].fData.fPointer, &ts, sizeof(ts)); 672 current++; 673 } 674 675 if (sIsAttrSet(FATTR4_TIME_METADATA, bitmap, bcount)) { 676 values[current].fAttribute = FATTR4_TIME_METADATA; 677 values[current].fFreePointer = true; 678 679 struct timespec ts; 680 ts.tv_sec = static_cast<time_t>(stream.GetHyper()); 681 ts.tv_nsec = static_cast<long>(stream.GetUInt()); 682 683 values[current].fData.fPointer = malloc(sizeof(ts)); 684 memcpy(values[current].fData.fPointer, &ts, sizeof(ts)); 685 current++; 686 } 687 688 if (sIsAttrSet(FATTR4_TIME_MODIFY, bitmap, bcount)) { 689 values[current].fAttribute = FATTR4_TIME_MODIFY; 690 values[current].fFreePointer = true; 691 692 struct timespec ts; 693 ts.tv_sec = static_cast<time_t>(stream.GetHyper()); 694 ts.tv_nsec = static_cast<long>(stream.GetUInt()); 695 696 values[current].fData.fPointer = malloc(sizeof(ts)); 697 memcpy(values[current].fData.fPointer, &ts, sizeof(ts)); 698 current++; 699 } 700 701 delete[] bitmap; 702 703 *count = attr_count; 704 *attrs = values; 705 return str.IsEOF() ? B_BAD_VALUE : B_OK; 706 } 707 708 709 status_t 710 ReplyInterpreter::_OperationError(Opcode op) 711 { 712 if (fDecodeError) 713 return B_BAD_VALUE; 714 715 if (fReply == NULL) 716 return B_NOT_INITIALIZED; 717 718 if (fReply->Error() != B_OK || fReply->Stream().IsEOF()) { 719 fDecodeError = true; 720 return fReply->Error(); 721 } 722 723 if (fReply->Stream().GetInt() != op) { 724 fDecodeError = true; 725 return B_BAD_VALUE; 726 } 727 728 status_t result = _NFS4ErrorToHaiku(fReply->Stream().GetUInt()); 729 if (result != B_OK) 730 fDecodeError = true; 731 return result; 732 } 733 734 735 status_t 736 ReplyInterpreter::_NFS4ErrorToHaiku(uint32 x) 737 { 738 switch (x) { 739 case NFS4_OK: return B_OK; 740 case NFS4ERR_PERM: return B_PERMISSION_DENIED; 741 case NFS4ERR_NOENT: return B_ENTRY_NOT_FOUND; 742 case NFS4ERR_IO: return B_IO_ERROR; 743 case NFS4ERR_NXIO: return B_DEVICE_NOT_FOUND; 744 case NFS4ERR_ACCESS: return B_NOT_ALLOWED; 745 case NFS4ERR_EXIST: return B_FILE_EXISTS; 746 case NFS4ERR_XDEV: return B_CROSS_DEVICE_LINK; 747 case NFS4ERR_NOTDIR: return B_NOT_A_DIRECTORY; 748 case NFS4ERR_ISDIR: return B_IS_A_DIRECTORY; 749 case NFS4ERR_INVAL: return B_BAD_VALUE; 750 case NFS4ERR_FBIG: return B_FILE_TOO_LARGE; 751 // ... 752 case NFS4ERR_DELAY: 753 case NFS4ERR_DENIED: 754 case NFS4ERR_LOCKED: 755 case NFS4ERR_GRACE: 756 return B_WOULD_BLOCK; 757 case NFS4ERR_FHEXPIRED: return B_ENTRY_NOT_FOUND; 758 // ... 759 default: return B_ERROR; 760 } 761 } 762 763