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