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 "RequestBuilder.h"
11
12#include <errno.h>
13#include <string.h>
14
15#include <util/Random.h>
16
17#include "Cookie.h"
18#include "OpenState.h"
19#include "RPCCallback.h"
20#include "RPCCallbackServer.h"
21
22
23RequestBuilder::RequestBuilder(Procedure proc)
24	:
25	fOpCount(0),
26	fProcedure(proc),
27	fRequest(NULL)
28{
29	_InitHeader();
30}
31
32
33RequestBuilder::~RequestBuilder()
34{
35	delete fRequest;
36}
37
38
39void
40RequestBuilder::_InitHeader()
41{
42	fRequest = RPC::Call::Create(fProcedure, RPC::Auth::CreateSys(),
43		RPC::Auth::CreateNone());
44
45	if (fRequest == NULL)
46		return;
47
48	if (fProcedure == ProcCompound) {
49		fRequest->Stream().AddOpaque(NULL, 0);
50		fRequest->Stream().AddUInt(0);
51
52		fOpCountPosition = fRequest->Stream().Current();
53		fRequest->Stream().AddUInt(0);
54	}
55}
56
57
58status_t
59RequestBuilder::Access()
60{
61	if (fProcedure != ProcCompound)
62		return B_BAD_VALUE;
63	if (fRequest == NULL)
64		return B_NO_MEMORY;
65
66	fRequest->Stream().AddUInt(OpAccess);
67	fRequest->Stream().AddUInt(ACCESS4_READ | ACCESS4_LOOKUP | ACCESS4_MODIFY
68		| ACCESS4_EXTEND | ACCESS4_DELETE | ACCESS4_EXECUTE);
69	fOpCount++;
70
71	return B_OK;
72}
73
74
75status_t
76RequestBuilder::Close(uint32 seq, const uint32* id, uint32 stateSeq)
77{
78	if (fProcedure != ProcCompound)
79		return B_BAD_VALUE;
80	if (fRequest == NULL)
81		return B_NO_MEMORY;
82
83	fRequest->Stream().AddUInt(OpClose);
84	fRequest->Stream().AddUInt(seq);
85	fRequest->Stream().AddUInt(stateSeq);
86	fRequest->Stream().AddUInt(id[0]);
87	fRequest->Stream().AddUInt(id[1]);
88	fRequest->Stream().AddUInt(id[2]);
89
90	fOpCount++;
91
92	return B_OK;
93}
94
95
96status_t
97RequestBuilder::Commit(uint64 offset, uint32 count)
98{
99	if (fProcedure != ProcCompound)
100		return B_BAD_VALUE;
101	if (fRequest == NULL)
102		return B_NO_MEMORY;
103
104	fRequest->Stream().AddUInt(OpCommit);
105	fRequest->Stream().AddUHyper(offset);
106	fRequest->Stream().AddUInt(count);
107
108	fOpCount++;
109
110	return B_OK;
111}
112
113
114status_t
115RequestBuilder::Create(FileType type, const char* name, AttrValue* attr,
116	uint32 count, const char* path)
117{
118	if (fProcedure != ProcCompound)
119		return B_BAD_VALUE;
120	if (fRequest == NULL)
121		return B_NO_MEMORY;
122	if (type == NF4LNK && path == NULL)
123		return B_BAD_VALUE;
124	if (name == NULL)
125		return B_BAD_VALUE;
126	if (type == NF4BLK || type == NF4CHR)
127		return B_BAD_VALUE;
128
129	fRequest->Stream().AddUInt(OpCreate);
130	fRequest->Stream().AddUInt(type);
131	if (type == NF4LNK)
132		fRequest->Stream().AddString(path);
133	fRequest->Stream().AddString(name);
134	_EncodeAttrs(fRequest->Stream(), attr, count);
135
136	fOpCount++;
137
138	return B_OK;
139}
140
141
142status_t
143RequestBuilder::DelegReturn(const uint32* id, uint32 seq)
144{
145	if (fProcedure != ProcCompound)
146		return B_BAD_VALUE;
147	if (fRequest == NULL)
148		return B_NO_MEMORY;
149
150	fRequest->Stream().AddUInt(OpDelegReturn);
151
152	fRequest->Stream().AddUInt(seq);
153	fRequest->Stream().AddUInt(id[0]);
154	fRequest->Stream().AddUInt(id[1]);
155	fRequest->Stream().AddUInt(id[2]);
156
157	fOpCount++;
158
159	return B_OK;
160}
161
162
163status_t
164RequestBuilder::GetAttr(Attribute* attrs, uint32 count)
165{
166	if (fProcedure != ProcCompound)
167		return B_BAD_VALUE;
168	if (fRequest == NULL)
169		return B_NO_MEMORY;
170
171	fRequest->Stream().AddUInt(OpGetAttr);
172	_AttrBitmap(fRequest->Stream(), attrs, count);
173
174	fOpCount++;
175
176	return B_OK;
177}
178
179
180status_t
181RequestBuilder::GetFH()
182{
183	if (fProcedure != ProcCompound)
184		return B_BAD_VALUE;
185	if (fRequest == NULL)
186		return B_NO_MEMORY;
187
188	fRequest->Stream().AddUInt(OpGetFH);
189	fOpCount++;
190
191	return B_OK;
192}
193
194
195void
196RequestBuilder::_GenerateLockOwner(XDR::WriteStream& stream,
197	OpenState* state, LockOwner* owner)
198{
199	stream.AddUHyper(state->fClientID);
200
201	uint64 lockOwner[2];
202	lockOwner[0] = owner->fOwner;
203	lockOwner[1] = state->fInfo.fFileId;
204	stream.AddOpaque(lockOwner, sizeof(lockOwner));
205}
206
207
208status_t
209RequestBuilder::Lock(OpenState* state, LockInfo* lock, uint32* sequence,
210	bool reclaim)
211{
212	if (fProcedure != ProcCompound)
213		return B_BAD_VALUE;
214	if (fRequest == NULL)
215		return B_NO_MEMORY;
216
217	fRequest->Stream().AddUInt(OpLock);
218
219	fRequest->Stream().AddInt(lock->fType);
220	fRequest->Stream().AddBoolean(reclaim);
221
222	fRequest->Stream().AddUHyper(lock->fStart);
223	fRequest->Stream().AddUHyper(lock->fLength);
224
225	if (lock->fOwner->fStateId[0] == 0 && lock->fOwner->fStateId[1] == 0
226		&& lock->fOwner->fStateId[2] == 0) {
227
228		fRequest->Stream().AddBoolean(true);			// new lock owner
229
230		// open seq stateid
231		fRequest->Stream().AddUInt(*sequence);
232		fRequest->Stream().AddUInt(state->fStateSeq);
233		fRequest->Stream().AddUInt(state->fStateID[0]);
234		fRequest->Stream().AddUInt(state->fStateID[1]);
235		fRequest->Stream().AddUInt(state->fStateID[2]);
236
237		// lock seq owner
238		fRequest->Stream().AddUInt(lock->fOwner->fSequence++);
239		_GenerateLockOwner(fRequest->Stream(), state, lock->fOwner);
240
241	} else {
242		fRequest->Stream().AddBoolean(false);			// old lock owner
243		(*sequence)--;
244
245		// lock stateid seq
246		fRequest->Stream().AddUInt(lock->fOwner->fStateSeq);
247		fRequest->Stream().AddUInt(lock->fOwner->fStateId[0]);
248		fRequest->Stream().AddUInt(lock->fOwner->fStateId[1]);
249		fRequest->Stream().AddUInt(lock->fOwner->fStateId[2]);
250
251		fRequest->Stream().AddUInt(lock->fOwner->fSequence++);
252	}
253
254	fOpCount++;
255
256	return B_OK;
257}
258
259
260status_t
261RequestBuilder::LockT(LockType type, uint64 pos, uint64 len,
262	OpenState* state)
263{
264	if (fProcedure != ProcCompound)
265		return B_BAD_VALUE;
266	if (fRequest == NULL)
267		return B_NO_MEMORY;
268
269	fRequest->Stream().AddUInt(OpLockT);
270
271	fRequest->Stream().AddInt(type);
272
273	fRequest->Stream().AddUHyper(pos);
274	fRequest->Stream().AddUHyper(len);
275
276	fRequest->Stream().AddUHyper(state->fClientID);
277
278	uint32 owner = find_thread(NULL);
279	fRequest->Stream().AddOpaque(&owner, sizeof(owner));
280
281	fOpCount++;
282
283	return B_OK;
284}
285
286
287status_t
288RequestBuilder::LockU(LockInfo* lock)
289{
290	if (fProcedure != ProcCompound)
291		return B_BAD_VALUE;
292	if (fRequest == NULL)
293		return B_NO_MEMORY;
294
295	fRequest->Stream().AddUInt(OpLockU);
296
297	fRequest->Stream().AddInt(lock->fType);
298
299	fRequest->Stream().AddUInt(lock->fOwner->fSequence++);
300	fRequest->Stream().AddUInt(lock->fOwner->fStateSeq);
301	fRequest->Stream().AddUInt(lock->fOwner->fStateId[0]);
302	fRequest->Stream().AddUInt(lock->fOwner->fStateId[1]);
303	fRequest->Stream().AddUInt(lock->fOwner->fStateId[2]);
304
305	fRequest->Stream().AddUHyper(lock->fStart);
306	fRequest->Stream().AddUHyper(lock->fLength);
307
308	fOpCount++;
309
310	return B_OK;
311}
312
313
314status_t
315RequestBuilder::Link(const char* name)
316{
317	if (fProcedure != ProcCompound)
318		return B_BAD_VALUE;
319	if (fRequest == NULL)
320		return B_NO_MEMORY;
321	if (name == NULL)
322		return B_BAD_VALUE;
323
324	fRequest->Stream().AddUInt(OpLink);
325	fRequest->Stream().AddString(name);
326	fOpCount++;
327
328	return B_OK;
329}
330
331
332status_t
333RequestBuilder::LookUp(const char* name)
334{
335	if (fProcedure != ProcCompound)
336		return B_BAD_VALUE;
337	if (fRequest == NULL)
338		return B_NO_MEMORY;
339	if (name == NULL)
340		return B_BAD_VALUE;
341
342	fRequest->Stream().AddUInt(OpLookUp);
343	fRequest->Stream().AddString(name, strlen(name));
344	fOpCount++;
345
346	return B_OK;
347}
348
349
350status_t
351RequestBuilder::LookUpUp()
352{
353	if (fProcedure != ProcCompound)
354		return B_BAD_VALUE;
355	if (fRequest == NULL)
356		return B_NO_MEMORY;
357
358	fRequest->Stream().AddUInt(OpLookUpUp);
359	fOpCount++;
360
361	return B_OK;
362}
363
364
365status_t
366RequestBuilder::Nverify(AttrValue* attr, uint32 count)
367{
368	if (fProcedure != ProcCompound)
369		return B_BAD_VALUE;
370	if (fRequest == NULL)
371		return B_NO_MEMORY;
372
373	fRequest->Stream().AddUInt(OpNverify);
374	_EncodeAttrs(fRequest->Stream(), attr, count);
375
376	fOpCount++;
377
378	return B_OK;
379}
380
381
382status_t
383RequestBuilder::Open(OpenClaim claim, uint32 seq, uint32 access, uint64 id,
384	OpenCreate oc, uint64 ownerId, const char* name, AttrValue* attr,
385	uint32 count, bool excl, OpenDelegation delegationType)
386{
387	if (fProcedure != ProcCompound)
388		return B_BAD_VALUE;
389	if (fRequest == NULL)
390		return B_NO_MEMORY;
391
392	fRequest->Stream().AddUInt(OpOpen);
393	fRequest->Stream().AddUInt(seq);
394	fRequest->Stream().AddUInt(access);
395	fRequest->Stream().AddUInt(0);			// deny none
396	fRequest->Stream().AddUHyper(id);
397
398	char owner[128];
399	int pos = 0;
400	*(uint64*)(owner + pos) = ownerId;
401	pos += sizeof(uint64);
402
403	fRequest->Stream().AddOpaque(owner, pos);
404
405	fRequest->Stream().AddUInt(oc);
406	if (oc == OPEN4_CREATE) {
407		fRequest->Stream().AddInt(excl ? GUARDED4 : UNCHECKED4);
408		_EncodeAttrs(fRequest->Stream(), attr, count);
409	}
410
411	fRequest->Stream().AddUInt(claim);
412	switch (claim) {
413		case CLAIM_NULL:
414			fRequest->Stream().AddString(name, strlen(name));
415			break;
416		case CLAIM_PREVIOUS:
417			fRequest->Stream().AddUInt(delegationType);
418			break;
419		default:
420			return B_UNSUPPORTED;
421	}
422
423	fOpCount++;
424
425	return B_OK;
426}
427
428
429status_t
430RequestBuilder::OpenConfirm(uint32 seq, const uint32* id, uint32 stateSeq)
431{
432	if (fProcedure != ProcCompound)
433		return B_BAD_VALUE;
434	if (fRequest == NULL)
435		return B_NO_MEMORY;
436
437	fRequest->Stream().AddUInt(OpOpenConfirm);
438	fRequest->Stream().AddUInt(stateSeq);
439	fRequest->Stream().AddUInt(id[0]);
440	fRequest->Stream().AddUInt(id[1]);
441	fRequest->Stream().AddUInt(id[2]);
442	fRequest->Stream().AddUInt(seq);
443
444	fOpCount++;
445
446	return B_OK;
447}
448
449
450status_t
451RequestBuilder::OpenAttrDir(bool create)
452{
453	if (fProcedure != ProcCompound)
454		return B_BAD_VALUE;
455	if (fRequest == NULL)
456		return B_NO_MEMORY;
457
458	fRequest->Stream().AddUInt(OpOpenAttrDir);
459	fRequest->Stream().AddBoolean(create);
460
461	fOpCount++;
462
463	return B_OK;
464}
465
466
467status_t
468RequestBuilder::PutFH(const FileHandle& fh)
469{
470	if (fProcedure != ProcCompound)
471		return B_BAD_VALUE;
472	if (fRequest == NULL)
473		return B_NO_MEMORY;
474
475	fRequest->Stream().AddUInt(OpPutFH);
476	fRequest->Stream().AddOpaque(fh.fData, fh.fSize);
477	fOpCount++;
478
479	return B_OK;
480}
481
482
483status_t
484RequestBuilder::PutRootFH()
485{
486	if (fProcedure != ProcCompound)
487		return B_BAD_VALUE;
488	if (fRequest == NULL)
489		return B_NO_MEMORY;
490
491	fRequest->Stream().AddUInt(OpPutRootFH);
492	fOpCount++;
493
494	return B_OK;
495}
496
497
498status_t
499RequestBuilder::Read(const uint32* id, uint32 stateSeq, uint64 pos, uint32 len)
500{
501	if (fProcedure != ProcCompound)
502		return B_BAD_VALUE;
503	if (fRequest == NULL)
504		return B_NO_MEMORY;
505
506	fRequest->Stream().AddUInt(OpRead);
507	fRequest->Stream().AddUInt(stateSeq);
508	fRequest->Stream().AddUInt(id[0]);
509	fRequest->Stream().AddUInt(id[1]);
510	fRequest->Stream().AddUInt(id[2]);
511	fRequest->Stream().AddUHyper(pos);
512	fRequest->Stream().AddUInt(len);
513
514	fOpCount++;
515
516	return B_OK;
517}
518
519
520status_t
521RequestBuilder::ReadDir(uint64 cookie, uint64 cookieVerf, Attribute* attrs,
522	uint32 attrCount)
523{
524	if (fProcedure != ProcCompound)
525		return B_BAD_VALUE;
526	if (fRequest == NULL)
527		return B_NO_MEMORY;
528
529	fRequest->Stream().AddUInt(OpReadDir);
530	fRequest->Stream().AddUHyper(cookie);
531	fRequest->Stream().AddUHyper(cookieVerf);
532
533	// consider predicting this values basing on count or buffer size
534	fRequest->Stream().AddUInt(0x2000);
535	fRequest->Stream().AddUInt(0x8000);
536	_AttrBitmap(fRequest->Stream(), attrs, attrCount);
537
538	fOpCount++;
539
540	return B_OK;
541}
542
543
544status_t
545RequestBuilder::ReadLink()
546{
547	if (fProcedure != ProcCompound)
548		return B_BAD_VALUE;
549	if (fRequest == NULL)
550		return B_NO_MEMORY;
551
552	fRequest->Stream().AddUInt(OpReadLink);
553
554	fOpCount++;
555
556	return B_OK;
557}
558
559
560status_t
561RequestBuilder::Remove(const char* file)
562{
563	if (fProcedure != ProcCompound)
564		return B_BAD_VALUE;
565	if (fRequest == NULL)
566		return B_NO_MEMORY;
567
568	fRequest->Stream().AddUInt(OpRemove);
569	fRequest->Stream().AddString(file);
570
571	fOpCount++;
572
573	return B_OK;
574}
575
576
577status_t
578RequestBuilder::Rename(const char* from, const char* to)
579{
580	if (fProcedure != ProcCompound)
581		return B_BAD_VALUE;
582	if (fRequest == NULL)
583		return B_NO_MEMORY;
584
585	fRequest->Stream().AddUInt(OpRename);
586	fRequest->Stream().AddString(from);
587	fRequest->Stream().AddString(to);
588
589	fOpCount++;
590
591	return B_OK;
592}
593
594
595status_t
596RequestBuilder::Renew(uint64 clientId)
597{
598	if (fProcedure != ProcCompound)
599		return B_BAD_VALUE;
600	if (fRequest == NULL)
601		return B_NO_MEMORY;
602
603	fRequest->Stream().AddUInt(OpRenew);
604	fRequest->Stream().AddUHyper(clientId);
605
606	fOpCount++;
607
608	return B_OK;
609}
610
611
612status_t
613RequestBuilder::SaveFH()
614{
615	if (fProcedure != ProcCompound)
616		return B_BAD_VALUE;
617	if (fRequest == NULL)
618		return B_NO_MEMORY;
619
620	fRequest->Stream().AddUInt(OpSaveFH);
621	fOpCount++;
622
623	return B_OK;
624}
625
626
627status_t
628RequestBuilder::SetAttr(const uint32* id, uint32 stateSeq, AttrValue* attr,
629	uint32 count)
630{
631	if (fProcedure != ProcCompound)
632		return B_BAD_VALUE;
633	if (fRequest == NULL)
634		return B_NO_MEMORY;
635
636	fRequest->Stream().AddUInt(OpSetAttr);
637	fRequest->Stream().AddUInt(stateSeq);
638	if (id != NULL) {
639		fRequest->Stream().AddUInt(id[0]);
640		fRequest->Stream().AddUInt(id[1]);
641		fRequest->Stream().AddUInt(id[2]);
642	} else {
643		fRequest->Stream().AddUInt(0);
644		fRequest->Stream().AddUInt(0);
645		fRequest->Stream().AddUInt(0);
646	}
647	_EncodeAttrs(fRequest->Stream(), attr, count);
648
649	fOpCount++;
650
651	return B_OK;
652}
653
654
655status_t
656RequestBuilder::SetClientID(RPC::Server* server)
657{
658	if (fProcedure != ProcCompound)
659		return B_BAD_VALUE;
660	if (fRequest == NULL)
661		return B_NO_MEMORY;
662
663	fRequest->Stream().AddUInt(OpSetClientID);
664	uint64 verifier = get_random<uint64>();
665	fRequest->Stream().AddUHyper(verifier);
666
667	status_t result = _GenerateClientId(fRequest->Stream(), server);
668	if (result != B_OK)
669		return result;
670
671	fRequest->Stream().AddUInt(0x40000000);
672
673	if (server->GetCallback() != NULL) {
674		ASSERT(server->GetCallback()->CBServer() != NULL);
675
676		uint32 id = server->GetCallback()->ID();
677
678		PeerAddress local = server->GetCallback()->CBServer()->LocalID();
679		PeerAddress servAddr = server->LocalID();
680		servAddr.SetPort(local.Port());
681
682		fRequest->Stream().AddString(local.ProtocolString());
683
684		char* uAddr = servAddr.UniversalAddress();
685		if (uAddr == NULL)
686			return B_NO_MEMORY;
687		fRequest->Stream().AddString(uAddr);
688		free(uAddr);
689
690		fRequest->Stream().AddUInt(id);
691	} else {
692		fRequest->Stream().AddString("");
693		fRequest->Stream().AddString("");
694		fRequest->Stream().AddUInt(0);
695	}
696
697	fOpCount++;
698
699	return B_OK;
700}
701
702
703status_t
704RequestBuilder::_GenerateClientId(XDR::WriteStream& stream,
705	const RPC::Server* server)
706{
707	char id[512] = "HAIKU:kernel:";
708	int pos = strlen(id);
709
710	PeerAddress local = server->LocalID();
711
712	memcpy(id + pos, server->ID().InAddr(), server->ID().InAddrSize());
713	pos += server->ID().InAddrSize();
714
715	memcpy(id + pos, local.InAddr(), local.InAddrSize());
716	pos += local.InAddrSize();
717
718	*(uint16*)(id + pos) = server->ID().Port();
719	pos += sizeof(uint16);
720
721	*(uint16*)(id + pos) = server->ID().fProtocol;
722	pos += sizeof(uint16);
723
724	stream.AddOpaque(id, pos);
725
726	return B_OK;
727}
728
729
730status_t
731RequestBuilder::SetClientIDConfirm(uint64 id, uint64 ver)
732{
733	if (fProcedure != ProcCompound)
734		return B_BAD_VALUE;
735	if (fRequest == NULL)
736		return B_NO_MEMORY;
737
738	fRequest->Stream().AddUInt(OpSetClientIDConfirm);
739	fRequest->Stream().AddUHyper(id);
740	fRequest->Stream().AddUHyper(ver);
741
742	fOpCount++;
743
744	return B_OK;
745}
746
747
748status_t
749RequestBuilder::Verify(AttrValue* attr, uint32 count)
750{
751	if (fProcedure != ProcCompound)
752		return B_BAD_VALUE;
753	if (fRequest == NULL)
754		return B_NO_MEMORY;
755
756	fRequest->Stream().AddUInt(OpVerify);
757	_EncodeAttrs(fRequest->Stream(), attr, count);
758
759	fOpCount++;
760
761	return B_OK;
762}
763
764
765status_t
766RequestBuilder::Write(const uint32* id, uint32 stateSeq, const void* buffer,
767	uint64 pos, uint32 len, bool stable)
768{
769	if (fProcedure != ProcCompound)
770		return B_BAD_VALUE;
771	if (fRequest == NULL)
772		return B_NO_MEMORY;
773
774	fRequest->Stream().AddUInt(OpWrite);
775	fRequest->Stream().AddUInt(stateSeq);
776	fRequest->Stream().AddUInt(id[0]);
777	fRequest->Stream().AddUInt(id[1]);
778	fRequest->Stream().AddUInt(id[2]);
779	fRequest->Stream().AddUHyper(pos);
780	fRequest->Stream().AddInt(stable ? FILE_SYNC4 : UNSTABLE4);
781	fRequest->Stream().AddOpaque(buffer, len);
782
783	fOpCount++;
784
785	return B_OK;
786}
787
788
789status_t
790RequestBuilder::ReleaseLockOwner(OpenState* state, LockOwner* owner)
791{
792	if (fProcedure != ProcCompound)
793		return B_BAD_VALUE;
794	if (fRequest == NULL)
795		return B_NO_MEMORY;
796
797	fRequest->Stream().AddUInt(OpReleaseLockOwner);
798	_GenerateLockOwner(fRequest->Stream(), state, owner);
799
800	fOpCount++;
801
802	return B_OK;
803}
804
805
806RPC::Call*
807RequestBuilder::Request()
808{
809	if (fProcedure == ProcCompound)
810		fRequest->Stream().InsertUInt(fOpCountPosition, fOpCount);
811
812	if (fRequest == NULL || fRequest->Stream().Error() == B_OK)
813		return fRequest;
814	else
815		return NULL;
816}
817
818
819void
820RequestBuilder::_AttrBitmap(XDR::WriteStream& stream, Attribute* attrs,
821	uint32 count)
822{
823	// 2 is safe in NFS4, not in NFS4.1 though
824	uint32 bitmap[2];
825	memset(bitmap, 0, sizeof(bitmap));
826	for (uint32 i = 0; i < count; i++) {
827		bitmap[attrs[i] / 32] |= 1 << attrs[i] % 32;
828	}
829
830	uint32 bcount = bitmap[1] != 0 ? 2 : 1;
831	stream.AddUInt(bcount);
832	for (uint32 i = 0; i < bcount; i++)
833		stream.AddUInt(bitmap[i]);
834}
835
836
837void
838RequestBuilder::_EncodeAttrs(XDR::WriteStream& stream, AttrValue* attr,
839	uint32 count)
840{
841	if (count == 0) {
842		stream.AddUInt(0);
843		stream.AddOpaque(NULL, 0);
844		return;
845	}
846
847	Attribute* attrs
848		= reinterpret_cast<Attribute*>(malloc(sizeof(Attribute) * count));
849	for (uint32 i = 0; i < count; i++)
850		attrs[i] = static_cast<Attribute>(attr[i].fAttribute);
851	_AttrBitmap(stream, attrs, count);
852	free(attrs);
853
854	uint32 i = 0;
855	XDR::WriteStream str;
856	if (i < count && attr[i].fAttribute == FATTR4_TYPE) {
857		str.AddUInt(attr[i].fData.fValue32);
858		i++;
859	}
860
861	if (i < count && attr[i].fAttribute == FATTR4_SIZE) {
862		str.AddUHyper(attr[i].fData.fValue64);
863		i++;
864	}
865
866	if (i < count && attr[i].fAttribute == FATTR4_FILEHANDLE) {
867		FileHandle* fh = reinterpret_cast<FileHandle*>(attr[i].fData.fPointer);
868		str.AddOpaque(fh->fData, fh->fSize);
869		i++;
870	}
871
872	if (i < count && attr[i].fAttribute == FATTR4_FILEID) {
873		str.AddUHyper(attr[i].fData.fValue64);
874		i++;
875	}
876
877	if (i < count && attr[i].fAttribute == FATTR4_MODE) {
878		str.AddUInt(attr[i].fData.fValue32);
879		i++;
880	}
881
882	if (i < count && attr[i].fAttribute == FATTR4_OWNER) {
883		str.AddString(reinterpret_cast<char*>(attr[i].fData.fPointer));
884		i++;
885	}
886
887	if (i < count && attr[i].fAttribute == FATTR4_OWNER_GROUP) {
888		str.AddString(reinterpret_cast<char*>(attr[i].fData.fPointer));
889		i++;
890	}
891
892	if (i < count && attr[i].fAttribute == FATTR4_TIME_ACCESS_SET) {
893		str.AddInt(1);		// SET_TO_CLIENT_TIME4
894
895		struct timespec* ts
896			= reinterpret_cast<timespec*>(attr[i].fData.fPointer);
897		str.AddHyper(ts->tv_sec);
898		str.AddUInt(ts->tv_nsec);
899
900		i++;
901	}
902
903	if (i < count && attr[i].fAttribute == FATTR4_TIME_MODIFY_SET) {
904		str.AddInt(1);		// SET_TO_CLIENT_TIME4
905
906		struct timespec* ts
907			= reinterpret_cast<timespec*>(attr[i].fData.fPointer);
908		str.AddHyper(ts->tv_sec);
909		str.AddUInt(ts->tv_nsec);
910
911		i++;
912	}
913
914	stream.AddOpaque(str);
915}
916
917