xref: /haiku/src/add-ons/kernel/file_systems/nfs4/ReplyInterpreter.cpp (revision b1fd656d454ab89fbfdac09fda1b6aafd81bbe89)
1 /*
2  * Copyright 2012 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Paweł Dziepak, pdziepak@quarnos.org
7  */
8 
9 
10 #include "ReplyInterpreter.h"
11 
12 #include <string.h>
13 
14 #include <util/kernel_cpp.h>
15 
16 #include "Cookie.h"
17 
18 
19 FSLocation::~FSLocation()
20 {
21 	free(const_cast<char*>(fRootPath));
22 	for (uint32 i = 0; i < fCount; i++)
23 		free(const_cast<char*>(fLocations[i]));
24 	delete[] fLocations;
25 }
26 
27 
28 FSLocations::~FSLocations()
29 {
30 	free(const_cast<char*>(fRootPath));
31 	delete[] fLocations;
32 }
33 
34 
35 AttrValue::AttrValue()
36 	:
37 	fFreePointer(false)
38 {
39 }
40 
41 
42 AttrValue::~AttrValue()
43 {
44 	if (fFreePointer)
45 		free(fData.fPointer);
46 	if (fAttribute == FATTR4_FS_LOCATIONS)
47 		delete fData.fLocations;
48 }
49 
50 
51 DirEntry::DirEntry()
52 	:
53 	fName(NULL),
54 	fAttrs(NULL),
55 	fAttrCount(0)
56 {
57 }
58 
59 
60 DirEntry::~DirEntry()
61 {
62 	free(const_cast<char*>(fName));
63 	delete[] fAttrs;
64 }
65 
66 
67 ReplyInterpreter::ReplyInterpreter(RPC::Reply* reply)
68 	:
69 	fNFS4Error(NFS4_OK),
70 	fDecodeError(false),
71 	fReply(reply)
72 {
73 	if (reply != NULL)
74 		_ParseHeader();
75 }
76 
77 
78 ReplyInterpreter::~ReplyInterpreter()
79 {
80 	delete fReply;
81 }
82 
83 
84 void
85 ReplyInterpreter::_ParseHeader()
86 {
87 	fNFS4Error = fReply->Stream().GetUInt();
88 	fReply->Stream().GetOpaque(NULL);
89 	fReply->Stream().GetUInt();
90 }
91 
92 
93 status_t
94 ReplyInterpreter::Access(uint32* supported, uint32* allowed)
95 {
96 	status_t res = _OperationError(OpAccess);
97 	if (res != B_OK)
98 		return res;
99 
100 	uint32 support = fReply->Stream().GetUInt();
101 	uint32 allow = fReply->Stream().GetUInt();
102 
103 	if (supported != NULL)
104 		*supported = support;
105 	if (allowed != NULL)
106 		*allowed = allow;
107 
108 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
109 }
110 
111 
112 status_t
113 ReplyInterpreter::Close()
114 {
115 	status_t res = _OperationError(OpClose);
116 	if (res != B_OK)
117 		return res;
118 
119 	fReply->Stream().GetUInt();
120 	fReply->Stream().GetUInt();
121 	fReply->Stream().GetUInt();
122 	fReply->Stream().GetUInt();
123 
124 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
125 }
126 
127 
128 status_t
129 ReplyInterpreter::Commit()
130 {
131 	status_t res = _OperationError(OpCommit);
132 	if (res != B_OK)
133 		return res;
134 
135 	fReply->Stream().GetOpaque(NULL);
136 
137 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
138 }
139 
140 
141 status_t
142 ReplyInterpreter::Create(uint64* before, uint64* after, bool& atomic)
143 {
144 	status_t res = _OperationError(OpCreate);
145 	if (res != B_OK)
146 		return res;
147 
148 	atomic = fReply->Stream().GetBoolean();
149 	*before = fReply->Stream().GetUHyper();
150 	*after = fReply->Stream().GetUHyper();
151 
152 	uint32 count = fReply->Stream().GetUInt();
153 	for (uint32 i = 0; i < count; i++)
154 		fReply->Stream().GetUInt();
155 
156 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
157 }
158 
159 
160 
161 // Bit Twiddling Hacks
162 // http://graphics.stanford.edu/~seander/bithacks.html
163 static inline uint32 sCountBits(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(OpLockU);
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 	if (!isNext)
425 		*eof = fReply->Stream().GetBoolean();
426 	else
427 		*eof = false;
428 
429 	*_count = count;
430 	*dirents = entries;
431 
432 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
433 }
434 
435 
436 status_t
437 ReplyInterpreter::ReadLink(void* buffer, uint32* size, uint32 maxSize)
438 {
439 	status_t res = _OperationError(OpReadLink);
440 	if (res != B_OK)
441 		return res;
442 
443 	const void* ptr = fReply->Stream().GetOpaque(size);
444 	memcpy(buffer, ptr, min_c(*size, maxSize));
445 
446 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
447 }
448 
449 
450 status_t
451 ReplyInterpreter::Remove(uint64* before, uint64* after, bool& atomic)
452 {
453 	status_t res = _OperationError(OpRemove);
454 	if (res != B_OK)
455 		return res;
456 
457 	atomic = fReply->Stream().GetBoolean();
458 	*before = fReply->Stream().GetUHyper();
459 	*after = fReply->Stream().GetUHyper();
460 
461 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
462 }
463 
464 
465 status_t
466 ReplyInterpreter::Rename(uint64* fromBefore, uint64* fromAfter,
467 	bool& fromAtomic, uint64* toBefore, uint64* toAfter, bool& toAtomic)
468 {
469 	status_t res = _OperationError(OpRename);
470 	if (res != B_OK)
471 		return res;
472 
473 	fromAtomic = fReply->Stream().GetBoolean();
474 	*fromBefore = fReply->Stream().GetUHyper();
475 	*fromAfter = fReply->Stream().GetUHyper();
476 
477 	toAtomic = fReply->Stream().GetBoolean();
478 	*toBefore = fReply->Stream().GetUHyper();
479 	*toAfter = fReply->Stream().GetUHyper();
480 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
481 }
482 
483 
484 status_t
485 ReplyInterpreter::SetAttr()
486 {
487 	status_t res = _OperationError(OpSetAttr);
488 	if (res != B_OK)
489 		return res;
490 
491 	uint32 bcount = fReply->Stream().GetUInt();
492 	for (uint32 i = 0; i < bcount; i++)
493 		fReply->Stream().GetUInt();
494 
495 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
496 }
497 
498 
499 status_t
500 ReplyInterpreter::SetClientID(uint64* clientid, uint64* verifier)
501 {
502 	status_t res = _OperationError(OpSetClientID);
503 	if (res != B_OK)
504 		return res;
505 
506 	*clientid = fReply->Stream().GetUHyper();
507 	*verifier = fReply->Stream().GetUHyper();
508 
509 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
510 }
511 
512 
513 status_t
514 ReplyInterpreter::Write(uint32* size)
515 {
516 	status_t res = _OperationError(OpWrite);
517 	if (res != B_OK)
518 		return res;
519 
520 	*size = fReply->Stream().GetUInt();
521 	fReply->Stream().GetInt();
522 	fReply->Stream().GetUHyper();
523 
524 	return fReply->Stream().IsEOF() ? B_BAD_VALUE : B_OK;
525 }
526 
527 
528 static const char*
529 sFlattenPathname(XDR::ReadStream& str)
530 {
531 	uint32 count = str.GetUInt();
532 	char* pathname = NULL;
533 	uint32 size = 0;
534 	for (uint32 i = 0; i < count; i++) {
535 		const char* path = str.GetString();
536 		size += strlen(path) + 1;
537 		if (pathname == NULL) {
538 			pathname = reinterpret_cast<char*>(malloc(strlen(path + 1)));
539 			pathname[0] = '\0';
540 		} else {
541 			*pathname++ = '/';
542 			pathname = reinterpret_cast<char*>(realloc(pathname, size));
543 		}
544 		strcat(pathname, path);
545 		free(const_cast<char*>(path));
546 	}
547 
548 	return pathname;
549 }
550 
551 
552 status_t
553 ReplyInterpreter::_DecodeAttrs(XDR::ReadStream& str, AttrValue** attrs,
554 	uint32* count)
555 {
556 	uint32 bcount = fReply->Stream().GetUInt();
557 	uint32 *bitmap = new(std::nothrow) uint32[bcount];
558 	if (bitmap == NULL)
559 		return B_NO_MEMORY;
560 
561 	uint32 attr_count = 0;
562 	for (uint32 i = 0; i < bcount; i++) {
563 		bitmap[i] = str.GetUInt();
564 		attr_count += sCountBits(bitmap[i]);
565 	}
566 
567 	if (attr_count == 0) {
568 		*attrs = NULL;
569 		*count = 0;
570 		return B_OK;
571 	} else if (attr_count > FATTR4_MAXIMUM_ATTR_ID)
572 		return B_BAD_VALUE;
573 
574 	uint32 size;
575 	const void* ptr = str.GetOpaque(&size);
576 	XDR::ReadStream stream(const_cast<void*>(ptr), size);
577 
578 	AttrValue* values = new(std::nothrow) AttrValue[attr_count];
579 	if (values == NULL) {
580 		delete[] bitmap;
581 		return B_NO_MEMORY;
582 	}
583 
584 	uint32 current = 0;
585 
586 	if (sIsAttrSet(FATTR4_SUPPORTED_ATTRS, bitmap, bcount)) {
587 		values[current].fAttribute = FATTR4_SUPPORTED_ATTRS;
588 		uint32 count = stream.GetInt();
589 		uint32 i;
590 		// two uint32 are enough for NFS4, not for NFS4.1
591 		for (i = 0; i < min_c(count, 2); i++)
592 			((uint32*)&values[current].fData.fValue64)[i] = stream.GetUInt();
593 		for (; i < count; i++)
594 			stream.GetUInt();
595 		current++;
596 	}
597 
598 	if (sIsAttrSet(FATTR4_TYPE, bitmap, bcount)) {
599 		values[current].fAttribute = FATTR4_TYPE;
600 		values[current].fData.fValue32 = stream.GetInt();
601 		current++;
602 	}
603 
604 	if (sIsAttrSet(FATTR4_FH_EXPIRE_TYPE, bitmap, bcount)) {
605 		values[current].fAttribute = FATTR4_FH_EXPIRE_TYPE;
606 		values[current].fData.fValue32 = stream.GetUInt();
607 		current++;
608 	}
609 
610 	if (sIsAttrSet(FATTR4_CHANGE, bitmap, bcount)) {
611 		values[current].fAttribute = FATTR4_CHANGE;
612 		values[current].fData.fValue64 = stream.GetUHyper();
613 		current++;
614 	}
615 
616 	if (sIsAttrSet(FATTR4_SIZE, bitmap, bcount)) {
617 		values[current].fAttribute = FATTR4_SIZE;
618 		values[current].fData.fValue64 = stream.GetUHyper();
619 		current++;
620 	}
621 
622 	if (sIsAttrSet(FATTR4_FSID, bitmap, bcount)) {
623 		values[current].fAttribute = FATTR4_FSID;
624 		values[current].fFreePointer = true;
625 
626 		FileSystemId fsid;
627 		fsid.fMajor = stream.GetUHyper();
628 		fsid.fMinor = stream.GetUHyper();
629 
630 		values[current].fData.fPointer = malloc(sizeof(fsid));
631 		memcpy(values[current].fData.fPointer, &fsid, sizeof(fsid));
632 		current++;
633 	}
634 
635 	if (sIsAttrSet(FATTR4_LEASE_TIME, bitmap, bcount)) {
636 		values[current].fAttribute = FATTR4_LEASE_TIME;
637 		values[current].fData.fValue32 = stream.GetUInt();
638 		current++;
639 	}
640 
641 	if (sIsAttrSet(FATTR4_FILEID, bitmap, bcount)) {
642 		values[current].fAttribute = FATTR4_FILEID;
643 		values[current].fData.fValue64 = stream.GetUHyper();
644 		current++;
645 	}
646 
647 	if (sIsAttrSet(FATTR4_FILES_FREE, bitmap, bcount)) {
648 		values[current].fAttribute = FATTR4_FILES_FREE;
649 		values[current].fData.fValue64 = stream.GetUHyper();
650 		current++;
651 	}
652 
653 	if (sIsAttrSet(FATTR4_FILES_TOTAL, bitmap, bcount)) {
654 		values[current].fAttribute = FATTR4_FILES_TOTAL;
655 		values[current].fData.fValue64 = stream.GetUHyper();
656 		current++;
657 	}
658 
659 	if (sIsAttrSet(FATTR4_FS_LOCATIONS, bitmap, bcount)) {
660 		values[current].fAttribute = FATTR4_FS_LOCATIONS;
661 
662 		FSLocations* locs = new FSLocations;
663 		locs->fRootPath = sFlattenPathname(stream);
664 		locs->fCount = stream.GetUInt();
665 		locs->fLocations = new FSLocation[locs->fCount];
666 		for (uint32 i = 0; i < locs->fCount; i++) {
667 			locs->fLocations[i].fRootPath = sFlattenPathname(stream);
668 			locs->fLocations[i].fCount = stream.GetUInt();
669 			locs->fLocations[i].fLocations =
670 				new const char*[locs->fLocations[i].fCount];
671 			for (uint32 j = 0; j < locs->fLocations[i].fCount; j++)
672 				locs->fLocations[i].fLocations[j] = stream.GetString();
673 		}
674 		values[current].fData.fLocations = locs;
675 		current++;
676 	}
677 
678 	if (sIsAttrSet(FATTR4_MAXREAD, bitmap, bcount)) {
679 		values[current].fAttribute = FATTR4_MAXREAD;
680 		values[current].fData.fValue64 = stream.GetUHyper();
681 		current++;
682 	}
683 
684 	if (sIsAttrSet(FATTR4_MAXWRITE, bitmap, bcount)) {
685 		values[current].fAttribute = FATTR4_MAXWRITE;
686 		values[current].fData.fValue64 = stream.GetUHyper();
687 		current++;
688 	}
689 
690 	if (sIsAttrSet(FATTR4_MODE, bitmap, bcount)) {
691 		values[current].fAttribute = FATTR4_MODE;
692 		values[current].fData.fValue32 = stream.GetUInt();
693 		current++;
694 	}
695 
696 	if (sIsAttrSet(FATTR4_NUMLINKS, bitmap, bcount)) {
697 		values[current].fAttribute = FATTR4_NUMLINKS;
698 		values[current].fData.fValue32 = stream.GetUInt();
699 		current++;
700 	}
701 
702 	if (sIsAttrSet(FATTR4_OWNER, bitmap, bcount)) {
703 		values[current].fAttribute = FATTR4_OWNER;
704 		values[current].fFreePointer = true;
705 		values[current].fData.fPointer = stream.GetString();
706 		current++;
707 	}
708 
709 	if (sIsAttrSet(FATTR4_OWNER_GROUP, bitmap, bcount)) {
710 		values[current].fAttribute = FATTR4_OWNER_GROUP;
711 		values[current].fFreePointer = true;
712 		values[current].fData.fPointer = stream.GetString();
713 		current++;
714 	}
715 
716 	if (sIsAttrSet(FATTR4_SPACE_FREE, bitmap, bcount)) {
717 		values[current].fAttribute = FATTR4_SPACE_FREE;
718 		values[current].fData.fValue64 = stream.GetUHyper();
719 		current++;
720 	}
721 
722 	if (sIsAttrSet(FATTR4_SPACE_TOTAL, bitmap, bcount)) {
723 		values[current].fAttribute = FATTR4_SPACE_TOTAL;
724 		values[current].fData.fValue64 = stream.GetUHyper();
725 		current++;
726 	}
727 
728 	if (sIsAttrSet(FATTR4_TIME_ACCESS, bitmap, bcount)) {
729 		values[current].fAttribute = FATTR4_TIME_ACCESS;
730 		values[current].fFreePointer = true;
731 
732 		struct timespec ts;
733 		ts.tv_sec = static_cast<time_t>(stream.GetHyper());
734 		ts.tv_nsec = static_cast<long>(stream.GetUInt());
735 
736 		values[current].fData.fPointer = malloc(sizeof(ts));
737 		memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
738 		current++;
739 	}
740 
741 	if (sIsAttrSet(FATTR4_TIME_CREATE, bitmap, bcount)) {
742 		values[current].fAttribute = FATTR4_TIME_CREATE;
743 		values[current].fFreePointer = true;
744 
745 		struct timespec ts;
746 		ts.tv_sec = static_cast<time_t>(stream.GetHyper());
747 		ts.tv_nsec = static_cast<long>(stream.GetUInt());
748 
749 		values[current].fData.fPointer = malloc(sizeof(ts));
750 		memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
751 		current++;
752 	}
753 
754 	if (sIsAttrSet(FATTR4_TIME_METADATA, bitmap, bcount)) {
755 		values[current].fAttribute = FATTR4_TIME_METADATA;
756 		values[current].fFreePointer = true;
757 
758 		struct timespec ts;
759 		ts.tv_sec = static_cast<time_t>(stream.GetHyper());
760 		ts.tv_nsec = static_cast<long>(stream.GetUInt());
761 
762 		values[current].fData.fPointer = malloc(sizeof(ts));
763 		memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
764 		current++;
765 	}
766 
767 	if (sIsAttrSet(FATTR4_TIME_MODIFY, bitmap, bcount)) {
768 		values[current].fAttribute = FATTR4_TIME_MODIFY;
769 		values[current].fFreePointer = true;
770 
771 		struct timespec ts;
772 		ts.tv_sec = static_cast<time_t>(stream.GetHyper());
773 		ts.tv_nsec = static_cast<long>(stream.GetUInt());
774 
775 		values[current].fData.fPointer = malloc(sizeof(ts));
776 		memcpy(values[current].fData.fPointer, &ts, sizeof(ts));
777 		current++;
778 	}
779 
780 	delete[] bitmap;
781 
782 	*count = attr_count;
783 	*attrs = values;
784 	return str.IsEOF() ? B_BAD_VALUE : B_OK;
785 }
786 
787 
788 status_t
789 ReplyInterpreter::_OperationError(Opcode op)
790 {
791 	if (fDecodeError)
792 		return B_BAD_VALUE;
793 
794 	if (fReply == NULL)
795 		return B_NOT_INITIALIZED;
796 
797 	if (fReply->Error() != B_OK || fReply->Stream().IsEOF()) {
798 		fDecodeError = true;
799 		return fReply->Error();
800 	}
801 
802 	if (fReply->Stream().GetInt() != op) {
803 		fDecodeError = true;
804 		return B_BAD_VALUE;
805 	}
806 
807 	status_t result = _NFS4ErrorToHaiku(fReply->Stream().GetUInt());
808 	if (result != B_OK)
809 		fDecodeError = true;
810 	return result;
811 }
812 
813 
814 status_t
815 ReplyInterpreter::_NFS4ErrorToHaiku(uint32 x)
816 {
817 	switch (x) {
818 		case NFS4_OK:			return B_OK;
819 		case NFS4ERR_PERM:		return B_PERMISSION_DENIED;
820 		case NFS4ERR_NOENT:		return B_ENTRY_NOT_FOUND;
821 		case NFS4ERR_IO:		return B_IO_ERROR;
822 		case NFS4ERR_NXIO:		return B_DEVICE_NOT_FOUND;
823 		case NFS4ERR_ACCESS:	return B_NOT_ALLOWED;
824 		case NFS4ERR_EXIST:		return B_FILE_EXISTS;
825 		case NFS4ERR_XDEV:		return B_CROSS_DEVICE_LINK;
826 		case NFS4ERR_NOTDIR:	return B_NOT_A_DIRECTORY;
827 		case NFS4ERR_ISDIR:		return B_IS_A_DIRECTORY;
828 		case NFS4ERR_INVAL:		return B_BAD_VALUE;
829 		case NFS4ERR_FBIG:		return B_FILE_TOO_LARGE;
830 		case NFS4ERR_NOTSUPP:	return B_UNSUPPORTED;
831 		// ...
832 		case NFS4ERR_DELAY:
833 		case NFS4ERR_DENIED:
834 		case NFS4ERR_LOCKED:
835 		case NFS4ERR_GRACE:
836 								return B_WOULD_BLOCK;
837 		case NFS4ERR_FHEXPIRED:	return B_ENTRY_NOT_FOUND;
838 		// ...
839 		default:				return B_ERROR;
840 	}
841 }
842 
843