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