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 <AutoDeleter.h>
11
12#include "IdMap.h"
13#include "Inode.h"
14#include "NFS4Inode.h"
15#include "Request.h"
16
17
18status_t
19NFS4Inode::GetChangeInfo(uint64* change, bool attrDir)
20{
21	ASSERT(change != NULL);
22
23	uint32 attempt = 0;
24	do {
25		RPC::Server* serv = fFileSystem->Server();
26		Request request(serv, fFileSystem);
27		RequestBuilder& req = request.Builder();
28
29		if (attrDir)
30			req.PutFH(fInfo.fAttrDir);
31		else
32			req.PutFH(fInfo.fHandle);
33
34		Attribute attr[] = { FATTR4_CHANGE };
35		req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
36
37		status_t result = request.Send();
38		if (result != B_OK)
39			return result;
40
41		ReplyInterpreter& reply = request.Reply();
42
43		if (HandleErrors(attempt, reply.NFS4Error(), serv))
44			continue;
45
46		reply.PutFH();
47
48		AttrValue* values;
49		uint32 count;
50		result = reply.GetAttr(&values, &count);
51		if (result != B_OK)
52			return result;
53
54		// FATTR4_CHANGE is mandatory
55		*change = values[0].fData.fValue64;
56		delete[] values;
57
58		return B_OK;
59	} while (true);
60}
61
62
63status_t
64NFS4Inode::CommitWrites()
65{
66	uint32 attempt = 0;
67	do {
68		RPC::Server* serv = fFileSystem->Server();
69		Request request(serv, fFileSystem);
70		RequestBuilder& req = request.Builder();
71
72		req.PutFH(fInfo.fHandle);
73		req.Commit(0, 0);
74
75		status_t result = request.Send();
76		if (result != B_OK)
77			return result;
78
79		ReplyInterpreter& reply = request.Reply();
80
81		if (HandleErrors(attempt, reply.NFS4Error(), serv))
82			continue;
83
84		reply.PutFH();
85		return reply.Commit();
86	} while (true);
87}
88
89
90status_t
91NFS4Inode::Access(uint32* allowed)
92{
93	ASSERT(allowed != NULL);
94
95	uint32 attempt = 0;
96	do {
97		RPC::Server* serv = fFileSystem->Server();
98		Request request(serv, fFileSystem);
99		RequestBuilder& req = request.Builder();
100
101		req.PutFH(fInfo.fHandle);
102		req.Access();
103
104		status_t result = request.Send();
105		if (result != B_OK)
106			return result;
107
108		ReplyInterpreter& reply = request.Reply();
109
110		if (HandleErrors(attempt, reply.NFS4Error(), serv))
111			continue;
112
113		reply.PutFH();
114
115		return reply.Access(NULL, allowed);
116	} while (true);
117}
118
119
120status_t
121NFS4Inode::LookUp(const char* name, uint64* change, uint64* fileID,
122	FileHandle* handle, bool parent)
123{
124	ASSERT(name != NULL);
125
126	uint32 attempt = 0;
127	do {
128		RPC::Server* serv = fFileSystem->Server();
129		Request request(serv, fFileSystem);
130		RequestBuilder& req = request.Builder();
131
132		(void)parent;	// TODO: add support for named attributes
133		req.PutFH(fInfo.fHandle);
134
135		if (change != NULL) {
136			Attribute dirAttr[] = { FATTR4_CHANGE };
137			req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute));
138		}
139
140		if (!strcmp(name, ".."))
141			req.LookUpUp();
142		else
143			req.LookUp(name);
144
145		if (handle != NULL)
146			req.GetFH();
147
148		Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID };
149		req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
150
151		status_t result = request.Send();
152		if (result != B_OK)
153			return result;
154
155		ReplyInterpreter& reply = request.Reply();
156
157		if (HandleErrors(attempt, reply.NFS4Error(), serv))
158			continue;
159
160		reply.PutFH();
161
162		AttrValue* values;
163		uint32 count;
164		if (change != NULL) {
165			result = reply.GetAttr(&values, &count);
166			if (result != B_OK)
167				return result;
168
169			*change = values[0].fData.fValue64;
170			delete[] values;
171		}
172
173		if (!strcmp(name, ".."))
174			result = reply.LookUpUp();
175		else
176			result = reply.LookUp();
177		if (result != B_OK)
178			return result;
179
180		if (handle != NULL)
181			reply.GetFH(handle);
182
183		result = reply.GetAttr(&values, &count);
184		if (result != B_OK)
185			return result;
186
187		// FATTR4_FSID is mandatory
188		FileSystemId* fsid
189			= reinterpret_cast<FileSystemId*>(values[0].fData.fPointer);
190		if (*fsid != fFileSystem->FsId()) {
191			delete[] values;
192			return B_ENTRY_NOT_FOUND;
193		}
194
195		if (fileID != NULL) {
196			if (count < 2 || values[1].fAttribute != FATTR4_FILEID)
197				*fileID = fFileSystem->AllocFileId();
198			else
199				*fileID = values[1].fData.fValue64;
200		}
201
202		delete[] values;
203
204		return B_OK;
205	} while (true);
206}
207
208
209status_t
210NFS4Inode::Link(Inode* dir, const char* name, ChangeInfo* changeInfo)
211{
212	ASSERT(dir != NULL);
213	ASSERT(name != NULL);
214	ASSERT(changeInfo != NULL);
215
216	uint32 attempt = 0;
217	do {
218		RPC::Server* serv = fFileSystem->Server();
219		Request request(serv, fFileSystem);
220		RequestBuilder& req = request.Builder();
221
222		req.PutFH(fInfo.fHandle);
223		req.SaveFH();
224		req.PutFH(dir->fInfo.fHandle);
225		req.Link(name);
226
227		status_t result = request.Send();
228		if (result != B_OK)
229			return result;
230
231		ReplyInterpreter& reply = request.Reply();
232
233		if (HandleErrors(attempt, reply.NFS4Error(), serv))
234			continue;
235
236		reply.PutFH();
237		reply.SaveFH();
238		reply.PutFH();
239
240		return reply.Link(&changeInfo->fBefore, &changeInfo->fAfter,
241			changeInfo->fAtomic);
242	} while (true);
243}
244
245
246status_t
247NFS4Inode::ReadLink(void* buffer, size_t* length)
248{
249	ASSERT(buffer != NULL);
250	ASSERT(length != NULL);
251
252	uint32 attempt = 0;
253	do {
254		RPC::Server* serv = fFileSystem->Server();
255		Request request(serv, fFileSystem);
256		RequestBuilder& req = request.Builder();
257
258		req.PutFH(fInfo.fHandle);
259		req.ReadLink();
260
261		status_t result = request.Send();
262		if (result != B_OK)
263			return result;
264
265		ReplyInterpreter& reply = request.Reply();
266
267		if (HandleErrors(attempt, reply.NFS4Error(), serv))
268			continue;
269
270		reply.PutFH();
271
272		uint32 size;
273		result = reply.ReadLink(buffer, &size, *length);
274		*length = static_cast<size_t>(size);
275
276		return result;
277	} while (true);
278}
279
280
281status_t
282NFS4Inode::GetStat(AttrValue** values, uint32* count, OpenAttrCookie* cookie)
283{
284	ASSERT(values != NULL);
285	ASSERT(count != NULL);
286
287	uint32 attempt = 0;
288	do {
289		RPC::Server* serv = fFileSystem->Server();
290		Request request(serv, fFileSystem);
291		RequestBuilder& req = request.Builder();
292
293		if (cookie != NULL)
294			req.PutFH(cookie->fOpenState->fInfo.fHandle);
295		else
296			req.PutFH(fInfo.fHandle);
297
298		Attribute attr[] = { FATTR4_SIZE, FATTR4_MODE, FATTR4_NUMLINKS,
299							FATTR4_OWNER, FATTR4_OWNER_GROUP,
300							FATTR4_TIME_ACCESS, FATTR4_TIME_CREATE,
301							FATTR4_TIME_METADATA, FATTR4_TIME_MODIFY };
302		req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
303
304		status_t result = request.Send(cookie);
305		if (result != B_OK)
306			return result;
307
308		ReplyInterpreter& reply = request.Reply();
309
310		if (HandleErrors(attempt, reply.NFS4Error(), serv))
311			continue;
312
313		reply.PutFH();
314
315		return reply.GetAttr(values, count);
316	} while (true);
317}
318
319
320status_t
321NFS4Inode::WriteStat(OpenState* state, AttrValue* attrs, uint32 attrCount)
322{
323	ASSERT(attrs != NULL);
324
325	uint32 attempt = 0;
326	do {
327		RPC::Server* serv = fFileSystem->Server();
328		Request request(serv, fFileSystem);
329		RequestBuilder& req = request.Builder();
330
331		if (state != NULL) {
332			req.PutFH(state->fInfo.fHandle);
333			req.SetAttr(state->fStateID, state->fStateSeq, attrs, attrCount);
334		} else {
335			req.PutFH(fInfo.fHandle);
336			req.SetAttr(NULL, 0, attrs, attrCount);
337		}
338
339		status_t result = request.Send();
340		if (result != B_OK)
341			return result;
342
343		ReplyInterpreter& reply = request.Reply();
344
345		if (HandleErrors(attempt, reply.NFS4Error(), serv))
346			continue;
347
348		reply.PutFH();
349		result = reply.SetAttr();
350
351		if (result != B_OK)
352			return result;
353
354		return B_OK;
355	} while (true);
356}
357
358
359status_t
360NFS4Inode::RenameNode(Inode* from, Inode* to, const char* fromName,
361	const char* toName, ChangeInfo* fromChange, ChangeInfo* toChange,
362	uint64* fileID, bool attribute)
363{
364	ASSERT(from != NULL);
365	ASSERT(to != NULL);
366	ASSERT(fromName != NULL);
367	ASSERT(toName != NULL);
368	ASSERT(fromChange != NULL);
369	ASSERT(toChange != NULL);
370
371	uint32 attempt = 0;
372	do {
373		RPC::Server* server = from->fFileSystem->Server();
374		Request request(server, from->fFileSystem);
375		RequestBuilder& req = request.Builder();
376
377		if (attribute)
378			req.PutFH(from->fInfo.fAttrDir);
379		else
380			req.PutFH(from->fInfo.fHandle);
381		req.SaveFH();
382		if (attribute)
383			req.PutFH(to->fInfo.fAttrDir);
384		else
385			req.PutFH(to->fInfo.fHandle);
386		req.Rename(fromName, toName);
387
388		if (!attribute) {
389			req.LookUp(toName);
390			Attribute attr[] = { FATTR4_FILEID };
391			req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
392		}
393
394		status_t result = request.Send();
395		if (result != B_OK)
396			return result;
397
398		ReplyInterpreter& reply = request.Reply();
399
400		// If we have to wait, migrate to another server, etc then the first
401		// HandleErrors() will do that. However, if the file handles
402		// were invalid then we need to update both Inodes.
403		bool retry = from->HandleErrors(attempt, reply.NFS4Error(), server);
404		if (IsFileHandleInvalid(reply.NFS4Error()))
405			retry |= to->HandleErrors(attempt, reply.NFS4Error(), server);
406		if (retry)
407			continue;
408
409		reply.PutFH();
410		reply.SaveFH();
411		reply.PutFH();
412
413		result = reply.Rename(&fromChange->fBefore, &fromChange->fAfter,
414			fromChange->fAtomic, &toChange->fBefore, &toChange->fAfter,
415			toChange->fAtomic);
416		if (result != B_OK)
417			return result;
418
419		if (!attribute) {
420			result = reply.LookUp();
421			if (result != B_OK)
422				return result;
423
424			AttrValue* values;
425			uint32 count;
426			result = reply.GetAttr(&values, &count);
427			if (result != B_OK)
428				return result;
429
430			if (count == 0)
431				*fileID = from->fFileSystem->AllocFileId();
432			else
433				*fileID = values[0].fData.fValue64;
434
435			delete[] values;
436		}
437
438		return B_OK;
439	} while (true);
440}
441
442
443status_t
444NFS4Inode::CreateFile(const char* name, int mode, int perms, OpenState* state,
445	ChangeInfo* changeInfo, uint64* fileID, FileHandle* handle,
446	OpenDelegationData* delegation)
447{
448	ASSERT(name != NULL);
449	ASSERT(state != NULL);
450	ASSERT(changeInfo != NULL);
451	ASSERT(handle != NULL);
452	ASSERT(delegation != NULL);
453
454	bool confirm;
455	status_t result;
456
457	uint32 attempt = 0;
458	uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
459	do {
460		state->fClientID = fFileSystem->NFSServer()->ClientId();
461
462		RPC::Server* serv = fFileSystem->Server();
463		Request request(serv, fFileSystem);
464		RequestBuilder& req = request.Builder();
465
466		req.PutFH(fInfo.fHandle);
467
468		AttrValue cattr[2];
469		uint32 i = 0;
470		if ((mode & O_TRUNC) == O_TRUNC) {
471			cattr[i].fAttribute = FATTR4_SIZE;
472			cattr[i].fFreePointer = false;
473			cattr[i].fData.fValue64 = 0;
474			i++;
475		}
476		cattr[i].fAttribute = FATTR4_MODE;
477		cattr[i].fFreePointer = false;
478		cattr[i].fData.fValue32 = perms;
479		i++;
480
481		req.Open(CLAIM_NULL, sequence, sModeToAccess(mode),
482			state->fClientID, OPEN4_CREATE, fFileSystem->OpenOwner(), name,
483			cattr, i, (mode & O_EXCL) == O_EXCL);
484
485		req.GetFH();
486
487		if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
488			Attribute attr[] = { FATTR4_FILEID };
489			req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
490		}
491
492		result = request.Send();
493		if (result != B_OK) {
494			fFileSystem->OpenOwnerSequenceUnlock(sequence);
495			return result;
496		}
497
498		ReplyInterpreter& reply = request.Reply();
499
500		result = reply.PutFH();
501		if (result == B_OK)
502			sequence += IncrementSequence(reply.NFS4Error());
503
504		if (HandleErrors(attempt, reply.NFS4Error(), serv, NULL, state,
505				&sequence)) {
506			continue;
507		}
508
509		result = reply.Open(state->fStateID, &state->fStateSeq, &confirm,
510			delegation, changeInfo);
511		if (result != B_OK) {
512			fFileSystem->OpenOwnerSequenceUnlock(sequence);
513			return result;
514		}
515
516		reply.GetFH(handle);
517
518		if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
519			AttrValue* values;
520			uint32 count;
521			result = reply.GetAttr(&values, &count);
522			if (result != B_OK) {
523				fFileSystem->OpenOwnerSequenceUnlock(sequence);
524				return result;
525			}
526
527			*fileID = values[0].fData.fValue64;
528
529			delete[] values;
530		} else
531			*fileID = fFileSystem->AllocFileId();
532
533		break;
534	} while (true);
535
536	state->fOpened = true;
537
538	if (confirm)
539		result = ConfirmOpen(*handle, state, &sequence);
540
541	fFileSystem->OpenOwnerSequenceUnlock(sequence);
542	return result;
543}
544
545
546status_t
547NFS4Inode::OpenFile(OpenState* state, int mode, OpenDelegationData* delegation)
548{
549	ASSERT(state != NULL);
550	ASSERT(delegation != NULL);
551
552	bool confirm;
553	status_t result;
554
555	uint32 attempt = 0;
556	uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
557	do {
558		state->fClientID = fFileSystem->NFSServer()->ClientId();
559
560		RPC::Server* serv = fFileSystem->Server();
561		Request request(serv, fFileSystem);
562		RequestBuilder& req = request.Builder();
563
564		// Since we are opening the file using a pair (parentFH, name) we
565		// need to check for race conditions.
566		if (fFileSystem->IsAttrSupported(FATTR4_FILEID)) {
567			req.PutFH(fInfo.fNames->fNames.Head()->fParent->fHandle);
568			req.LookUp(fInfo.fNames->fNames.Head()->fName);
569			AttrValue attr;
570			attr.fAttribute = FATTR4_FILEID;
571			attr.fFreePointer = false;
572			attr.fData.fValue64 = fInfo.fFileId;
573			req.Verify(&attr, 1);
574		} else if (fFileSystem->ExpireType() == FH4_PERSISTENT) {
575			req.PutFH(fInfo.fNames->fNames.Head()->fParent->fHandle);
576			req.LookUp(fInfo.fNames->fNames.Head()->fName);
577			AttrValue attr;
578			attr.fAttribute = FATTR4_FILEHANDLE;
579			attr.fFreePointer = true;
580			attr.fData.fPointer = malloc(sizeof(fInfo.fHandle));
581			memcpy(attr.fData.fPointer, &fInfo.fHandle, sizeof(fInfo.fHandle));
582			req.Verify(&attr, 1);
583		}
584
585		req.PutFH(fInfo.fNames->fNames.Head()->fParent->fHandle);
586		req.Open(CLAIM_NULL, sequence, sModeToAccess(mode), state->fClientID,
587			OPEN4_NOCREATE, fFileSystem->OpenOwner(),
588			fInfo.fNames->fNames.Head()->fName);
589		req.GetFH();
590
591		result = request.Send();
592		if (result != B_OK) {
593			fFileSystem->OpenOwnerSequenceUnlock(sequence);
594			return result;
595		}
596
597		ReplyInterpreter& reply = request.Reply();
598
599		// Verify if the file we want to open is the file this Inode
600		// represents.
601		if (fFileSystem->IsAttrSupported(FATTR4_FILEID)
602			|| fFileSystem->ExpireType() == FH4_PERSISTENT) {
603			reply.PutFH();
604			result = reply.LookUp();
605			if (result != B_OK) {
606				fFileSystem->OpenOwnerSequenceUnlock(sequence);
607				return result;
608			}
609
610			result = reply.Verify();
611			if (result != B_OK && reply.NFS4Error() == NFS4ERR_NOT_SAME) {
612				fFileSystem->OpenOwnerSequenceUnlock(sequence);
613				return B_ENTRY_NOT_FOUND;
614			}
615		}
616
617		result = reply.PutFH();
618		if (result == B_OK)
619			sequence += IncrementSequence(reply.NFS4Error());
620
621		if (HandleErrors(attempt, reply.NFS4Error(), serv, NULL, state,
622				&sequence)) {
623			continue;
624		}
625
626		result = reply.Open(state->fStateID, &state->fStateSeq, &confirm,
627			delegation);
628		if (result != B_OK) {
629			fFileSystem->OpenOwnerSequenceUnlock(sequence);
630			return result;
631		}
632
633		FileHandle handle;
634		result = reply.GetFH(&handle);
635		if (result != B_OK) {
636			fFileSystem->OpenOwnerSequenceUnlock(sequence);
637			return result;
638		}
639
640		break;
641	} while (true);
642
643	state->fOpened = true;
644
645	if (confirm)
646		result = ConfirmOpen(fInfo.fHandle, state, &sequence);
647
648	fFileSystem->OpenOwnerSequenceUnlock(sequence);
649	return result;
650}
651
652
653status_t
654NFS4Inode::OpenAttr(OpenState* state, const char* name, int mode,
655	OpenDelegationData* delegation, bool create)
656{
657	ASSERT(name != NULL);
658	ASSERT(state != NULL);
659	ASSERT(delegation != NULL);
660
661	bool confirm;
662	status_t result;
663
664	uint32 attempt = 0;
665	uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
666	do {
667		state->fClientID = fFileSystem->NFSServer()->ClientId();
668
669		RPC::Server* serv = fFileSystem->Server();
670		Request request(serv, fFileSystem);
671		RequestBuilder& req = request.Builder();
672
673		req.PutFH(fInfo.fAttrDir);
674		req.Open(CLAIM_NULL, sequence, sModeToAccess(mode), state->fClientID,
675			create ? OPEN4_CREATE : OPEN4_NOCREATE, fFileSystem->OpenOwner(),
676			name);
677		req.GetFH();
678
679		result = request.Send();
680		if (result != B_OK) {
681			fFileSystem->OpenOwnerSequenceUnlock(sequence);
682			return result;
683		}
684
685		ReplyInterpreter& reply = request.Reply();
686
687		result = reply.PutFH();
688		if (result == B_OK)
689			sequence += IncrementSequence(reply.NFS4Error());
690
691		if (HandleErrors(attempt, reply.NFS4Error(), serv, NULL, state,
692				&sequence)) {
693			continue;
694		}
695
696		result = reply.Open(state->fStateID, &state->fStateSeq, &confirm,
697			delegation);
698
699		reply.GetFH(&state->fInfo.fHandle);
700
701		if (result != B_OK) {
702			fFileSystem->OpenOwnerSequenceUnlock(sequence);
703			return result;
704		}
705
706		break;
707	} while (true);
708
709	state->fOpened = true;
710
711	if (confirm)
712		result = ConfirmOpen(state->fInfo.fHandle, state, &sequence);
713
714	fFileSystem->OpenOwnerSequenceUnlock(sequence);
715	return result;
716}
717
718
719status_t
720NFS4Inode::ReadFile(OpenStateCookie* cookie, OpenState* state, uint64 position,
721	uint32* length, void* buffer, bool* eof)
722{
723	ASSERT(state != NULL);
724	ASSERT(length != NULL);
725	ASSERT(buffer != NULL);
726	ASSERT(eof != NULL);
727
728	uint32 attempt = 0;
729	do {
730		RPC::Server* serv = fFileSystem->Server();
731		Request request(serv, fFileSystem);
732		RequestBuilder& req = request.Builder();
733
734		req.PutFH(state->fInfo.fHandle);
735		req.Read(state->fStateID, state->fStateSeq, position, *length);
736
737		status_t result = request.Send(cookie);
738		if (result != B_OK)
739			return result;
740
741		ReplyInterpreter& reply = request.Reply();
742
743		if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie, state))
744			continue;
745
746		reply.PutFH();
747		result = reply.Read(buffer, length, eof);
748		if (result != B_OK)
749			return result;
750
751		return B_OK;
752	} while (true);
753}
754
755
756status_t
757NFS4Inode::WriteFile(OpenStateCookie* cookie, OpenState* state, uint64 position,
758	uint32* length, const void* buffer, bool commit)
759{
760	ASSERT(state != NULL);
761	ASSERT(length != NULL);
762	ASSERT(buffer != NULL);
763
764	uint32 attempt = 0;
765	do {
766		RPC::Server* serv = fFileSystem->Server();
767		Request request(serv, fFileSystem);
768		RequestBuilder& req = request.Builder();
769
770		req.PutFH(state->fInfo.fHandle);
771
772		req.Write(state->fStateID, state->fStateSeq, buffer, position, *length,
773			commit);
774
775		status_t result = request.Send(cookie);
776		if (result != B_OK)
777			return result;
778
779		ReplyInterpreter& reply = request.Reply();
780
781		if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie, state))
782			continue;
783
784		reply.PutFH();
785
786		result = reply.Write(length);
787		if (result != B_OK)
788			return result;
789
790		return B_OK;
791	} while (true);
792}
793
794
795status_t
796NFS4Inode::CreateObject(const char* name, const char* path, int mode,
797	FileType type, ChangeInfo* changeInfo, uint64* fileID, FileHandle* handle,
798	bool parent)
799{
800	ASSERT(name != NULL);
801	ASSERT(changeInfo != NULL);
802	ASSERT(handle != NULL);
803
804	uint32 attempt = 0;
805	do {
806		RPC::Server* serv = fFileSystem->Server();
807		Request request(serv, fFileSystem);
808		RequestBuilder& req = request.Builder();
809
810		(void)parent;	// TODO: support named attributes
811		req.PutFH(fInfo.fHandle);
812
813		uint32 i = 0;
814		AttrValue cattr[1];
815		cattr[i].fAttribute = FATTR4_MODE;
816		cattr[i].fFreePointer = false;
817		cattr[i].fData.fValue32 = mode;
818		i++;
819
820		switch (type) {
821			case NF4DIR:
822				req.Create(NF4DIR, name, cattr, i);
823				break;
824			case NF4LNK:
825				req.Create(NF4LNK, name, cattr, i, path);
826				break;
827			default:
828				return B_BAD_VALUE;
829		}
830
831		req.GetFH();
832
833		if (fileID != NULL) {
834			Attribute attr[] = { FATTR4_FILEID };
835			req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
836		}
837
838		status_t result = request.Send();
839		if (result != B_OK)
840			return result;
841
842		ReplyInterpreter& reply = request.Reply();
843
844		if (HandleErrors(attempt, reply.NFS4Error(), serv))
845			continue;
846
847		reply.PutFH();
848		result = reply.Create(&changeInfo->fBefore, &changeInfo->fAfter,
849			changeInfo->fAtomic);
850		if (result != B_OK)
851			return result;
852
853		result = reply.GetFH(handle);
854		if (result != B_OK)
855			return result;
856
857		if (fileID != NULL) {
858			AttrValue* values;
859			uint32 count;
860			result = reply.GetAttr(&values, &count);
861			if (result != B_OK)
862				return result;
863
864			if (count == 0)
865				*fileID = fFileSystem->AllocFileId();
866			else
867				*fileID = values[0].fData.fValue64;
868
869			delete[] values;
870		}
871
872		return B_OK;
873	} while (true);
874}
875
876
877status_t
878NFS4Inode::RemoveObject(const char* name, FileType type, ChangeInfo* changeInfo,
879	uint64* fileID)
880{
881	ASSERT(name != NULL);
882	ASSERT(changeInfo != NULL);
883
884	uint32 attempt = 0;
885	do {
886		RPC::Server* serv = fFileSystem->Server();
887		Request request(serv, fFileSystem);
888		RequestBuilder& req = request.Builder();
889
890		req.PutFH(fInfo.fHandle);
891		req.LookUp(name);
892		AttrValue attr;
893		attr.fAttribute = FATTR4_TYPE;
894		attr.fFreePointer = false;
895		attr.fData.fValue32 = NF4DIR;
896		if (type == NF4DIR)
897			req.Verify(&attr, 1);
898		else
899			req.Nverify(&attr, 1);
900
901		if (type != NF4NAMEDATTR) {
902			Attribute idAttr[] = { FATTR4_FILEID };
903			req.GetAttr(idAttr, sizeof(idAttr) / sizeof(Attribute));
904		}
905
906		req.PutFH(fInfo.fHandle);
907		req.Remove(name);
908
909		status_t result = request.Send();
910		if (result != B_OK)
911			return result;
912
913		ReplyInterpreter& reply = request.Reply();
914
915		if (HandleErrors(attempt, reply.NFS4Error(), serv))
916			continue;
917
918		reply.PutFH();
919		result = reply.LookUp();
920		if (result != B_OK)
921			return result;
922
923		if (type == NF4DIR)
924			result = reply.Verify();
925		else
926			result = reply.Nverify();
927
928		if (result == NFS4ERR_SAME && type != NF4DIR)
929			return B_IS_A_DIRECTORY;
930		if (result == NFS4ERR_NOT_SAME && type == NF4DIR)
931			return B_NOT_A_DIRECTORY;
932		if (result != B_OK)
933			return result;
934
935		if (type != NF4NAMEDATTR) {
936			AttrValue* values;
937			uint32 count;
938			result = reply.GetAttr(&values, &count);
939			if (result != B_OK)
940				return result;
941
942			if (count == 0)
943				*fileID = fFileSystem->AllocFileId();
944			else
945				*fileID = values[0].fData.fValue64;
946			delete[] values;
947		}
948
949		reply.PutFH();
950		return reply.Remove(&changeInfo->fBefore, &changeInfo->fAfter,
951			changeInfo->fAtomic);
952	} while (true);
953}
954
955
956status_t
957NFS4Inode::ReadDirOnce(DirEntry** dirents, uint32* count, OpenDirCookie* cookie,
958	bool* eof, uint64* change, uint64* dirCookie, uint64* dirCookieVerf,
959	bool attribute)
960{
961	ASSERT(dirents != NULL);
962	ASSERT(count != NULL);
963	ASSERT(eof != NULL);
964
965	uint32 attempt = 0;
966	do {
967		RPC::Server* serv = fFileSystem->Server();
968		Request request(serv, fFileSystem);
969		RequestBuilder& req = request.Builder();
970
971		if (attribute)
972			req.PutFH(fInfo.fAttrDir);
973		else
974			req.PutFH(fInfo.fHandle);
975
976		Attribute dirAttr[] = { FATTR4_CHANGE };
977		if (*change == 0)
978			req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute));
979
980		Attribute attr[] = { FATTR4_FSID, FATTR4_FILEID };
981		req.ReadDir(*dirCookie, *dirCookieVerf, attr,
982			sizeof(attr) / sizeof(Attribute));
983
984		req.GetAttr(dirAttr, sizeof(dirAttr) / sizeof(Attribute));
985
986		status_t result = request.Send(cookie);
987		if (result != B_OK)
988			return result;
989
990		ReplyInterpreter& reply = request.Reply();
991
992		if (HandleErrors(attempt, reply.NFS4Error(), serv))
993			continue;
994
995		reply.PutFH();
996
997		AttrValue* before = NULL;
998		uint32 attrCount;
999		if (*change == 0) {
1000			result = reply.GetAttr(&before, &attrCount);
1001			if (result != B_OK)
1002				return result;
1003		}
1004		ArrayDeleter<AttrValue> beforeDeleter(before);
1005
1006		result = reply.ReadDir(dirCookie, dirCookieVerf, dirents, count, eof);
1007		if (result != B_OK)
1008			return result;
1009
1010		ArrayDeleter<DirEntry> entriesDeleter(*dirents);
1011
1012		AttrValue* after;
1013		result = reply.GetAttr(&after, &attrCount);
1014		if (result != B_OK)
1015			return result;
1016
1017		ArrayDeleter<AttrValue> afterDeleter(after);
1018
1019		if ((*change == 0
1020				&& before[0].fData.fValue64 == after[0].fData.fValue64)
1021			|| *change == after[0].fData.fValue64)
1022			*change = after[0].fData.fValue64;
1023		else
1024			return B_ERROR;
1025
1026		entriesDeleter.Detach();
1027		return B_OK;
1028	} while (true);
1029}
1030
1031
1032status_t
1033NFS4Inode::OpenAttrDir(FileHandle* handle)
1034{
1035	ASSERT(handle != NULL);
1036
1037	uint32 attempt = 0;
1038	do {
1039		RPC::Server* serv = fFileSystem->Server();
1040		Request request(serv, fFileSystem);
1041		RequestBuilder& req = request.Builder();
1042
1043		req.PutFH(fInfo.fHandle);
1044		req.OpenAttrDir(true);
1045		req.GetFH();
1046
1047		status_t result = request.Send();
1048		if (result != B_OK)
1049			return result;
1050
1051		ReplyInterpreter& reply = request.Reply();
1052
1053		if (HandleErrors(attempt, reply.NFS4Error(), serv))
1054			continue;
1055
1056		reply.PutFH();
1057		result = reply.OpenAttrDir();
1058		if (result != B_OK)
1059			return result;
1060
1061		return reply.GetFH(handle);
1062	} while (true);
1063}
1064
1065
1066status_t
1067NFS4Inode::TestLock(OpenFileCookie* cookie, LockType* type, uint64* position,
1068	uint64* length, bool& conflict)
1069{
1070	ASSERT(cookie != NULL);
1071	ASSERT(type != NULL);
1072	ASSERT(position != NULL);
1073	ASSERT(length != NULL);
1074
1075	uint32 attempt = 0;
1076	do {
1077		RPC::Server* serv = fFileSystem->Server();
1078		Request request(serv, fFileSystem);
1079		RequestBuilder& req = request.Builder();
1080
1081		req.PutFH(fInfo.fHandle);
1082		req.LockT(*type, *position, *length, cookie->fOpenState);
1083
1084		status_t result = request.Send();
1085		if (result != B_OK)
1086			return result;
1087
1088		ReplyInterpreter& reply = request.Reply();
1089		if (reply.NFS4Error() != NFS4ERR_DENIED) {
1090			if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie))
1091				continue;
1092		}
1093
1094		reply.PutFH();
1095		result = reply.LockT(position, length, type);
1096		if (reply.NFS4Error() == NFS4ERR_DENIED) {
1097			conflict = true;
1098			result = B_OK;
1099		} else if (reply.NFS4Error() == NFS4_OK)
1100			conflict = false;
1101
1102		return result;
1103	} while (true);
1104
1105	return B_OK;
1106}
1107
1108
1109status_t
1110NFS4Inode::AcquireLock(OpenFileCookie* cookie, LockInfo* lockInfo, bool wait)
1111{
1112	ASSERT(cookie != NULL);
1113	ASSERT(lockInfo != NULL);
1114
1115	uint32 attempt = 0;
1116	uint32 sequence = fFileSystem->OpenOwnerSequenceLock();
1117	do {
1118		MutexLocker ownerLocker(lockInfo->fOwner->fLock);
1119
1120		RPC::Server* serv = fFileSystem->Server();
1121		Request request(serv, fFileSystem);
1122		RequestBuilder& req = request.Builder();
1123
1124		req.PutFH(fInfo.fHandle);
1125		req.Lock(cookie->fOpenState, lockInfo, &sequence);
1126
1127		status_t result = request.Send();
1128		if (result != B_OK) {
1129			fFileSystem->OpenOwnerSequenceUnlock(sequence);
1130			return result;
1131		}
1132
1133		ReplyInterpreter& reply = request.Reply();
1134
1135		result = reply.PutFH();
1136		if (result == B_OK)
1137			sequence += IncrementSequence(reply.NFS4Error());
1138
1139		result = reply.Lock(lockInfo);
1140
1141		ownerLocker.Unlock();
1142
1143		if (reply.NFS4Error() != NFS4ERR_DENIED || wait) {
1144			if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie, NULL,
1145					&sequence)) {
1146				continue;
1147			}
1148		}
1149
1150		fFileSystem->OpenOwnerSequenceUnlock(sequence);
1151		if (result != B_OK)
1152			return result;
1153
1154		return B_OK;
1155	} while (true);
1156}
1157
1158
1159status_t
1160NFS4Inode::ReleaseLock(OpenFileCookie* cookie, LockInfo* lockInfo)
1161{
1162	ASSERT(cookie != NULL);
1163	ASSERT(lockInfo != NULL);
1164
1165	uint32 attempt = 0;
1166	do {
1167		MutexLocker ownerLocker(lockInfo->fOwner->fLock);
1168
1169		RPC::Server* serv = fFileSystem->Server();
1170		Request request(serv, fFileSystem);
1171		RequestBuilder& req = request.Builder();
1172
1173		req.PutFH(fInfo.fHandle);
1174		req.LockU(lockInfo);
1175
1176		status_t result = request.Send();
1177		if (result != B_OK)
1178			return result;
1179
1180		ReplyInterpreter& reply = request.Reply();
1181
1182		reply.PutFH();
1183		result = reply.LockU(lockInfo);
1184
1185		ownerLocker.Unlock();
1186		if (HandleErrors(attempt, reply.NFS4Error(), serv, cookie))
1187			continue;
1188
1189		if (result != B_OK)
1190			return result;
1191
1192		return B_OK;
1193	} while (true);
1194}
1195
1196