1/*
2 * Copyright 2012-2016 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 "Inode.h"
11
12#include <ctype.h>
13#include <string.h>
14
15#include <AutoDeleter.h>
16#include <fs_cache.h>
17#include <NodeMonitor.h>
18
19#include "IdMap.h"
20#include "Request.h"
21#include "RootInode.h"
22
23
24Inode::Inode()
25	:
26	fMetaCache(this),
27	fCache(NULL),
28	fAttrCache(NULL),
29	fDelegation(NULL),
30	fFileCache(NULL),
31	fMaxFileSize(0),
32	fOpenState(NULL),
33	fWriteDirty(false),
34	fAIOWait(create_sem(1, NULL)),
35	fAIOCount(0)
36{
37	rw_lock_init(&fDelegationLock, NULL);
38	mutex_init(&fStateLock, NULL);
39	mutex_init(&fFileCacheLock, NULL);
40	rw_lock_init(&fWriteLock, NULL);
41	mutex_init(&fAIOLock, NULL);
42}
43
44
45status_t
46Inode::CreateInode(FileSystem* fs, const FileInfo& fi, Inode** _inode)
47{
48	ASSERT(fs != NULL);
49	ASSERT(_inode != NULL);
50
51	Inode* inode = NULL;
52	if (fs->Root() == NULL)
53		inode = new(std::nothrow) RootInode;
54	else
55		inode = new(std::nothrow) Inode;
56
57	if (inode == NULL)
58		return B_NO_MEMORY;
59
60	inode->fInfo = fi;
61	inode->fFileSystem = fs;
62
63	uint32 attempt = 0;
64	uint64 size;
65	do {
66		RPC::Server* serv = fs->Server();
67		Request request(serv, fs);
68		RequestBuilder& req = request.Builder();
69
70		req.PutFH(inode->fInfo.fHandle);
71
72		Attribute attr[] = { FATTR4_TYPE, FATTR4_CHANGE, FATTR4_SIZE,
73			FATTR4_FSID, FATTR4_FILEID };
74		req.GetAttr(attr, sizeof(attr) / sizeof(Attribute));
75
76		status_t result = request.Send();
77		if (result != B_OK)
78			return result;
79
80		ReplyInterpreter& reply = request.Reply();
81
82		if (inode->HandleErrors(attempt, reply.NFS4Error(), serv))
83			continue;
84
85		reply.PutFH();
86
87		AttrValue* values;
88		uint32 count;
89		result = reply.GetAttr(&values, &count);
90		if (result != B_OK)
91			return result;
92
93		if (fi.fFileId == 0) {
94			if (count < 5 || values[4].fAttribute != FATTR4_FILEID)
95				inode->fInfo.fFileId = fs->AllocFileId();
96			else
97				inode->fInfo.fFileId = values[4].fData.fValue64;
98		} else
99			inode->fInfo.fFileId = fi.fFileId;
100
101		// FATTR4_TYPE is mandatory
102		inode->fType = values[0].fData.fValue32;
103
104		if (inode->fType == NF4DIR)
105			inode->fCache = new DirectoryCache(inode);
106		inode->fAttrCache = new DirectoryCache(inode, true);
107
108		// FATTR4_CHANGE is mandatory
109		inode->fChange = values[1].fData.fValue64;
110
111		// FATTR4_SIZE is mandatory
112		size = values[2].fData.fValue64;
113		inode->fMaxFileSize = size;
114
115		// FATTR4_FSID is mandatory
116		FileSystemId* fsid
117			= reinterpret_cast<FileSystemId*>(values[3].fData.fPointer);
118		if (*fsid != fs->FsId()) {
119			delete[] values;
120			return B_ENTRY_NOT_FOUND;
121		}
122
123		delete[] values;
124
125		*_inode = inode;
126
127		break;
128	} while (true);
129
130	if (inode->fType == NF4REG)
131		inode->fFileCache = file_cache_create(fs->DevId(), inode->ID(), size);
132
133	return B_OK;
134}
135
136
137Inode::~Inode()
138{
139	if (fDelegation != NULL)
140		RecallDelegation();
141
142	if (fFileCache != NULL)
143		file_cache_delete(fFileCache);
144
145	delete fCache;
146	delete fAttrCache;
147
148	delete_sem(fAIOWait);
149	mutex_destroy(&fAIOLock);
150	mutex_destroy(&fStateLock);
151	mutex_destroy(&fFileCacheLock);
152	rw_lock_destroy(&fDelegationLock);
153	rw_lock_destroy(&fWriteLock);
154
155	ASSERT(fAIOCount == 0);
156}
157
158
159status_t
160Inode::RevalidateFileCache()
161{
162	if (fDelegation != NULL)
163		return B_OK;
164
165	uint64 change;
166	status_t result = GetChangeInfo(&change);
167	if (result != B_OK)
168		return result;
169
170	MutexLocker _(fFileCacheLock);
171	if (change == fChange)
172		return B_OK;
173	SyncAndCommit(true);
174
175	file_cache_delete(fFileCache);
176
177	struct stat st;
178	fMetaCache.InvalidateStat();
179	result = Stat(&st);
180	if (result == B_OK)
181		fMaxFileSize = st.st_size;
182	fFileCache = file_cache_create(fFileSystem->DevId(), ID(), fMaxFileSize);
183
184	change = fChange;
185	return B_OK;
186}
187
188
189status_t
190Inode::LookUp(const char* name, ino_t* id)
191{
192	ASSERT(name != NULL);
193	ASSERT(id != NULL);
194
195	if (fType != NF4DIR)
196		return B_NOT_A_DIRECTORY;
197
198	uint64 change;
199	uint64 fileID;
200	FileHandle handle;
201	status_t result = NFS4Inode::LookUp(name, &change, &fileID, &handle);
202	if (result != B_OK)
203		return result;
204
205	*id = FileIdToInoT(fileID);
206
207	result = ChildAdded(name, fileID, handle);
208	if (result != B_OK)
209		return result;
210
211	fCache->Lock();
212	if (!fCache->Valid()) {
213		fCache->Reset();
214		fCache->SetChangeInfo(change);
215	} else
216		fCache->ValidateChangeInfo(change);
217
218	fCache->AddEntry(name, *id);
219	fCache->Unlock();
220
221	return B_OK;
222}
223
224
225status_t
226Inode::Link(Inode* dir, const char* name)
227{
228	ASSERT(dir != NULL);
229	ASSERT(name != NULL);
230
231	ChangeInfo changeInfo;
232	status_t result = NFS4Inode::Link(dir, name, &changeInfo);
233	if (result != B_OK)
234		return result;
235
236	fFileSystem->Root()->MakeInfoInvalid();
237	fInfo.fNames->AddName(dir->fInfo.fNames, name);
238
239	dir->fCache->Lock();
240	if (dir->fCache->Valid()) {
241		if (changeInfo.fAtomic
242			&& dir->fCache->ChangeInfo() == changeInfo.fBefore) {
243			dir->fCache->AddEntry(name, fInfo.fFileId, true);
244			dir->fCache->SetChangeInfo(changeInfo.fAfter);
245		} else
246			dir->fCache->Trash();
247	}
248	dir->fCache->Unlock();
249
250	notify_entry_created(fFileSystem->DevId(), dir->ID(), name, ID());
251
252	return B_OK;
253}
254
255
256status_t
257Inode::Remove(const char* name, FileType type, ino_t* id)
258{
259	ASSERT(name != NULL);
260
261	MemoryDeleter nameDeleter;
262	if (type == NF4NAMEDATTR) {
263		status_t result = LoadAttrDirHandle();
264		if (result != B_OK)
265			return result;
266
267		name = AttrToFileName(name);
268		if (name == NULL)
269			return B_NO_MEMORY;
270		nameDeleter.SetTo(const_cast<char*>(name));
271	}
272
273	ChangeInfo changeInfo;
274	uint64 fileID;
275	status_t result = NFS4Inode::RemoveObject(name, type, &changeInfo, &fileID);
276	if (result != B_OK)
277		return result;
278
279	DirectoryCache* cache = type != NF4NAMEDATTR ? fCache : fAttrCache;
280	cache->Lock();
281	if (cache->Valid()) {
282		if (changeInfo.fAtomic
283			&& fCache->ChangeInfo() == changeInfo.fBefore) {
284			cache->RemoveEntry(name);
285			cache->SetChangeInfo(changeInfo.fAfter);
286		} else if (cache->ChangeInfo() != changeInfo.fBefore)
287			cache->Trash();
288	}
289	cache->Unlock();
290
291	fFileSystem->Root()->MakeInfoInvalid();
292	if (id != NULL)
293		*id = FileIdToInoT(fileID);
294
295	if (type == NF4NAMEDATTR) {
296		notify_attribute_changed(fFileSystem->DevId(), -1, ID(), name,
297			B_ATTR_REMOVED);
298	} else {
299		notify_entry_removed(fFileSystem->DevId(), ID(), name,
300			FileIdToInoT(fileID));
301	}
302
303	return B_OK;
304}
305
306
307status_t
308Inode::Rename(Inode* from, Inode* to, const char* fromName, const char* toName,
309	bool attribute, ino_t* id, ino_t* oldID)
310{
311	ASSERT(from != NULL);
312	ASSERT(fromName != NULL);
313	ASSERT(to != NULL);
314	ASSERT(toName != NULL);
315
316	if (from->fFileSystem != to->fFileSystem)
317		return B_DONT_DO_THAT;
318
319	MemoryDeleter fromNameDeleter;
320	MemoryDeleter toNameDeleter;
321	if (attribute) {
322		status_t result = from->LoadAttrDirHandle();
323		if (result != B_OK)
324			return result;
325
326		result = to->LoadAttrDirHandle();
327		if (result != B_OK)
328			return result;
329
330		fromName = from->AttrToFileName(fromName);
331		toName = to->AttrToFileName(toName);
332
333		fromNameDeleter.SetTo(const_cast<char*>(fromName));
334		toNameDeleter.SetTo(const_cast<char*>(toName));
335		if (fromName == NULL || toName == NULL)
336			return B_NO_MEMORY;
337	}
338
339	uint64 oldFileID = 0;
340	if (!attribute)
341		to->NFS4Inode::LookUp(toName, NULL, &oldFileID, NULL);
342
343	uint64 fileID;
344	ChangeInfo fromChange, toChange;
345	status_t result = NFS4Inode::RenameNode(from, to, fromName, toName,
346		&fromChange, &toChange, &fileID, attribute);
347	if (result != B_OK)
348		return result;
349
350	from->fFileSystem->Root()->MakeInfoInvalid();
351
352	if (id != NULL)
353		*id = FileIdToInoT(fileID);
354	if (oldID != NULL)
355		*oldID = FileIdToInoT(oldFileID);
356
357	DirectoryCache* cache = attribute ? from->fAttrCache : from->fCache;
358	cache->Lock();
359	if (cache->Valid()) {
360		if (fromChange.fAtomic && cache->ChangeInfo() == fromChange.fBefore) {
361			cache->RemoveEntry(fromName);
362			if (to == from)
363				cache->AddEntry(toName, fileID, true);
364			cache->SetChangeInfo(fromChange.fAfter);
365		} else
366			cache->Trash();
367	}
368	cache->Unlock();
369
370	if (to != from) {
371		cache = attribute ? to->fAttrCache : to->fCache;
372		cache->Lock();
373		if (cache->Valid()) {
374			if (toChange.fAtomic
375				&& (cache->ChangeInfo() == toChange.fBefore)) {
376				cache->AddEntry(toName, fileID, true);
377				cache->SetChangeInfo(toChange.fAfter);
378			} else
379				cache->Trash();
380		}
381		cache->Unlock();
382	}
383
384	if (attribute) {
385		notify_attribute_changed(from->fFileSystem->DevId(), -1, from->ID(),
386			fromName, B_ATTR_REMOVED);
387		notify_attribute_changed(to->fFileSystem->DevId(), -1, to->ID(), toName,
388			B_ATTR_CREATED);
389	} else {
390		notify_entry_moved(from->fFileSystem->DevId(), from->ID(), fromName,
391			to->ID(), toName, FileIdToInoT(fileID));
392	}
393
394	return B_OK;
395}
396
397
398status_t
399Inode::CreateLink(const char* name, const char* path, int mode, ino_t* id)
400{
401	return CreateObject(name, path, mode, NF4LNK, id);
402}
403
404
405status_t
406Inode::CreateObject(const char* name, const char* path, int mode, FileType type,
407	ino_t* id)
408{
409	ASSERT(name != NULL);
410	ASSERT(type != NF4LNK || path != NULL);
411
412	ChangeInfo changeInfo;
413	uint64 fileID;
414	FileHandle handle;
415
416	status_t result = NFS4Inode::CreateObject(name, path, mode, type,
417		&changeInfo, &fileID, &handle);
418	if (result != B_OK)
419		return result;
420
421	fFileSystem->Root()->MakeInfoInvalid();
422
423	result = ChildAdded(name, fileID, handle);
424	if (result != B_OK)
425		return result;
426
427	fCache->Lock();
428	if (fCache->Valid()) {
429		if (changeInfo.fAtomic && fCache->ChangeInfo() == changeInfo.fBefore) {
430			fCache->AddEntry(name, fileID, true);
431			fCache->SetChangeInfo(changeInfo.fAfter);
432		} else
433			fCache->Trash();
434	}
435	fCache->Unlock();
436
437	notify_entry_created(fFileSystem->DevId(), ID(), name,
438		FileIdToInoT(fileID));
439
440	*id = FileIdToInoT(fileID);
441	return B_OK;
442}
443
444
445status_t
446Inode::Access(int mode)
447{
448	int acc = 0;
449
450	uint32 allowed;
451	bool cache = fFileSystem->GetConfiguration().fCacheMetadata;
452	status_t result = fMetaCache.GetAccess(geteuid(), &allowed);
453	if (result != B_OK || !cache) {
454		result = NFS4Inode::Access(&allowed);
455		if (result != B_OK)
456			return result;
457		fMetaCache.SetAccess(geteuid(), allowed);
458	}
459
460	if ((allowed & ACCESS4_READ) != 0)
461		acc |= R_OK;
462
463	if ((allowed & ACCESS4_LOOKUP) != 0)
464		acc |= X_OK | R_OK;
465
466	if ((allowed & ACCESS4_EXECUTE) != 0)
467		acc |= X_OK;
468
469	if ((allowed & ACCESS4_MODIFY) != 0)
470		acc |= W_OK;
471
472	if ((mode & acc) != mode)
473		return B_NOT_ALLOWED;
474
475	return B_OK;
476}
477
478
479status_t
480Inode::Stat(struct stat* st, OpenAttrCookie* attr)
481{
482	ASSERT(st != NULL);
483
484	if (attr != NULL)
485		return GetStat(st, attr);
486
487	bool cache = fFileSystem->GetConfiguration().fCacheMetadata;
488	if (!cache)
489		return GetStat(st, NULL);
490
491	status_t result = fMetaCache.GetStat(st);
492	if (result != B_OK) {
493		struct stat temp;
494		result = GetStat(&temp);
495		if (result != B_OK)
496			return result;
497		fMetaCache.SetStat(temp);
498		fMetaCache.GetStat(st);
499	}
500
501	return B_OK;
502}
503
504
505status_t
506Inode::GetStat(struct stat* st, OpenAttrCookie* attr)
507{
508	ASSERT(st != NULL);
509
510	AttrValue* values;
511	uint32 count;
512	status_t result = NFS4Inode::GetStat(&values, &count, attr);
513	if (result != B_OK)
514		return result;
515
516	// FATTR4_SIZE is mandatory
517	if (count < 1 || values[0].fAttribute != FATTR4_SIZE) {
518		delete[] values;
519		return B_BAD_VALUE;
520	}
521	st->st_size = values[0].fData.fValue64;
522
523	uint32 next = 1;
524	st->st_mode = Type();
525	if (count >= next && values[next].fAttribute == FATTR4_MODE) {
526		st->st_mode |= values[next].fData.fValue32;
527		next++;
528	} else
529		st->st_mode = 777;
530
531	if (count >= next && values[next].fAttribute == FATTR4_NUMLINKS) {
532		st->st_nlink = values[next].fData.fValue32;
533		next++;
534	} else
535		st->st_nlink = 1;
536
537	if (count >= next && values[next].fAttribute == FATTR4_OWNER) {
538		char* owner = reinterpret_cast<char*>(values[next].fData.fPointer);
539		if (owner != NULL && isdigit(owner[0]))
540			st->st_uid = atoi(owner);
541		else
542			st->st_uid = gIdMapper->GetUserId(owner);
543		next++;
544	} else
545		st->st_uid = 0;
546
547	if (count >= next && values[next].fAttribute == FATTR4_OWNER_GROUP) {
548		char* group = reinterpret_cast<char*>(values[next].fData.fPointer);
549		if (group != NULL && isdigit(group[0]))
550			st->st_gid = atoi(group);
551		else
552			st->st_gid = gIdMapper->GetGroupId(group);
553		next++;
554	} else
555		st->st_gid = 0;
556
557	if (count >= next && values[next].fAttribute == FATTR4_TIME_ACCESS) {
558		memcpy(&st->st_atim, values[next].fData.fPointer,
559			sizeof(timespec));
560		next++;
561	} else
562		memset(&st->st_atim, 0, sizeof(timespec));
563
564	if (count >= next && values[next].fAttribute == FATTR4_TIME_CREATE) {
565		memcpy(&st->st_crtim, values[next].fData.fPointer,
566			sizeof(timespec));
567		next++;
568	} else
569		memset(&st->st_crtim, 0, sizeof(timespec));
570
571	if (count >= next && values[next].fAttribute == FATTR4_TIME_METADATA) {
572		memcpy(&st->st_ctim, values[next].fData.fPointer,
573			sizeof(timespec));
574		next++;
575	} else
576		memset(&st->st_ctim, 0, sizeof(timespec));
577
578	if (count >= next && values[next].fAttribute == FATTR4_TIME_MODIFY) {
579		memcpy(&st->st_mtim, values[next].fData.fPointer,
580			sizeof(timespec));
581		next++;
582	} else
583		memset(&st->st_mtim, 0, sizeof(timespec));
584	delete[] values;
585
586	st->st_blksize = fFileSystem->Root()->IOSize();
587	st->st_blocks = st->st_size / st->st_blksize;
588	st->st_blocks += st->st_size % st->st_blksize == 0 ? 0 : 1;
589
590	return B_OK;
591}
592
593
594status_t
595Inode::WriteStat(const struct stat* st, uint32 mask, OpenAttrCookie* cookie)
596{
597	ASSERT(st != NULL);
598
599	status_t result;
600	AttrValue attr[6];
601	uint32 i = 0;
602
603	if ((mask & B_STAT_SIZE) != 0) {
604		fMaxFileSize = st->st_size;
605		file_cache_set_size(fFileCache, st->st_size);
606
607		attr[i].fAttribute = FATTR4_SIZE;
608		attr[i].fFreePointer = false;
609		attr[i].fData.fValue64 = st->st_size;
610		i++;
611	}
612
613	if ((mask & B_STAT_MODE) != 0) {
614		attr[i].fAttribute = FATTR4_MODE;
615		attr[i].fFreePointer = false;
616		attr[i].fData.fValue32 = st->st_mode;
617		i++;
618	}
619
620	if ((mask & B_STAT_UID) != 0) {
621		attr[i].fAttribute = FATTR4_OWNER;
622		attr[i].fFreePointer = true;
623		attr[i].fData.fPointer = gIdMapper->GetOwner(st->st_uid);
624		i++;
625	}
626
627	if ((mask & B_STAT_GID) != 0) {
628		attr[i].fAttribute = FATTR4_OWNER_GROUP;
629		attr[i].fFreePointer = true;
630		attr[i].fData.fPointer = gIdMapper->GetOwnerGroup(st->st_gid);
631		i++;
632	}
633
634	if ((mask & B_STAT_ACCESS_TIME) != 0) {
635		attr[i].fAttribute = FATTR4_TIME_ACCESS_SET;
636		attr[i].fFreePointer = true;
637		attr[i].fData.fPointer = malloc(sizeof(st->st_atim));
638		memcpy(attr[i].fData.fPointer, &st->st_atim, sizeof(st->st_atim));
639		i++;
640	}
641
642	if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
643		attr[i].fAttribute = FATTR4_TIME_MODIFY_SET;
644		attr[i].fFreePointer = true;
645		attr[i].fData.fPointer = malloc(sizeof(st->st_mtim));
646		memcpy(attr[i].fData.fPointer, &st->st_mtim, sizeof(st->st_mtim));
647		i++;
648	}
649
650	if (cookie == NULL) {
651		MutexLocker stateLocker(fStateLock);
652		ASSERT(fOpenState != NULL || !(mask & B_STAT_SIZE));
653		result = NFS4Inode::WriteStat(fOpenState, attr, i);
654	} else
655		result = NFS4Inode::WriteStat(cookie->fOpenState, attr, i);
656
657	fMetaCache.InvalidateStat();
658
659	const uint32 kAccessMask = B_STAT_MODE | B_STAT_UID | B_STAT_GID;
660	if ((mask & kAccessMask) != 0)
661		fMetaCache.InvalidateAccess();
662
663	return result;
664}
665
666
667inline status_t
668Inode::CheckLockType(short ltype, uint32 mode)
669{
670	switch (ltype) {
671		case F_UNLCK:
672			return B_OK;
673
674		case F_RDLCK:
675			if ((mode & O_RDONLY) == 0 && (mode & O_RDWR) == 0)
676				return EBADF;
677			return B_OK;
678
679		case F_WRLCK:
680			if ((mode & O_WRONLY) == 0 && (mode & O_RDWR) == 0)
681				return EBADF;
682			return B_OK;
683
684		default:
685			return B_BAD_VALUE;
686	}
687}
688
689
690status_t
691Inode::TestLock(OpenFileCookie* cookie, struct flock* lock)
692{
693	ASSERT(cookie != NULL);
694	ASSERT(lock != NULL);
695
696	if (lock->l_type == F_UNLCK)
697		return B_OK;
698
699	status_t result = CheckLockType(lock->l_type, cookie->fMode);
700	if (result != B_OK)
701		return result;
702
703	LockType ltype = sGetLockType(lock->l_type, false);
704	uint64 position = lock->l_start;
705	uint64 length;
706	if (lock->l_len + lock->l_start == OFF_MAX)
707		length = UINT64_MAX;
708	else
709		length = lock->l_len;
710
711	bool conflict;
712	result = NFS4Inode::TestLock(cookie, &ltype, &position, &length, conflict);
713	if (result != B_OK)
714		return result;
715
716	if (conflict) {
717		lock->l_type = sLockTypeToHaiku(ltype);
718		lock->l_start = static_cast<off_t>(position);
719		if (length >= OFF_MAX)
720			lock->l_len = OFF_MAX;
721		else
722			lock->l_len = static_cast<off_t>(length);
723	} else
724		lock->l_type = F_UNLCK;
725
726	return B_OK;
727}
728
729
730status_t
731Inode::AcquireLock(OpenFileCookie* cookie, const struct flock* lock,
732	bool wait)
733{
734	ASSERT(cookie != NULL);
735	ASSERT(lock != NULL);
736
737	OpenState* state = cookie->fOpenState;
738
739	status_t result = CheckLockType(lock->l_type, cookie->fMode);
740	if (result != B_OK)
741		return result;
742
743	thread_info info;
744	get_thread_info(find_thread(NULL), &info);
745
746	MutexLocker locker(state->fOwnerLock);
747	LockOwner* owner = state->GetLockOwner(info.team);
748	if (owner == NULL)
749		return B_NO_MEMORY;
750
751	LockInfo* linfo = new(std::nothrow) LockInfo(owner);
752	if (linfo == NULL)
753		return B_NO_MEMORY;
754	locker.Unlock();
755
756	linfo->fStart = lock->l_start;
757	if (lock->l_len + lock->l_start == OFF_MAX)
758		linfo->fLength = UINT64_MAX;
759	else
760		linfo->fLength = lock->l_len;
761	linfo->fType = sGetLockType(lock->l_type, wait);
762
763	result = NFS4Inode::AcquireLock(cookie, linfo, wait);
764	if (result != B_OK) {
765		delete linfo;
766		return result;
767	}
768
769	MutexLocker _(state->fLocksLock);
770	state->AddLock(linfo);
771	cookie->AddLock(linfo);
772
773	return B_OK;
774}
775
776
777status_t
778Inode::ReleaseLock(OpenFileCookie* cookie, const struct flock* lock)
779{
780	ASSERT(cookie != NULL);
781	ASSERT(lock != NULL);
782
783	SyncAndCommit();
784
785	LockInfo* prev = NULL;
786
787	thread_info info;
788	get_thread_info(find_thread(NULL), &info);
789	uint32 owner = info.team;
790
791	OpenState* state = cookie->fOpenState;
792	MutexLocker locker(state->fLocksLock);
793	LockInfo* linfo = state->fLocks;
794	while (linfo != NULL) {
795		if (linfo->fOwner->fOwner == owner && *linfo == *lock) {
796			state->RemoveLock(linfo, prev);
797			break;
798		}
799
800		prev = linfo;
801		linfo = linfo->fNext;
802	}
803
804	prev = NULL;
805	linfo = cookie->fLocks;
806	while (linfo != NULL) {
807		if (linfo->fOwner->fOwner == owner && *linfo == *lock) {
808			cookie->RemoveLock(linfo, prev);
809			break;
810		}
811
812		prev = linfo;
813		linfo = linfo->fCookieNext;
814	}
815	locker.Unlock();
816
817	if (linfo == NULL)
818		return B_BAD_VALUE;
819
820	status_t result = NFS4Inode::ReleaseLock(cookie, linfo);
821	if (result != B_OK)
822		return result;
823
824	state->DeleteLock(linfo);
825
826	return B_OK;
827}
828
829
830status_t
831Inode::ReleaseAllLocks(OpenFileCookie* cookie)
832{
833	ASSERT(cookie != NULL);
834
835	if (cookie->fLocks)
836		SyncAndCommit();
837
838	OpenState* state = cookie->fOpenState;
839	MutexLocker _(state->fLocksLock);
840	LockInfo* linfo = cookie->fLocks;
841	while (linfo != NULL) {
842		cookie->RemoveLock(linfo, NULL);
843
844		LockInfo* prev = NULL;
845		LockInfo* stateLock = state->fLocks;
846		while (stateLock != NULL) {
847			if (*linfo == *stateLock) {
848				state->RemoveLock(stateLock, prev);
849				break;
850			}
851
852			prev = stateLock;
853			stateLock = stateLock->fNext;
854		}
855
856		NFS4Inode::ReleaseLock(cookie, linfo);
857		state->DeleteLock(linfo);
858
859		linfo = cookie->fLocks;
860	}
861
862	return B_OK;
863}
864
865
866status_t
867Inode::ChildAdded(const char* name, uint64 fileID,
868	const FileHandle& fileHandle)
869{
870	ASSERT(name != NULL);
871
872	fFileSystem->Root()->MakeInfoInvalid();
873
874	FileInfo fi;
875	fi.fFileId = fileID;
876	fi.fHandle = fileHandle;
877
878	return fFileSystem->InoIdMap()->AddName(fi, fInfo.fNames, name,
879		FileIdToInoT(fileID));
880}
881
882
883const char*
884Inode::Name() const
885{
886	ASSERT(fInfo.fNames->fNames.Head() != NULL);
887	return fInfo.fNames->fNames.Head()->fName;
888}
889
890
891void
892Inode::SetDelegation(Delegation* delegation)
893{
894	ASSERT(delegation != NULL);
895
896	WriteLocker _(fDelegationLock);
897
898	fMetaCache.InvalidateStat();
899	struct stat st;
900	Stat(&st);
901	fMetaCache.LockValid();
902
903	fDelegation = delegation;
904	fOpenState->AcquireReference();
905	fOpenState->fDelegation = delegation;
906	fFileSystem->AddDelegation(delegation);
907}
908
909
910void
911Inode::RecallDelegation(bool truncate)
912{
913	WriteLocker _(fDelegationLock);
914	if (fDelegation == NULL)
915		return;
916	ReturnDelegation(truncate);
917}
918
919
920void
921Inode::RecallReadDelegation()
922{
923	WriteLocker _(fDelegationLock);
924	if (fDelegation == NULL || fDelegation->Type() != OPEN_DELEGATE_READ)
925		return;
926	ReturnDelegation(false);
927}
928
929
930void
931Inode::ReturnDelegation(bool truncate)
932{
933	ASSERT(fDelegation != NULL);
934
935	fDelegation->GiveUp(truncate);
936
937	fMetaCache.UnlockValid();
938	fFileSystem->RemoveDelegation(fDelegation);
939
940	MutexLocker stateLocker(fStateLock);
941	fOpenState->fDelegation = NULL;
942	ReleaseOpenState();
943
944	delete fDelegation;
945	fDelegation = NULL;
946}
947
948
949void
950Inode::ReleaseOpenState()
951{
952	ASSERT(fOpenState != NULL);
953
954	if (fOpenState->ReleaseReference() == 1) {
955		ASSERT(fAIOCount == 0);
956		fOpenState = NULL;
957	}
958}
959
960
961status_t
962Inode::SyncAndCommit(bool force)
963{
964	if (!force && fDelegation != NULL)
965		return B_OK;
966
967	file_cache_sync(fFileCache);
968	WaitAIOComplete();
969	return Commit();
970}
971
972
973void
974Inode::BeginAIOOp()
975{
976	MutexLocker _(fAIOLock);
977	fAIOCount++;
978	if (fAIOCount == 1)
979		acquire_sem(fAIOWait);
980}
981
982
983void
984Inode::EndAIOOp()
985{
986	MutexLocker _(fAIOLock);
987	ASSERT(fAIOCount > 0);
988	fAIOCount--;
989	if (fAIOCount == 0)
990		release_sem(fAIOWait);
991}
992
993