xref: /haiku/src/add-ons/kernel/file_systems/nfs4/NFS4Inode.cpp (revision 71452e98334eaac603bf542d159e24788a46bebb)
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 
18 status_t
19 NFS4Inode::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 
63 status_t
64 NFS4Inode::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 
90 status_t
91 NFS4Inode::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 
120 status_t
121 NFS4Inode::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 
209 status_t
210 NFS4Inode::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 
246 status_t
247 NFS4Inode::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 
281 status_t
282 NFS4Inode::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 
320 status_t
321 NFS4Inode::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 
359 status_t
360 NFS4Inode::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 
443 status_t
444 NFS4Inode::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 
546 status_t
547 NFS4Inode::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 
653 status_t
654 NFS4Inode::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 
719 status_t
720 NFS4Inode::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 
756 status_t
757 NFS4Inode::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 
795 status_t
796 NFS4Inode::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 
877 status_t
878 NFS4Inode::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 
956 status_t
957 NFS4Inode::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 
1032 status_t
1033 NFS4Inode::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 
1066 status_t
1067 NFS4Inode::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 
1109 status_t
1110 NFS4Inode::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 
1159 status_t
1160 NFS4Inode::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