xref: /haiku/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp (revision c5a88cf7e6c5f032e1aecea28cf3d90d0c1b7310)
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 	fAttribute(0),
38 	fFreePointer(false)
39 {
40 }
41 
42 
43 AttrValue::~AttrValue()
44 {
45 	if (fFreePointer)
46 		free(fData.fPointer);
47 	if (fAttribute == FATTR4_FS_LOCATIONS)
48 		delete fData.fLocations;
49 }
50 
51 
52 DirEntry::DirEntry()
53 	:
54 	fName(NULL),
55 	fAttrs(NULL),
56 	fAttrCount(0)
57 {
58 }
59 
60 
61 DirEntry::~DirEntry()
62 {
63 	free(const_cast<char*>(fName));
64 	delete[] fAttrs;
65 }
66 
67 
68 ReplyInterpreter::ReplyInterpreter(RPC::Reply* reply)
69 	:
70 	fNFS4Error(NFS4_OK),
71 	fDecodeError(false),
72 	fReply(reply)
73 {
74 	if (reply != NULL)
75 		_ParseHeader();
76 }
77 
78 
79 ReplyInterpreter::~ReplyInterpreter()
80 {
81 	delete fReply;
82 }
83 
84 
85 void
86 ReplyInterpreter::_ParseHeader()
87 {
88 	fNFS4Error = fReply->Stream().GetUInt();
89 	fReply->Stream().GetOpaque(NULL);
90 	fReply->Stream().GetUInt();
91 }
92 
93 
94 status_t
95 ReplyInterpreter::Access(uint32* supported, uint32* allowed)
96 {
97 	status_t res = _OperationError(OpAccess);
98 	if (res != B_OK)
99 		return res;
100 
101 	uint32 support = fReply->Stream().GetUInt();
102 	uint32 allow = fReply->Stream().GetUInt();
103 
104 	if (supported != NULL)
105 		*supported = support;
106 	if (allowed != NULL)
107 		*allowed = allow;
108 
109 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
110 }
111 
112 
113 status_t
114 ReplyInterpreter::Close()
115 {
116 	status_t res = _OperationError(OpClose);
117 	if (res != B_OK)
118 		return res;
119 
120 	fReply->Stream().GetUInt();
121 	fReply->Stream().GetUInt();
122 	fReply->Stream().GetUInt();
123 	fReply->Stream().GetUInt();
124 
125 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
126 }
127 
128 
129 status_t
130 ReplyInterpreter::Commit()
131 {
132 	status_t res = _OperationError(OpCommit);
133 	if (res != B_OK)
134 		return res;
135 
136 	fReply->Stream().GetOpaque(NULL);
137 
138 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
139 }
140 
141 
142 status_t
143 ReplyInterpreter::Create(uint64* before, uint64* after, bool& atomic)
144 {
145 	status_t res = _OperationError(OpCreate);
146 	if (res != B_OK)
147 		return res;
148 
149 	atomic = fReply->Stream().GetBoolean();
150 	*before = fReply->Stream().GetUHyper();
151 	*after = fReply->Stream().GetUHyper();
152 
153 	uint32 count = fReply->Stream().GetUInt();
154 	for (uint32 i = 0; i < count; i++)
155 		fReply->Stream().GetUInt();
156 
157 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
158 }
159 
160 
161 // Bit Twiddling Hacks
162 // http://graphics.stanford.edu/~seander/bithacks.html
163 static inline uint32 CountBits(uint32 v)
164 {
165 	v = v - ((v >> 1) & 0x55555555);
166 	v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
167 	return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
168 }
169 
170 
171 status_t
172 ReplyInterpreter::GetAttr(AttrValue** attrs, uint32* count)
173 {
174 	status_t res = _OperationError(OpGetAttr);
175 	if (res != B_OK)
176 		return res;
177 
178 	return _DecodeAttrs(fReply->Stream(), attrs, count);
179 }
180 
181 
182 status_t
183 ReplyInterpreter::GetFH(FileHandle* fh)
184 {
185 	status_t res = _OperationError(OpGetFH);
186 	if (res != B_OK)
187 		return res;
188 
189 	uint32 size;
190 	const void* ptr = fReply->Stream().GetOpaque(&size);
191 	if (ptr == NULL || size > NFS4_FHSIZE)
192 		return B_BAD_VALUE;
193 
194 	if (fh != NULL) {
195 		fh->fSize = size;
196 		memcpy(fh->fData, ptr, size);
197 	}
198 
199 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
200 }
201 
202 
203 status_t
204 ReplyInterpreter::Link(uint64* before, uint64* after, bool& atomic)
205 {
206 	status_t res = _OperationError(OpLink);
207 	if (res != B_OK)
208 		return res;
209 
210 	atomic = fReply->Stream().GetBoolean();
211 	*before = fReply->Stream().GetUHyper();
212 	*after = fReply->Stream().GetUHyper();
213 
214 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
215 }
216 
217 
218 status_t
219 ReplyInterpreter::Lock(LockInfo* linfo)
220 {
221 	status_t res = _OperationError(OpLock);
222 	if (res != B_OK)
223 		return res;
224 
225 	linfo->fOwner->fStateSeq = fReply->Stream().GetUInt();
226 	linfo->fOwner->fStateId[0] = fReply->Stream().GetUInt();
227 	linfo->fOwner->fStateId[1] = fReply->Stream().GetUInt();
228 	linfo->fOwner->fStateId[2] = fReply->Stream().GetUInt();
229 
230 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
231 }
232 
233 
234 status_t
235 ReplyInterpreter::LockT(uint64* pos, uint64* len, LockType* type)
236 {
237 	status_t res = _OperationError(OpLockT);
238 	if (res != B_WOULD_BLOCK || NFS4Error() != NFS4ERR_DENIED)
239 		return res;
240 
241 	*pos = fReply->Stream().GetUHyper();
242 	*len = fReply->Stream().GetUHyper();
243 	*type = static_cast<LockType>(fReply->Stream().GetInt());
244 
245 	fReply->Stream().GetUHyper();
246 	fReply->Stream().GetOpaque(NULL);
247 
248 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
249 }
250 
251 
252 status_t
253 ReplyInterpreter::LockU(LockInfo* linfo)
254 {
255 	status_t res = _OperationError(OpLockU);
256 	if (res != B_OK)
257 		return res;
258 
259 	linfo->fOwner->fStateSeq = fReply->Stream().GetUInt();
260 	linfo->fOwner->fStateId[0] = fReply->Stream().GetUInt();
261 	linfo->fOwner->fStateId[1] = fReply->Stream().GetUInt();
262 	linfo->fOwner->fStateId[2] = fReply->Stream().GetUInt();
263 
264 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
265 }
266 
267 
268 status_t
269 ReplyInterpreter::Open(uint32* id, uint32* seq, bool* confirm,
270 	OpenDelegationData* delegData, ChangeInfo* changeInfo)
271 {
272 	status_t res = _OperationError(OpOpen);
273 	if (res != B_OK)
274 		return res;
275 
276 	*seq = fReply->Stream().GetUInt();
277 	id[0] = fReply->Stream().GetUInt();
278 	id[1] = fReply->Stream().GetUInt();
279 	id[2] = fReply->Stream().GetUInt();
280 
281 	// change info
282 	bool atomic = fReply->Stream().GetBoolean();
283 	uint64 before = fReply->Stream().GetUHyper();
284 	uint64 after = fReply->Stream().GetUHyper();
285 	if (changeInfo != NULL) {
286 		changeInfo->fAtomic = atomic;
287 		changeInfo->fBefore = before;
288 		changeInfo->fAfter = after;
289 	}
290 
291 	uint32 flags = fReply->Stream().GetUInt();
292 	*confirm = (flags & OPEN4_RESULT_CONFIRM) == OPEN4_RESULT_CONFIRM;
293 
294 	// attrmask
295 	uint32 bcount = fReply->Stream().GetUInt();
296 	for (uint32 i = 0; i < bcount; i++)
297 		fReply->Stream().GetUInt();
298 
299 	// delegation info
300 	uint32 delegation = fReply->Stream().GetUInt();
301 	OpenDelegationData data;
302 	if (delegData == NULL)
303 		delegData = &data;
304 
305 	if (delegation == OPEN_DELEGATE_NONE) {
306 		delegData->fType = OPEN_DELEGATE_NONE;
307 		return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
308 	}
309 
310 	delegData->fStateSeq = fReply->Stream().GetUInt();
311 	delegData->fStateID[0] = fReply->Stream().GetUInt();
312 	delegData->fStateID[1] = fReply->Stream().GetUInt();
313 	delegData->fStateID[2] = fReply->Stream().GetUInt();
314 
315 	delegData->fRecall = fReply->Stream().GetBoolean();
316 
317 	switch (delegation) {
318 		case OPEN_DELEGATE_READ:
319 			delegData->fType = OPEN_DELEGATE_READ;
320 			break;
321 		case OPEN_DELEGATE_WRITE:
322 			delegData->fType = OPEN_DELEGATE_WRITE;
323 
324 			int32 limitBy = fReply->Stream().GetInt();
325 			if (limitBy == NFS_LIMIT_SIZE)
326 				delegData->fSpaceLimit = fReply->Stream().GetUHyper();
327 			else if (limitBy == NFS_LIMIT_BLOCKS) {
328 				uint32 numBlocks = fReply->Stream().GetUInt();
329 				delegData->fSpaceLimit = fReply->Stream().GetUInt() * numBlocks;
330 			}
331 			break;
332 	}
333 
334 	// ACE data
335 	fReply->Stream().GetUInt();
336 	fReply->Stream().GetUInt();
337 	fReply->Stream().GetUInt();
338 	fReply->Stream().GetOpaque(NULL);
339 
340 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
341 }
342 
343 
344 status_t
345 ReplyInterpreter::OpenConfirm(uint32* stateSeq)
346 {
347 	status_t res = _OperationError(OpOpenConfirm);
348 	if (res != B_OK)
349 		return res;
350 
351 	*stateSeq = fReply->Stream().GetUInt();
352 	fReply->Stream().GetUInt();
353 	fReply->Stream().GetUInt();
354 	fReply->Stream().GetUInt();
355 
356 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
357 }
358 
359 
360 status_t
361 ReplyInterpreter::Read(void* buffer, uint32* size, bool* eof)
362 {
363 	status_t res = _OperationError(OpRead);
364 	if (res != B_OK)
365 		return res;
366 
367 	*eof = fReply->Stream().GetBoolean();
368 	const void* ptr = fReply->Stream().GetOpaque(size);
369 	memcpy(buffer, ptr, *size);
370 
371 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
372 }
373 
374 
375 status_t
376 ReplyInterpreter::ReadDir(uint64* cookie, uint64* cookieVerf,
377 	DirEntry** dirents, uint32* _count,	bool* eof)
378 {
379 	status_t res = _OperationError(OpReadDir);
380 	if (res != B_OK)
381 		return res;
382 
383 	*cookieVerf = fReply->Stream().GetUHyper();
384 
385 	bool isNext;
386 	uint32 count = 0;
387 
388 	// TODO: using  list instead of array would make this much more elegant
389 	// and efficient
390 	XDR::Stream::Position dataStart = fReply->Stream().Current();
391 	isNext = fReply->Stream().GetBoolean();
392 	while (isNext) {
393 		fReply->Stream().GetUHyper();
394 
395 		free(fReply->Stream().GetString());
396 		AttrValue* values;
397 		uint32 attrCount;
398 		_DecodeAttrs(fReply->Stream(), &values,	&attrCount);
399 		delete[] values;
400 
401 		count++;
402 
403 		isNext = fReply->Stream().GetBoolean();
404 	}
405 
406 	DirEntry* entries = new(std::nothrow) DirEntry[count];
407 	if (entries == NULL)
408 		return B_NO_MEMORY;
409 
410 	count = 0;
411 	fReply->Stream().SetPosition(dataStart);
412 	isNext = fReply->Stream().GetBoolean();
413 	while (isNext) {
414 		*cookie = fReply->Stream().GetUHyper();
415 
416 		entries[count].fName = fReply->Stream().GetString();
417 		_DecodeAttrs(fReply->Stream(), &entries[count].fAttrs,
418 			&entries[count].fAttrCount);
419 
420 		count++;
421 
422 		isNext = fReply->Stream().GetBoolean();
423 	}
424 	*eof = fReply->Stream().GetBoolean();
425 
426 	*_count = count;
427 	*dirents = entries;
428 
429 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
430 }
431 
432 
433 status_t
434 ReplyInterpreter::ReadLink(void* buffer, uint32* size, uint32 maxSize)
435 {
436 	status_t res = _OperationError(OpReadLink);
437 	if (res != B_OK)
438 		return res;
439 
440 	const void* ptr = fReply->Stream().GetOpaque(size);
441 	memcpy(buffer, ptr, min_c(*size, maxSize));
442 
443 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
444 }
445 
446 
447 status_t
448 ReplyInterpreter::Remove(uint64* before, uint64* after, bool& atomic)
449 {
450 	status_t res = _OperationError(OpRemove);
451 	if (res != B_OK)
452 		return res;
453 
454 	atomic = fReply->Stream().GetBoolean();
455 	*before = fReply->Stream().GetUHyper();
456 	*after = fReply->Stream().GetUHyper();
457 
458 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
459 }
460 
461 
462 status_t
463 ReplyInterpreter::Rename(uint64* fromBefore, uint64* fromAfter,
464 	bool& fromAtomic, uint64* toBefore, uint64* toAfter, bool& toAtomic)
465 {
466 	status_t res = _OperationError(OpRename);
467 	if (res != B_OK)
468 		return res;
469 
470 	fromAtomic = fReply->Stream().GetBoolean();
471 	*fromBefore = fReply->Stream().GetUHyper();
472 	*fromAfter = fReply->Stream().GetUHyper();
473 
474 	toAtomic = fReply->Stream().GetBoolean();
475 	*toBefore = fReply->Stream().GetUHyper();
476 	*toAfter = fReply->Stream().GetUHyper();
477 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
478 }
479 
480 
481 status_t
482 ReplyInterpreter::SetAttr()
483 {
484 	status_t res = _OperationError(OpSetAttr);
485 	if (res != B_OK)
486 		return res;
487 
488 	uint32 bcount = fReply->Stream().GetUInt();
489 	for (uint32 i = 0; i < bcount; i++)
490 		fReply->Stream().GetUInt();
491 
492 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
493 }
494 
495 
496 status_t
497 ReplyInterpreter::SetClientID(uint64* clientid, uint64* verifier)
498 {
499 	status_t res = _OperationError(OpSetClientID);
500 	if (res != B_OK)
501 		return res;
502 
503 	*clientid = fReply->Stream().GetUHyper();
504 	*verifier = fReply->Stream().GetUHyper();
505 
506 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
507 }
508 
509 
510 status_t
511 ReplyInterpreter::Write(uint32* size)
512 {
513 	status_t res = _OperationError(OpWrite);
514 	if (res != B_OK)
515 		return res;
516 
517 	*size = fReply->Stream().GetUInt();
518 	fReply->Stream().GetInt();
519 	fReply->Stream().GetUHyper();
520 
521 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
522 }
523 
524 
525 static const char*
526 sFlattenPathname(XDR::ReadStream& str)
527 {
528 	uint32 count = str.GetUInt();
529 	char* pathname = NULL;
530 	uint32 size = 0;
531 	for (uint32 i = 0; i < count; i++) {
532 		const char* path = str.GetString();
533 		size += strlen(path) + 1;
534 		if (pathname == NULL) {
535 			pathname = reinterpret_cast<char*>(malloc(strlen(path + 1)));
536 			pathname[0] = '\0';
537 		} else {
538 			*pathname++ = '/';
539 			pathname = reinterpret_cast<char*>(realloc(pathname, size));
540 		}
541 		strcat(pathname, path);
542 		free(const_cast<char*>(path));
543 	}
544 
545 	return pathname;
546 }
547 
548 
549 status_t
550 ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs,
551 	uint32* count)
552 {
553 	uint32 bcount = fReply->Stream().GetUInt();
554 	uint32* bitmap = new(std::nothrow) uint32[bcount];
555 	if (bitmap == NULL)
556 		return B_NO_MEMORY;
557 
558 	uint32 attr_count = 0;
559 	for (uint32 i = 0; i < bcount; i++) {
560 		bitmap[i] = str.GetUInt();
561 		attr_count += CountBits(bitmap[i]);
562 	}
563 
564 	if (attr_count == 0) {
565 		*attrs = NULL;
566 		*count = 0;
567 		return B_OK;
568 	} else if (attr_count > FATTR4_MAXIMUM_ATTR_ID)
569 		return B_BAD_VALUE;
570 
571 	uint32 size;
572 	const void* ptr = str.GetOpaque(&size);
573 	XDR::ReadStream stream(const_cast<void*>(ptr), size);
574 
575 	AttrValue* values = new(std::nothrow) AttrValue[attr_count];
576 	if (values == NULL) {
577 		delete[] bitmap;
578 		return B_NO_MEMORY;
579 	}
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 = sFlattenPathname(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 = sFlattenPathname(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 	delete[] bitmap;
778 
779 	*count = attr_count;
780 	*attrs = values;
781 	return str.IsEOF() ? B_BAD_VALUE : B_OK;
782 }
783 
784 
785 status_t
786 ReplyInterpreter::_OperationError(Opcode op)
787 {
788 	if (fDecodeError)
789 		return B_BAD_VALUE;
790 
791 	if (fReply == NULL)
792 		return B_NOT_INITIALIZED;
793 
794 	if (fReply->Error() != B_OK || fReply->Stream().IsEOF()) {
795 		fDecodeError = true;
796 		return fReply->Error();
797 	}
798 
799 	if (fReply->Stream().GetInt() != op) {
800 		fDecodeError = true;
801 		return B_BAD_VALUE;
802 	}
803 
804 	status_t result = _NFS4ErrorToHaiku(fReply->Stream().GetUInt());
805 	if (result != B_OK)
806 		fDecodeError = true;
807 	return result;
808 }
809 
810 
811 status_t
812 ReplyInterpreter::_NFS4ErrorToHaiku(uint32 x)
813 {
814 	switch (x) {
815 		case NFS4_OK:			return B_OK;
816 		case NFS4ERR_PERM:		return B_PERMISSION_DENIED;
817 		case NFS4ERR_NOENT:		return B_ENTRY_NOT_FOUND;
818 		case NFS4ERR_IO:		return B_IO_ERROR;
819 		case NFS4ERR_NXIO:		return B_DEVICE_NOT_FOUND;
820 		case NFS4ERR_ACCESS:	return B_NOT_ALLOWED;
821 		case NFS4ERR_EXIST:		return B_FILE_EXISTS;
822 		case NFS4ERR_XDEV:		return B_CROSS_DEVICE_LINK;
823 		case NFS4ERR_NOTDIR:	return B_NOT_A_DIRECTORY;
824 		case NFS4ERR_ISDIR:		return B_IS_A_DIRECTORY;
825 		case NFS4ERR_INVAL:		return B_BAD_VALUE;
826 		case NFS4ERR_FBIG:		return B_FILE_TOO_LARGE;
827 		case NFS4ERR_NOTSUPP:	return B_UNSUPPORTED;
828 		case NFS4ERR_ROFS:		return B_READ_ONLY_DEVICE;
829 		case NFS4ERR_NAMETOOLONG:	return B_NAME_TOO_LONG;
830 		case NFS4ERR_NOTEMPTY:	return B_DIRECTORY_NOT_EMPTY;
831 		// ...
832 		case NFS4ERR_DELAY:
833 		case NFS4ERR_DENIED:
834 		case NFS4ERR_LOCKED:
835 		case NFS4ERR_GRACE:
836 								return B_WOULD_BLOCK;
837 
838 		case NFS4ERR_STALE:
839 		case NFS4ERR_FHEXPIRED:
840 								return B_FILE_NOT_FOUND;
841 		// ...
842 		default:				return B_ERROR;
843 	}
844 }
845 
846