xref: /haiku/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
1 /*
2  * Copyright 2012-2020 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 <AutoDeleter.h>
15 #include <util/kernel_cpp.h>
16 
17 #include "Cookie.h"
18 
19 
20 #define ERROR(x...) dprintf("nfs4: " x)
21 
22 #ifdef DEBUG
23 #define TRACE(x...) dprintf("nfs4: " x)
24 #define CALLED() dprintf("nfs4: called %s", __func__)
25 #else
26 #define TRACE(x...)
27 #define CALLED()
28 #endif
29 
30 
31 static status_t
32 ProcessStream(RPC::Reply* reply, const char* callName)
33 {
34 	status_t result = reply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
35 	if (result != B_OK)
36 		TRACE("call %s failed!\n", callName);
37 	return result;
38 }
39 
40 
41 FSLocation::~FSLocation()
42 {
43 	CALLED();
44 
45 	if (fRootPath != NULL) {
46 		for (uint32 i = 0; fRootPath[i] != NULL; i++)
47 			free(const_cast<char*>(fRootPath[i]));
48 	}
49 	delete[] fRootPath;
50 
51 	for (uint32 i = 0; i < fCount; i++)
52 		free(const_cast<char*>(fLocations[i]));
53 	delete[] fLocations;
54 }
55 
56 
57 FSLocations::~FSLocations()
58 {
59 	CALLED();
60 
61 	if (fRootPath != NULL) {
62 		for (uint32 i = 0; fRootPath[i] != NULL; i++)
63 			free(const_cast<char*>(fRootPath[i]));
64 	}
65 	delete[] fRootPath;
66 
67 	delete[] fLocations;
68 }
69 
70 
71 AttrValue::AttrValue()
72 	:
73 	fAttribute(0),
74 	fFreePointer(false)
75 {
76 }
77 
78 
79 AttrValue::~AttrValue()
80 {
81 	CALLED();
82 
83 	if (fFreePointer)
84 		free(fData.fPointer);
85 	if (fAttribute == FATTR4_FS_LOCATIONS)
86 		delete fData.fLocations;
87 }
88 
89 
90 DirEntry::DirEntry()
91 	:
92 	fName(NULL),
93 	fAttrs(NULL),
94 	fAttrCount(0)
95 {
96 }
97 
98 
99 DirEntry::~DirEntry()
100 {
101 	free(const_cast<char*>(fName));
102 	delete[] fAttrs;
103 }
104 
105 
106 ReplyInterpreter::ReplyInterpreter(RPC::Reply* reply)
107 	:
108 	fNFS4Error(NFS4_OK),
109 	fDecodeError(false),
110 	fReply(reply)
111 {
112 	CALLED();
113 
114 	if (reply != NULL)
115 		_ParseHeader();
116 }
117 
118 
119 ReplyInterpreter::~ReplyInterpreter()
120 {
121 	delete fReply;
122 }
123 
124 
125 void
126 ReplyInterpreter::_ParseHeader()
127 {
128 	CALLED();
129 
130 	fNFS4Error = fReply->Stream().GetUInt();
131 	fReply->Stream().GetOpaque(NULL);
132 	fReply->Stream().GetUInt();
133 }
134 
135 
136 status_t
137 ReplyInterpreter::Access(uint32* supported, uint32* allowed)
138 {
139 	CALLED();
140 
141 	status_t res = _OperationError(OpAccess);
142 	if (res != B_OK)
143 		return res;
144 
145 	uint32 support = fReply->Stream().GetUInt();
146 	uint32 allow = fReply->Stream().GetUInt();
147 
148 	if (supported != NULL)
149 		*supported = support;
150 	if (allowed != NULL)
151 		*allowed = allow;
152 
153 	return ProcessStream(fReply, __func__);
154 }
155 
156 
157 status_t
158 ReplyInterpreter::Close()
159 {
160 	CALLED();
161 
162 	status_t res = _OperationError(OpClose);
163 	if (res != B_OK)
164 		return res;
165 
166 	fReply->Stream().GetUInt();
167 	fReply->Stream().GetUInt();
168 	fReply->Stream().GetUInt();
169 	fReply->Stream().GetUInt();
170 
171 	return ProcessStream(fReply, __func__);
172 }
173 
174 
175 status_t
176 ReplyInterpreter::Commit()
177 {
178 	CALLED();
179 
180 	status_t res = _OperationError(OpCommit);
181 	if (res != B_OK)
182 		return res;
183 
184 	fReply->Stream().GetOpaque(NULL);
185 
186 	return ProcessStream(fReply, __func__);
187 }
188 
189 
190 status_t
191 ReplyInterpreter::Create(uint64* before, uint64* after, bool& atomic)
192 {
193 	CALLED();
194 
195 	status_t res = _OperationError(OpCreate);
196 	if (res != B_OK)
197 		return res;
198 
199 	atomic = fReply->Stream().GetBoolean();
200 	*before = fReply->Stream().GetUHyper();
201 	*after = fReply->Stream().GetUHyper();
202 
203 	uint32 count = fReply->Stream().GetUInt();
204 	for (uint32 i = 0; i < count; i++)
205 		fReply->Stream().GetUInt();
206 
207 	return ProcessStream(fReply, __func__);
208 }
209 
210 
211 // Bit Twiddling Hacks
212 // http://graphics.stanford.edu/~seander/bithacks.html
213 static inline uint32 CountBits(uint32 v)
214 {
215 	CALLED();
216 
217 	v = v - ((v >> 1) & 0x55555555);
218 	v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
219 	return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
220 }
221 
222 
223 status_t
224 ReplyInterpreter::GetAttr(AttrValue** attrs, uint32* count)
225 {
226 	CALLED();
227 
228 	status_t res = _OperationError(OpGetAttr);
229 	if (res != B_OK)
230 		return res;
231 
232 	return _DecodeAttrs(fReply->Stream(), attrs, count);
233 }
234 
235 
236 status_t
237 ReplyInterpreter::GetFH(FileHandle* fh)
238 {
239 	CALLED();
240 
241 	status_t res = _OperationError(OpGetFH);
242 	if (res != B_OK)
243 		return res;
244 
245 	uint32 size;
246 	const void* ptr = fReply->Stream().GetOpaque(&size);
247 	if (ptr == NULL || size > NFS4_FHSIZE) {
248 		ERROR("Unable to %s!\n", __func__);
249 		return B_BAD_VALUE;
250 	}
251 
252 	if (fh != NULL) {
253 		fh->fSize = size;
254 		memcpy(fh->fData, ptr, size);
255 	}
256 
257 	return ProcessStream(fReply, __func__);
258 }
259 
260 
261 status_t
262 ReplyInterpreter::Link(uint64* before, uint64* after, bool& atomic)
263 {
264 	CALLED();
265 
266 	status_t res = _OperationError(OpLink);
267 	if (res != B_OK)
268 		return res;
269 
270 	atomic = fReply->Stream().GetBoolean();
271 	*before = fReply->Stream().GetUHyper();
272 	*after = fReply->Stream().GetUHyper();
273 
274 	return ProcessStream(fReply, __func__);
275 }
276 
277 
278 status_t
279 ReplyInterpreter::Lock(LockInfo* linfo)
280 {
281 	CALLED();
282 
283 	status_t res = _OperationError(OpLock);
284 	if (res != B_OK)
285 		return res;
286 
287 	linfo->fOwner->fStateSeq = fReply->Stream().GetUInt();
288 	linfo->fOwner->fStateId[0] = fReply->Stream().GetUInt();
289 	linfo->fOwner->fStateId[1] = fReply->Stream().GetUInt();
290 	linfo->fOwner->fStateId[2] = fReply->Stream().GetUInt();
291 
292 	return ProcessStream(fReply, __func__);
293 }
294 
295 
296 status_t
297 ReplyInterpreter::LockT(uint64* pos, uint64* len, LockType* type)
298 {
299 	CALLED();
300 
301 	status_t res = _OperationError(OpLockT);
302 	if (res != B_WOULD_BLOCK || NFS4Error() != NFS4ERR_DENIED)
303 		return res;
304 
305 	*pos = fReply->Stream().GetUHyper();
306 	*len = fReply->Stream().GetUHyper();
307 	*type = static_cast<LockType>(fReply->Stream().GetInt());
308 
309 	fReply->Stream().GetUHyper();
310 	fReply->Stream().GetOpaque(NULL);
311 
312 	return ProcessStream(fReply, __func__);
313 }
314 
315 
316 status_t
317 ReplyInterpreter::LockU(LockInfo* linfo)
318 {
319 	CALLED();
320 
321 	status_t res = _OperationError(OpLockU);
322 	if (res != B_OK)
323 		return res;
324 
325 	linfo->fOwner->fStateSeq = fReply->Stream().GetUInt();
326 	linfo->fOwner->fStateId[0] = fReply->Stream().GetUInt();
327 	linfo->fOwner->fStateId[1] = fReply->Stream().GetUInt();
328 	linfo->fOwner->fStateId[2] = fReply->Stream().GetUInt();
329 
330 	return ProcessStream(fReply, __func__);
331 }
332 
333 
334 status_t
335 ReplyInterpreter::Open(uint32* id, uint32* seq, bool* confirm,
336 	OpenDelegationData* delegData, ChangeInfo* changeInfo)
337 {
338 	CALLED();
339 
340 	status_t res = _OperationError(OpOpen);
341 	if (res != B_OK)
342 		return res;
343 
344 	*seq = fReply->Stream().GetUInt();
345 	id[0] = fReply->Stream().GetUInt();
346 	id[1] = fReply->Stream().GetUInt();
347 	id[2] = fReply->Stream().GetUInt();
348 
349 	// change info
350 	bool atomic = fReply->Stream().GetBoolean();
351 	uint64 before = fReply->Stream().GetUHyper();
352 	uint64 after = fReply->Stream().GetUHyper();
353 	if (changeInfo != NULL) {
354 		changeInfo->fAtomic = atomic;
355 		changeInfo->fBefore = before;
356 		changeInfo->fAfter = after;
357 	}
358 
359 	uint32 flags = fReply->Stream().GetUInt();
360 	*confirm = (flags & OPEN4_RESULT_CONFIRM) == OPEN4_RESULT_CONFIRM;
361 
362 	// attrmask
363 	uint32 bcount = fReply->Stream().GetUInt();
364 	for (uint32 i = 0; i < bcount; i++)
365 		fReply->Stream().GetUInt();
366 
367 	// delegation info
368 	uint32 delegation = fReply->Stream().GetUInt();
369 	OpenDelegationData data;
370 	if (delegData == NULL)
371 		delegData = &data;
372 
373 	if (delegation == OPEN_DELEGATE_NONE) {
374 		delegData->fType = OPEN_DELEGATE_NONE;
375 		return ProcessStream(fReply, __func__);
376 	}
377 
378 	delegData->fStateSeq = fReply->Stream().GetUInt();
379 	delegData->fStateID[0] = fReply->Stream().GetUInt();
380 	delegData->fStateID[1] = fReply->Stream().GetUInt();
381 	delegData->fStateID[2] = fReply->Stream().GetUInt();
382 
383 	delegData->fRecall = fReply->Stream().GetBoolean();
384 
385 	switch (delegation) {
386 		case OPEN_DELEGATE_READ:
387 			delegData->fType = OPEN_DELEGATE_READ;
388 			break;
389 		case OPEN_DELEGATE_WRITE:
390 			delegData->fType = OPEN_DELEGATE_WRITE;
391 
392 			int32 limitBy = fReply->Stream().GetInt();
393 			if (limitBy == NFS_LIMIT_SIZE)
394 				delegData->fSpaceLimit = fReply->Stream().GetUHyper();
395 			else if (limitBy == NFS_LIMIT_BLOCKS) {
396 				uint32 numBlocks = fReply->Stream().GetUInt();
397 				delegData->fSpaceLimit = fReply->Stream().GetUInt() * numBlocks;
398 			}
399 			break;
400 	}
401 
402 	// ACE data
403 	fReply->Stream().GetUInt();
404 	fReply->Stream().GetUInt();
405 	fReply->Stream().GetUInt();
406 	fReply->Stream().GetOpaque(NULL);
407 
408 	return ProcessStream(fReply, __func__);
409 }
410 
411 
412 status_t
413 ReplyInterpreter::OpenConfirm(uint32* stateSeq)
414 {
415 	CALLED();
416 
417 	status_t res = _OperationError(OpOpenConfirm);
418 	if (res != B_OK)
419 		return res;
420 
421 	*stateSeq = fReply->Stream().GetUInt();
422 	fReply->Stream().GetUInt();
423 	fReply->Stream().GetUInt();
424 	fReply->Stream().GetUInt();
425 
426 	return ProcessStream(fReply, __func__);
427 }
428 
429 
430 status_t
431 ReplyInterpreter::Read(void* buffer, uint32* size, bool* eof)
432 {
433 	CALLED();
434 
435 	status_t res = _OperationError(OpRead);
436 	if (res != B_OK)
437 		return res;
438 
439 	*eof = fReply->Stream().GetBoolean();
440 	const void* ptr = fReply->Stream().GetOpaque(size);
441 	memcpy(buffer, ptr, *size);
442 
443 	return ProcessStream(fReply, __func__);
444 }
445 
446 
447 status_t
448 ReplyInterpreter::ReadDir(uint64* cookie, uint64* cookieVerf,
449 	DirEntry** dirents, uint32* _count,	bool* eof)
450 {
451 	CALLED();
452 
453 	status_t res = _OperationError(OpReadDir);
454 	if (res != B_OK)
455 		return res;
456 
457 	*cookieVerf = fReply->Stream().GetUHyper();
458 
459 	bool isNext;
460 	uint32 count = 0;
461 
462 	// TODO: using  list instead of array would make this much more elegant
463 	// and efficient
464 	XDR::Stream::Position dataStart = fReply->Stream().Current();
465 	isNext = fReply->Stream().GetBoolean();
466 	while (isNext) {
467 		fReply->Stream().GetUHyper();
468 
469 		free(fReply->Stream().GetString());
470 		AttrValue* values;
471 		uint32 attrCount;
472 		_DecodeAttrs(fReply->Stream(), &values,	&attrCount);
473 		delete[] values;
474 
475 		count++;
476 
477 		isNext = fReply->Stream().GetBoolean();
478 	}
479 
480 	DirEntry* entries = new(std::nothrow) DirEntry[count];
481 	if (entries == NULL)
482 		return B_NO_MEMORY;
483 
484 	count = 0;
485 	fReply->Stream().SetPosition(dataStart);
486 	isNext = fReply->Stream().GetBoolean();
487 	while (isNext) {
488 		*cookie = fReply->Stream().GetUHyper();
489 
490 		entries[count].fName = fReply->Stream().GetString();
491 		_DecodeAttrs(fReply->Stream(), &entries[count].fAttrs,
492 			&entries[count].fAttrCount);
493 
494 		count++;
495 
496 		isNext = fReply->Stream().GetBoolean();
497 	}
498 	*eof = fReply->Stream().GetBoolean();
499 
500 	*_count = count;
501 	*dirents = entries;
502 
503 	if (fReply->Stream().IsEOF()) {
504 		delete[] entries;
505 		ERROR("Unable to %s!\n", __func__);
506 		return B_BAD_VALUE;
507 	}
508 
509 	return B_OK;
510 }
511 
512 
513 status_t
514 ReplyInterpreter::ReadLink(void* buffer, uint32* size, uint32 maxSize)
515 {
516 	CALLED();
517 
518 	status_t res = _OperationError(OpReadLink);
519 	if (res != B_OK)
520 		return res;
521 
522 	const void* ptr = fReply->Stream().GetOpaque(size);
523 	memcpy(buffer, ptr, min_c(*size, maxSize));
524 
525 	return ProcessStream(fReply, __func__);
526 }
527 
528 
529 status_t
530 ReplyInterpreter::Remove(uint64* before, uint64* after, bool& atomic)
531 {
532 	CALLED();
533 
534 	status_t res = _OperationError(OpRemove);
535 	if (res != B_OK)
536 		return res;
537 
538 	atomic = fReply->Stream().GetBoolean();
539 	*before = fReply->Stream().GetUHyper();
540 	*after = fReply->Stream().GetUHyper();
541 
542 	return ProcessStream(fReply, __func__);
543 }
544 
545 
546 status_t
547 ReplyInterpreter::Rename(uint64* fromBefore, uint64* fromAfter,
548 	bool& fromAtomic, uint64* toBefore, uint64* toAfter, bool& toAtomic)
549 {
550 	CALLED();
551 
552 	status_t res = _OperationError(OpRename);
553 	if (res != B_OK)
554 		return res;
555 
556 	fromAtomic = fReply->Stream().GetBoolean();
557 	*fromBefore = fReply->Stream().GetUHyper();
558 	*fromAfter = fReply->Stream().GetUHyper();
559 
560 	toAtomic = fReply->Stream().GetBoolean();
561 	*toBefore = fReply->Stream().GetUHyper();
562 	*toAfter = fReply->Stream().GetUHyper();
563 
564 	return ProcessStream(fReply, __func__);
565 }
566 
567 
568 status_t
569 ReplyInterpreter::SetAttr()
570 {
571 	CALLED();
572 
573 	status_t res = _OperationError(OpSetAttr);
574 	if (res != B_OK)
575 		return res;
576 
577 	uint32 bcount = fReply->Stream().GetUInt();
578 	for (uint32 i = 0; i < bcount; i++)
579 		fReply->Stream().GetUInt();
580 
581 	return ProcessStream(fReply, __func__);
582 }
583 
584 
585 status_t
586 ReplyInterpreter::SetClientID(uint64* clientid, uint64* verifier)
587 {
588 	CALLED();
589 
590 	status_t res = _OperationError(OpSetClientID);
591 	if (res != B_OK)
592 		return res;
593 
594 	*clientid = fReply->Stream().GetUHyper();
595 	*verifier = fReply->Stream().GetUHyper();
596 
597 	return ProcessStream(fReply, __func__);
598 }
599 
600 
601 status_t
602 ReplyInterpreter::Write(uint32* size)
603 {
604 	CALLED();
605 
606 	status_t res = _OperationError(OpWrite);
607 	if (res != B_OK)
608 		return res;
609 
610 	*size = fReply->Stream().GetUInt();
611 	fReply->Stream().GetInt();
612 	fReply->Stream().GetUHyper();
613 
614 	return ProcessStream(fReply, __func__);
615 }
616 
617 
618 const char**
619 ReplyInterpreter::_GetPath(XDR::ReadStream& stream)
620 {
621 	CALLED();
622 
623 	uint32 count = stream.GetUInt();
624 	char** path = new char*[count + 1];
625 	if (path == NULL)
626 		return NULL;
627 
628 	uint32 i;
629 	for (i = 0; i < count; i++) {
630 		path[i] = stream.GetString();
631 		if (path[i] == NULL)
632 			goto out;
633 	}
634 	path[count] = NULL;
635 
636 	return const_cast<const char**>(path);
637 
638 out:
639 	for (uint32 j = 0; j < i; j++)
640 		free(path[i]);
641 	delete[] path;
642 	return NULL;
643 }
644 
645 
646 status_t
647 ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs,
648 	uint32* count)
649 {
650 	CALLED();
651 
652 	uint32 bcount = fReply->Stream().GetUInt();
653 	uint32* bitmap = new(std::nothrow) uint32[bcount];
654 	if (bitmap == NULL)
655 		return B_NO_MEMORY;
656 	ArrayDeleter<uint32> _(bitmap);
657 
658 	uint32 attr_count = 0;
659 	for (uint32 i = 0; i < bcount; i++) {
660 		bitmap[i] = str.GetUInt();
661 		attr_count += CountBits(bitmap[i]);
662 	}
663 
664 	if (attr_count == 0) {
665 		*attrs = NULL;
666 		*count = 0;
667 		return B_OK;
668 	} else if (attr_count > FATTR4_MAXIMUM_ATTR_ID) {
669 		ERROR("too many attr!\n");
670 		return B_BAD_VALUE;
671 	}
672 
673 	uint32 size;
674 	const void* ptr = str.GetOpaque(&size);
675 	XDR::ReadStream stream(const_cast<void*>(ptr), size);
676 
677 	AttrValue* values = new(std::nothrow) AttrValue[attr_count];
678 	if (values == NULL)
679 		return B_NO_MEMORY;
680 
681 	uint32 current = 0;
682 
683 	if (sIsAttrSet(FATTR4_SUPPORTED_ATTRS, bitmap, bcount)) {
684 		values[current].fAttribute = FATTR4_SUPPORTED_ATTRS;
685 		uint32 count = stream.GetInt();
686 		uint32 i;
687 		// two uint32 are enough for NFS4, not for NFS4.1
688 		for (i = 0; i < min_c(count, 2); i++)
689 			((uint32*)&values[current].fData.fValue64)[i] = stream.GetUInt();
690 		for (; i < count; i++)
691 			stream.GetUInt();
692 		current++;
693 	}
694 
695 	if (sIsAttrSet(FATTR4_TYPE, bitmap, bcount)) {
696 		values[current].fAttribute = FATTR4_TYPE;
697 		values[current].fData.fValue32 = stream.GetInt();
698 		current++;
699 	}
700 
701 	if (sIsAttrSet(FATTR4_FH_EXPIRE_TYPE, bitmap, bcount)) {
702 		values[current].fAttribute = FATTR4_FH_EXPIRE_TYPE;
703 		values[current].fData.fValue32 = stream.GetUInt();
704 		current++;
705 	}
706 
707 	if (sIsAttrSet(FATTR4_CHANGE, bitmap, bcount)) {
708 		values[current].fAttribute = FATTR4_CHANGE;
709 		values[current].fData.fValue64 = stream.GetUHyper();
710 		current++;
711 	}
712 
713 	if (sIsAttrSet(FATTR4_SIZE, bitmap, bcount)) {
714 		values[current].fAttribute = FATTR4_SIZE;
715 		values[current].fData.fValue64 = stream.GetUHyper();
716 		current++;
717 	}
718 
719 	if (sIsAttrSet(FATTR4_FSID, bitmap, bcount)) {
720 		values[current].fAttribute = FATTR4_FSID;
721 		values[current].fFreePointer = true;
722 
723 		FileSystemId fsid;
724 		fsid.fMajor = stream.GetUHyper();
725 		fsid.fMinor = stream.GetUHyper();
726 
727 		values[current].fData.fPointer = malloc(sizeof(fsid));
728 		memcpy(values[current].fData.fPointer, &fsid, sizeof(fsid));
729 		current++;
730 	}
731 
732 	if (sIsAttrSet(FATTR4_LEASE_TIME, bitmap, bcount)) {
733 		values[current].fAttribute = FATTR4_LEASE_TIME;
734 		values[current].fData.fValue32 = stream.GetUInt();
735 		current++;
736 	}
737 
738 	if (sIsAttrSet(FATTR4_FILEID, bitmap, bcount)) {
739 		values[current].fAttribute = FATTR4_FILEID;
740 		values[current].fData.fValue64 = stream.GetUHyper();
741 		current++;
742 	}
743 
744 	if (sIsAttrSet(FATTR4_FILES_FREE, bitmap, bcount)) {
745 		values[current].fAttribute = FATTR4_FILES_FREE;
746 		values[current].fData.fValue64 = stream.GetUHyper();
747 		current++;
748 	}
749 
750 	if (sIsAttrSet(FATTR4_FILES_TOTAL, bitmap, bcount)) {
751 		values[current].fAttribute = FATTR4_FILES_TOTAL;
752 		values[current].fData.fValue64 = stream.GetUHyper();
753 		current++;
754 	}
755 
756 	if (sIsAttrSet(FATTR4_FS_LOCATIONS, bitmap, bcount)) {
757 		values[current].fAttribute = FATTR4_FS_LOCATIONS;
758 
759 		FSLocations* locs = new FSLocations;
760 		locs->fRootPath = _GetPath(stream);
761 		locs->fCount = stream.GetUInt();
762 		locs->fLocations = new FSLocation[locs->fCount];
763 		for (uint32 i = 0; i < locs->fCount; i++) {
764 			locs->fLocations[i].fRootPath = _GetPath(stream);
765 			locs->fLocations[i].fCount = stream.GetUInt();
766 			locs->fLocations[i].fLocations
767 				= new const char*[locs->fLocations[i].fCount];
768 			for (uint32 j = 0; j < locs->fLocations[i].fCount; j++)
769 				locs->fLocations[i].fLocations[j] = stream.GetString();
770 		}
771 		values[current].fData.fLocations = locs;
772 		current++;
773 	}
774 
775 	if (sIsAttrSet(FATTR4_MAXREAD, bitmap, bcount)) {
776 		values[current].fAttribute = FATTR4_MAXREAD;
777 		values[current].fData.fValue64 = stream.GetUHyper();
778 		current++;
779 	}
780 
781 	if (sIsAttrSet(FATTR4_MAXWRITE, bitmap, bcount)) {
782 		values[current].fAttribute = FATTR4_MAXWRITE;
783 		values[current].fData.fValue64 = stream.GetUHyper();
784 		current++;
785 	}
786 
787 	if (sIsAttrSet(FATTR4_MODE, bitmap, bcount)) {
788 		values[current].fAttribute = FATTR4_MODE;
789 		values[current].fData.fValue32 = stream.GetUInt();
790 		current++;
791 	}
792 
793 	if (sIsAttrSet(FATTR4_NUMLINKS, bitmap, bcount)) {
794 		values[current].fAttribute = FATTR4_NUMLINKS;
795 		values[current].fData.fValue32 = stream.GetUInt();
796 		current++;
797 	}
798 
799 	if (sIsAttrSet(FATTR4_OWNER, bitmap, bcount)) {
800 		values[current].fAttribute = FATTR4_OWNER;
801 		values[current].fFreePointer = true;
802 		values[current].fData.fPointer = stream.GetString();
803 		current++;
804 	}
805 
806 	if (sIsAttrSet(FATTR4_OWNER_GROUP, bitmap, bcount)) {
807 		values[current].fAttribute = FATTR4_OWNER_GROUP;
808 		values[current].fFreePointer = true;
809 		values[current].fData.fPointer = stream.GetString();
810 		current++;
811 	}
812 
813 	if (sIsAttrSet(FATTR4_SPACE_FREE, bitmap, bcount)) {
814 		values[current].fAttribute = FATTR4_SPACE_FREE;
815 		values[current].fData.fValue64 = stream.GetUHyper();
816 		current++;
817 	}
818 
819 	if (sIsAttrSet(FATTR4_SPACE_TOTAL, bitmap, bcount)) {
820 		values[current].fAttribute = FATTR4_SPACE_TOTAL;
821 		values[current].fData.fValue64 = stream.GetUHyper();
822 		current++;
823 	}
824 
825 	if (sIsAttrSet(FATTR4_TIME_ACCESS, bitmap, bcount)) {
826 		values[current].fAttribute = FATTR4_TIME_ACCESS;
827 		values[current].fFreePointer = true;
828 
829 		struct timespec ts;
830 		ts.tv_sec = static_cast<time_t>(stream.GetHyper());
831 		ts.tv_nsec = static_cast<long>(stream.GetUInt());
832 
833 		values[current].fData.fPointer = malloc(sizeof(ts));
834 		memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
835 		current++;
836 	}
837 
838 	if (sIsAttrSet(FATTR4_TIME_CREATE, bitmap, bcount)) {
839 		values[current].fAttribute = FATTR4_TIME_CREATE;
840 		values[current].fFreePointer = true;
841 
842 		struct timespec ts;
843 		ts.tv_sec = static_cast<time_t>(stream.GetHyper());
844 		ts.tv_nsec = static_cast<long>(stream.GetUInt());
845 
846 		values[current].fData.fPointer = malloc(sizeof(ts));
847 		memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
848 		current++;
849 	}
850 
851 	if (sIsAttrSet(FATTR4_TIME_METADATA, bitmap, bcount)) {
852 		values[current].fAttribute = FATTR4_TIME_METADATA;
853 		values[current].fFreePointer = true;
854 
855 		struct timespec ts;
856 		ts.tv_sec = static_cast<time_t>(stream.GetHyper());
857 		ts.tv_nsec = static_cast<long>(stream.GetUInt());
858 
859 		values[current].fData.fPointer = malloc(sizeof(ts));
860 		memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
861 		current++;
862 	}
863 
864 	if (sIsAttrSet(FATTR4_TIME_MODIFY, bitmap, bcount)) {
865 		values[current].fAttribute = FATTR4_TIME_MODIFY;
866 		values[current].fFreePointer = true;
867 
868 		struct timespec ts;
869 		ts.tv_sec = static_cast<time_t>(stream.GetHyper());
870 		ts.tv_nsec = static_cast<long>(stream.GetUInt());
871 
872 		values[current].fData.fPointer = malloc(sizeof(ts));
873 		memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
874 		current++;
875 	}
876 
877 	*count = attr_count;
878 	*attrs = values;
879 	if (str.IsEOF()) {
880 		delete[] values;
881 		ERROR("call %s failed!\n", __func__);
882 		return B_BAD_VALUE;
883 	}
884 	return B_OK;
885 }
886 
887 
888 status_t
889 ReplyInterpreter::_OperationError(Opcode op)
890 {
891 	if (fDecodeError) {
892 		ERROR("Decode Error!\n");
893 		return B_BAD_VALUE;
894 	}
895 
896 	if (fReply == NULL)
897 		return B_NOT_INITIALIZED;
898 
899 	if (fReply->Error() != B_OK || fReply->Stream().IsEOF()) {
900 		ERROR("Error not B_OK or empty stream!\n");
901 		fDecodeError = true;
902 		return fReply->Error();
903 	}
904 
905 	if (fReply->Stream().GetInt() != op) {
906 		ERROR("Stream GetInt != op!\n");
907 		fDecodeError = true;
908 		return B_BAD_VALUE;
909 	}
910 
911 	status_t result = _NFS4ErrorToHaiku(fReply->Stream().GetUInt());
912 	if (result != B_OK) {
913 		ERROR("NFS Error: %s\n", strerror(result));
914 		fDecodeError = true;
915 	}
916 	return result;
917 }
918 
919 
920 status_t
921 ReplyInterpreter::_NFS4ErrorToHaiku(uint32 x)
922 {
923 	switch (x) {
924 		case NFS4_OK:			return B_OK;
925 		case NFS4ERR_PERM:		return B_PERMISSION_DENIED;
926 		case NFS4ERR_NOENT:		return B_ENTRY_NOT_FOUND;
927 		case NFS4ERR_IO:		return B_IO_ERROR;
928 		case NFS4ERR_NXIO:		return B_DEVICE_NOT_FOUND;
929 		case NFS4ERR_ACCESS:	return B_NOT_ALLOWED;
930 		case NFS4ERR_EXIST:		return B_FILE_EXISTS;
931 		case NFS4ERR_XDEV:		return B_CROSS_DEVICE_LINK;
932 		case NFS4ERR_NOTDIR:	return B_NOT_A_DIRECTORY;
933 		case NFS4ERR_ISDIR:		return B_IS_A_DIRECTORY;
934 		case NFS4ERR_INVAL:		return B_BAD_VALUE;
935 		case NFS4ERR_FBIG:		return B_FILE_TOO_LARGE;
936 		case NFS4ERR_NOTSUPP:	return B_UNSUPPORTED;
937 		case NFS4ERR_ROFS:		return B_READ_ONLY_DEVICE;
938 		case NFS4ERR_NAMETOOLONG:	return B_NAME_TOO_LONG;
939 		case NFS4ERR_NOTEMPTY:	return B_DIRECTORY_NOT_EMPTY;
940 		// ...
941 		case NFS4ERR_DELAY:
942 		case NFS4ERR_DENIED:
943 		case NFS4ERR_LOCKED:
944 		case NFS4ERR_GRACE:
945 								return B_WOULD_BLOCK;
946 
947 		case NFS4ERR_STALE:
948 		case NFS4ERR_FHEXPIRED:
949 								return B_ENTRY_NOT_FOUND;
950 		// ...
951 		default:				return B_ERROR;
952 	}
953 }
954 
955