xref: /haiku/src/add-ons/kernel/file_systems/nfs4/Inode.cpp (revision e5d65858f2361fe0552495b61620c84dcee6bc00)
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 "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 
24 Inode::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 
45 status_t
46 Inode::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 
137 Inode::~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 
159 status_t
160 Inode::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 
174 	SyncAndCommit(true);
175 	file_cache_delete(fFileCache);
176 
177 	struct stat st;
178 	fMetaCache.InvalidateStat();
179 	result = Stat(&st);
180 	if (result != B_OK)
181 		return result;
182 
183 	fFileCache = file_cache_create(fFileSystem->DevId(), ID(), st.st_size);
184 
185 	change = fChange;
186 	return B_OK;
187 }
188 
189 
190 status_t
191 Inode::LookUp(const char* name, ino_t* id)
192 {
193 	ASSERT(name != NULL);
194 	ASSERT(id != NULL);
195 
196 	if (fType != NF4DIR)
197 		return B_NOT_A_DIRECTORY;
198 
199 	uint64 change;
200 	uint64 fileID;
201 	FileHandle handle;
202 	status_t result = NFS4Inode::LookUp(name, &change, &fileID, &handle);
203 	if (result != B_OK)
204 		return result;
205 
206 	*id = FileIdToInoT(fileID);
207 
208 	result = ChildAdded(name, fileID, handle);
209 	if (result != B_OK)
210 		return result;
211 
212 	fCache->Lock();
213 	if (!fCache->Valid()) {
214 		fCache->Reset();
215 		fCache->SetChangeInfo(change);
216 	} else
217 		fCache->ValidateChangeInfo(change);
218 
219 	fCache->AddEntry(name, *id);
220 	fCache->Unlock();
221 
222 	return B_OK;
223 }
224 
225 
226 status_t
227 Inode::Link(Inode* dir, const char* name)
228 {
229 	ASSERT(dir != NULL);
230 	ASSERT(name != NULL);
231 
232 	ChangeInfo changeInfo;
233 	status_t result = NFS4Inode::Link(dir, name, &changeInfo);
234 	if (result != B_OK)
235 		return result;
236 
237 	fFileSystem->Root()->MakeInfoInvalid();
238 	fInfo.fNames->AddName(dir->fInfo.fNames, name);
239 
240 	dir->fCache->Lock();
241 	if (dir->fCache->Valid()) {
242 		if (changeInfo.fAtomic
243 			&& dir->fCache->ChangeInfo() == changeInfo.fBefore) {
244 			dir->fCache->AddEntry(name, fInfo.fFileId, true);
245 			dir->fCache->SetChangeInfo(changeInfo.fAfter);
246 		} else
247 			dir->fCache->Trash();
248 	}
249 	dir->fCache->Unlock();
250 
251 	notify_entry_created(fFileSystem->DevId(), dir->ID(), name, ID());
252 
253 	return B_OK;
254 }
255 
256 
257 status_t
258 Inode::Remove(const char* name, FileType type, ino_t* id)
259 {
260 	ASSERT(name != NULL);
261 
262 	MemoryDeleter nameDeleter;
263 	if (type == NF4NAMEDATTR) {
264 		status_t result = LoadAttrDirHandle();
265 		if (result != B_OK)
266 			return result;
267 
268 		name = AttrToFileName(name);
269 		if (name == NULL)
270 			return B_NO_MEMORY;
271 		nameDeleter.SetTo(const_cast<char*>(name));
272 	}
273 
274 	ChangeInfo changeInfo;
275 	uint64 fileID;
276 	status_t result = NFS4Inode::RemoveObject(name, type, &changeInfo, &fileID);
277 	if (result != B_OK)
278 		return result;
279 
280 	DirectoryCache* cache = type != NF4NAMEDATTR ? fCache : fAttrCache;
281 	cache->Lock();
282 	if (cache->Valid()) {
283 		if (changeInfo.fAtomic
284 			&& fCache->ChangeInfo() == changeInfo.fBefore) {
285 			cache->RemoveEntry(name);
286 			cache->SetChangeInfo(changeInfo.fAfter);
287 		} else if (cache->ChangeInfo() != changeInfo.fBefore)
288 			cache->Trash();
289 	}
290 	cache->Unlock();
291 
292 	fFileSystem->Root()->MakeInfoInvalid();
293 	if (id != NULL)
294 		*id = FileIdToInoT(fileID);
295 
296 	if (type == NF4NAMEDATTR) {
297 		notify_attribute_changed(fFileSystem->DevId(), ID(), name,
298 			B_ATTR_REMOVED);
299 	} else {
300 		notify_entry_removed(fFileSystem->DevId(), ID(), name,
301 			FileIdToInoT(fileID));
302 	}
303 
304 	return B_OK;
305 }
306 
307 
308 status_t
309 Inode::Rename(Inode* from, Inode* to, const char* fromName, const char* toName,
310 	bool attribute, ino_t* id, ino_t* oldID)
311 {
312 	ASSERT(from != NULL);
313 	ASSERT(fromName != NULL);
314 	ASSERT(to != NULL);
315 	ASSERT(toName != NULL);
316 
317 	if (from->fFileSystem != to->fFileSystem)
318 		return B_DONT_DO_THAT;
319 
320 	MemoryDeleter fromNameDeleter;
321 	MemoryDeleter toNameDeleter;
322 	if (attribute) {
323 		status_t result = from->LoadAttrDirHandle();
324 		if (result != B_OK)
325 			return result;
326 
327 		result = to->LoadAttrDirHandle();
328 		if (result != B_OK)
329 			return result;
330 
331 		fromName = from->AttrToFileName(fromName);
332 		toName = to->AttrToFileName(toName);
333 
334 		fromNameDeleter.SetTo(const_cast<char*>(fromName));
335 		toNameDeleter.SetTo(const_cast<char*>(toName));
336 		if (fromName == NULL || toName == NULL)
337 			return B_NO_MEMORY;
338 	}
339 
340 	uint64 oldFileID = 0;
341 	if (!attribute)
342 		to->NFS4Inode::LookUp(toName, NULL, &oldFileID, NULL);
343 
344 	uint64 fileID;
345 	ChangeInfo fromChange, toChange;
346 	status_t result = NFS4Inode::RenameNode(from, to, fromName, toName,
347 		&fromChange, &toChange, &fileID, attribute);
348 	if (result != B_OK)
349 		return result;
350 
351 	from->fFileSystem->Root()->MakeInfoInvalid();
352 
353 	if (id != NULL)
354 		*id = FileIdToInoT(fileID);
355 	if (oldID != NULL)
356 		*oldID = FileIdToInoT(oldFileID);
357 
358 	DirectoryCache* cache = attribute ? from->fAttrCache : from->fCache;
359 	cache->Lock();
360 	if (cache->Valid()) {
361 		if (fromChange.fAtomic && cache->ChangeInfo() == fromChange.fBefore) {
362 			cache->RemoveEntry(fromName);
363 			if (to == from)
364 				cache->AddEntry(toName, fileID, true);
365 			cache->SetChangeInfo(fromChange.fAfter);
366 		} else
367 			cache->Trash();
368 	}
369 	cache->Unlock();
370 
371 	if (to != from) {
372 		cache = attribute ? to->fAttrCache : to->fCache;
373 		cache->Lock();
374 		if (cache->Valid()) {
375 			if (toChange.fAtomic
376 				&& (cache->ChangeInfo() == toChange.fBefore)) {
377 				cache->AddEntry(toName, fileID, true);
378 				cache->SetChangeInfo(toChange.fAfter);
379 			} else
380 				cache->Trash();
381 		}
382 		cache->Unlock();
383 	}
384 
385 	if (attribute) {
386 		notify_attribute_changed(from->fFileSystem->DevId(), from->ID(),
387 			fromName, B_ATTR_REMOVED);
388 		notify_attribute_changed(to->fFileSystem->DevId(), to->ID(), toName,
389 			B_ATTR_CREATED);
390 	} else {
391 		notify_entry_moved(from->fFileSystem->DevId(), from->ID(), fromName,
392 			to->ID(), toName, FileIdToInoT(fileID));
393 	}
394 
395 	return B_OK;
396 }
397 
398 
399 status_t
400 Inode::CreateLink(const char* name, const char* path, int mode, ino_t* id)
401 {
402 	return CreateObject(name, path, mode, NF4LNK, id);
403 }
404 
405 
406 status_t
407 Inode::CreateObject(const char* name, const char* path, int mode, FileType type,
408 	ino_t* id)
409 {
410 	ASSERT(name != NULL);
411 	ASSERT(type != NF4LNK || path != NULL);
412 
413 	ChangeInfo changeInfo;
414 	uint64 fileID;
415 	FileHandle handle;
416 
417 	status_t result = NFS4Inode::CreateObject(name, path, mode, type,
418 		&changeInfo, &fileID, &handle);
419 	if (result != B_OK)
420 		return result;
421 
422 	fFileSystem->Root()->MakeInfoInvalid();
423 
424 	result = ChildAdded(name, fileID, handle);
425 	if (result != B_OK)
426 		return result;
427 
428 	fCache->Lock();
429 	if (fCache->Valid()) {
430 		if (changeInfo.fAtomic && fCache->ChangeInfo() == changeInfo.fBefore) {
431 			fCache->AddEntry(name, fileID, true);
432 			fCache->SetChangeInfo(changeInfo.fAfter);
433 		} else
434 			fCache->Trash();
435 	}
436 	fCache->Unlock();
437 
438 	notify_entry_created(fFileSystem->DevId(), ID(), name,
439 		FileIdToInoT(fileID));
440 
441 	*id = FileIdToInoT(fileID);
442 	return B_OK;
443 }
444 
445 
446 status_t
447 Inode::Access(int mode)
448 {
449 	int acc = 0;
450 
451 	uint32 allowed;
452 	bool cache = fFileSystem->GetConfiguration().fCacheMetadata;
453 	status_t result = fMetaCache.GetAccess(geteuid(), &allowed);
454 	if (result != B_OK || !cache) {
455 		result = NFS4Inode::Access(&allowed);
456 		if (result != B_OK)
457 			return result;
458 		fMetaCache.SetAccess(geteuid(), allowed);
459 	}
460 
461 	if ((allowed & ACCESS4_READ) != 0)
462 		acc |= R_OK;
463 
464 	if ((allowed & ACCESS4_LOOKUP) != 0)
465 		acc |= X_OK | R_OK;
466 
467 	if ((allowed & ACCESS4_EXECUTE) != 0)
468 		acc |= X_OK;
469 
470 	if ((allowed & ACCESS4_MODIFY) != 0)
471 		acc |= W_OK;
472 
473 	if ((mode & acc) != mode)
474 		return B_NOT_ALLOWED;
475 
476 	return B_OK;
477 }
478 
479 
480 status_t
481 Inode::Stat(struct stat* st, OpenAttrCookie* attr)
482 {
483 	ASSERT(st != NULL);
484 
485 	if (attr != NULL)
486 		return GetStat(st, attr);
487 
488 	bool cache = fFileSystem->GetConfiguration().fCacheMetadata;
489 	if (!cache)
490 		return GetStat(st, NULL);
491 
492 	status_t result = fMetaCache.GetStat(st);
493 	if (result != B_OK) {
494 		struct stat temp;
495 		result = GetStat(&temp);
496 		if (result != B_OK)
497 			return result;
498 		fMetaCache.SetStat(temp);
499 		fMetaCache.GetStat(st);
500 	}
501 
502 	return B_OK;
503 }
504 
505 
506 status_t
507 Inode::GetStat(struct stat* st, OpenAttrCookie* attr)
508 {
509 	ASSERT(st != NULL);
510 
511 	AttrValue* values;
512 	uint32 count;
513 	status_t result = NFS4Inode::GetStat(&values, &count, attr);
514 	if (result != B_OK)
515 		return result;
516 
517 	// FATTR4_SIZE is mandatory
518 	if (count < 1 || values[0].fAttribute != FATTR4_SIZE) {
519 		delete[] values;
520 		return B_BAD_VALUE;
521 	}
522 	st->st_size = values[0].fData.fValue64;
523 
524 	uint32 next = 1;
525 	st->st_mode = Type();
526 	if (count >= next && values[next].fAttribute == FATTR4_MODE) {
527 		st->st_mode |= values[next].fData.fValue32;
528 		next++;
529 	} else
530 		st->st_mode = 777;
531 
532 	if (count >= next && values[next].fAttribute == FATTR4_NUMLINKS) {
533 		st->st_nlink = values[next].fData.fValue32;
534 		next++;
535 	} else
536 		st->st_nlink = 1;
537 
538 	if (count >= next && values[next].fAttribute == FATTR4_OWNER) {
539 		char* owner = reinterpret_cast<char*>(values[next].fData.fPointer);
540 		if (owner != NULL && isdigit(owner[0]))
541 			st->st_uid = atoi(owner);
542 		else
543 			st->st_uid = gIdMapper->GetUserId(owner);
544 		next++;
545 	} else
546 		st->st_uid = 0;
547 
548 	if (count >= next && values[next].fAttribute == FATTR4_OWNER_GROUP) {
549 		char* group = reinterpret_cast<char*>(values[next].fData.fPointer);
550 		if (group != NULL && isdigit(group[0]))
551 			st->st_gid = atoi(group);
552 		else
553 			st->st_gid = gIdMapper->GetGroupId(group);
554 		next++;
555 	} else
556 		st->st_gid = 0;
557 
558 	if (count >= next && values[next].fAttribute == FATTR4_TIME_ACCESS) {
559 		memcpy(&st->st_atim, values[next].fData.fPointer,
560 			sizeof(timespec));
561 		next++;
562 	} else
563 		memset(&st->st_atim, 0, sizeof(timespec));
564 
565 	if (count >= next && values[next].fAttribute == FATTR4_TIME_CREATE) {
566 		memcpy(&st->st_crtim, values[next].fData.fPointer,
567 			sizeof(timespec));
568 		next++;
569 	} else
570 		memset(&st->st_crtim, 0, sizeof(timespec));
571 
572 	if (count >= next && values[next].fAttribute == FATTR4_TIME_METADATA) {
573 		memcpy(&st->st_ctim, values[next].fData.fPointer,
574 			sizeof(timespec));
575 		next++;
576 	} else
577 		memset(&st->st_ctim, 0, sizeof(timespec));
578 
579 	if (count >= next && values[next].fAttribute == FATTR4_TIME_MODIFY) {
580 		memcpy(&st->st_mtim, values[next].fData.fPointer,
581 			sizeof(timespec));
582 		next++;
583 	} else
584 		memset(&st->st_mtim, 0, sizeof(timespec));
585 	delete[] values;
586 
587 	st->st_blksize = fFileSystem->Root()->IOSize();
588 	st->st_blocks = st->st_size / st->st_blksize;
589 	st->st_blocks += st->st_size % st->st_blksize == 0 ? 0 : 1;
590 
591 	return B_OK;
592 }
593 
594 
595 status_t
596 Inode::WriteStat(const struct stat* st, uint32 mask, OpenAttrCookie* cookie)
597 {
598 	ASSERT(st != NULL);
599 
600 	status_t result;
601 	AttrValue attr[6];
602 	uint32 i = 0;
603 
604 	if ((mask & B_STAT_SIZE) != 0) {
605 		fMaxFileSize = st->st_size;
606 		file_cache_set_size(fFileCache, st->st_size);
607 
608 		attr[i].fAttribute = FATTR4_SIZE;
609 		attr[i].fFreePointer = false;
610 		attr[i].fData.fValue64 = st->st_size;
611 		i++;
612 	}
613 
614 	if ((mask & B_STAT_MODE) != 0) {
615 		attr[i].fAttribute = FATTR4_MODE;
616 		attr[i].fFreePointer = false;
617 		attr[i].fData.fValue32 = st->st_mode;
618 		i++;
619 	}
620 
621 	if ((mask & B_STAT_UID) != 0) {
622 		attr[i].fAttribute = FATTR4_OWNER;
623 		attr[i].fFreePointer = true;
624 		attr[i].fData.fPointer = gIdMapper->GetOwner(st->st_uid);
625 		i++;
626 	}
627 
628 	if ((mask & B_STAT_GID) != 0) {
629 		attr[i].fAttribute = FATTR4_OWNER_GROUP;
630 		attr[i].fFreePointer = true;
631 		attr[i].fData.fPointer = gIdMapper->GetOwnerGroup(st->st_gid);
632 		i++;
633 	}
634 
635 	if ((mask & B_STAT_ACCESS_TIME) != 0) {
636 		attr[i].fAttribute = FATTR4_TIME_ACCESS_SET;
637 		attr[i].fFreePointer = true;
638 		attr[i].fData.fPointer = malloc(sizeof(st->st_atim));
639 		memcpy(attr[i].fData.fPointer, &st->st_atim, sizeof(st->st_atim));
640 		i++;
641 	}
642 
643 	if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
644 		attr[i].fAttribute = FATTR4_TIME_MODIFY_SET;
645 		attr[i].fFreePointer = true;
646 		attr[i].fData.fPointer = malloc(sizeof(st->st_mtim));
647 		memcpy(attr[i].fData.fPointer, &st->st_mtim, sizeof(st->st_mtim));
648 		i++;
649 	}
650 
651 	if (cookie == NULL) {
652 		MutexLocker stateLocker(fStateLock);
653 		ASSERT(fOpenState != NULL || !(mask & B_STAT_SIZE));
654 		result = NFS4Inode::WriteStat(fOpenState, attr, i);
655 	} else
656 		result = NFS4Inode::WriteStat(cookie->fOpenState, attr, i);
657 
658 	fMetaCache.InvalidateStat();
659 
660 	const uint32 kAccessMask = B_STAT_MODE | B_STAT_UID | B_STAT_GID;
661 	if ((mask & kAccessMask) != 0)
662 		fMetaCache.InvalidateAccess();
663 
664 	return result;
665 }
666 
667 
668 inline status_t
669 Inode::CheckLockType(short ltype, uint32 mode)
670 {
671 	switch (ltype) {
672 		case F_UNLCK:
673 			return B_OK;
674 
675 		case F_RDLCK:
676 			if ((mode & O_RDONLY) == 0 && (mode & O_RDWR) == 0)
677 				return EBADF;
678 			return B_OK;
679 
680 		case F_WRLCK:
681 			if ((mode & O_WRONLY) == 0 && (mode & O_RDWR) == 0)
682 				return EBADF;
683 			return B_OK;
684 
685 		default:
686 			return B_BAD_VALUE;
687 	}
688 }
689 
690 
691 status_t
692 Inode::TestLock(OpenFileCookie* cookie, struct flock* lock)
693 {
694 	ASSERT(cookie != NULL);
695 	ASSERT(lock != NULL);
696 
697 	if (lock->l_type == F_UNLCK)
698 		return B_OK;
699 
700 	status_t result = CheckLockType(lock->l_type, cookie->fMode);
701 	if (result != B_OK)
702 		return result;
703 
704 	LockType ltype = sGetLockType(lock->l_type, false);
705 	uint64 position = lock->l_start;
706 	uint64 length;
707 	if (lock->l_len + lock->l_start == OFF_MAX)
708 		length = UINT64_MAX;
709 	else
710 		length = lock->l_len;
711 
712 	bool conflict;
713 	result = NFS4Inode::TestLock(cookie, &ltype, &position, &length, conflict);
714 	if (result != B_OK)
715 		return result;
716 
717 	if (conflict) {
718 		lock->l_type = sLockTypeToHaiku(ltype);
719 		lock->l_start = static_cast<off_t>(position);
720 		if (length >= OFF_MAX)
721 			lock->l_len = OFF_MAX;
722 		else
723 			lock->l_len = static_cast<off_t>(length);
724 	} else
725 		lock->l_type = F_UNLCK;
726 
727 	return B_OK;
728 }
729 
730 
731 status_t
732 Inode::AcquireLock(OpenFileCookie* cookie, const struct flock* lock,
733 	bool wait)
734 {
735 	ASSERT(cookie != NULL);
736 	ASSERT(lock != NULL);
737 
738 	OpenState* state = cookie->fOpenState;
739 
740 	status_t result = CheckLockType(lock->l_type, cookie->fMode);
741 	if (result != B_OK)
742 		return result;
743 
744 	thread_info info;
745 	get_thread_info(find_thread(NULL), &info);
746 
747 	MutexLocker locker(state->fOwnerLock);
748 	LockOwner* owner = state->GetLockOwner(info.team);
749 	if (owner == NULL)
750 		return B_NO_MEMORY;
751 
752 	LockInfo* linfo = new LockInfo(owner);
753 	if (linfo == NULL)
754 		return B_NO_MEMORY;
755 	locker.Unlock();
756 
757 	linfo->fStart = lock->l_start;
758 	if (lock->l_len + lock->l_start == OFF_MAX)
759 		linfo->fLength = UINT64_MAX;
760 	else
761 		linfo->fLength = lock->l_len;
762 	linfo->fType = sGetLockType(lock->l_type, wait);
763 
764 	result = NFS4Inode::AcquireLock(cookie, linfo, wait);
765 	if (result != B_OK) {
766 		delete linfo;
767 		return result;
768 	}
769 
770 	MutexLocker _(state->fLocksLock);
771 	state->AddLock(linfo);
772 	cookie->AddLock(linfo);
773 
774 	return B_OK;
775 }
776 
777 
778 status_t
779 Inode::ReleaseLock(OpenFileCookie* cookie, const struct flock* lock)
780 {
781 	ASSERT(cookie != NULL);
782 	ASSERT(lock != NULL);
783 
784 	SyncAndCommit();
785 
786 	LockInfo* prev = NULL;
787 
788 	thread_info info;
789 	get_thread_info(find_thread(NULL), &info);
790 	uint32 owner = info.team;
791 
792 	OpenState* state = cookie->fOpenState;
793 	MutexLocker locker(state->fLocksLock);
794 	LockInfo* linfo = state->fLocks;
795 	while (linfo != NULL) {
796 		if (linfo->fOwner->fOwner == owner && *linfo == *lock) {
797 			state->RemoveLock(linfo, prev);
798 			break;
799 		}
800 
801 		prev = linfo;
802 		linfo = linfo->fNext;
803 	}
804 
805 	prev = NULL;
806 	linfo = cookie->fLocks;
807 	while (linfo != NULL) {
808 		if (linfo->fOwner->fOwner == owner && *linfo == *lock) {
809 			cookie->RemoveLock(linfo, prev);
810 			break;
811 		}
812 
813 		prev = linfo;
814 		linfo = linfo->fCookieNext;
815 	}
816 	locker.Unlock();
817 
818 	if (linfo == NULL)
819 		return B_BAD_VALUE;
820 
821 	status_t result = NFS4Inode::ReleaseLock(cookie, linfo);
822 	if (result != B_OK)
823 		return result;
824 
825 	state->DeleteLock(linfo);
826 
827 	return B_OK;
828 }
829 
830 
831 status_t
832 Inode::ReleaseAllLocks(OpenFileCookie* cookie)
833 {
834 	ASSERT(cookie != NULL);
835 
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 
866 status_t
867 Inode::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 
883 const char*
884 Inode::Name() const
885 {
886 	ASSERT(fInfo.fNames->fNames.Head() != NULL);
887 	return fInfo.fNames->fNames.Head()->fName;
888 }
889 
890 
891 void
892 Inode::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 
910 void
911 Inode::RecallDelegation(bool truncate)
912 {
913 	WriteLocker _(fDelegationLock);
914 	if (fDelegation == NULL)
915 		return;
916 	ReturnDelegation(truncate);
917 }
918 
919 
920 void
921 Inode::RecallReadDelegation()
922 {
923 	WriteLocker _(fDelegationLock);
924 	if (fDelegation == NULL || fDelegation->Type() != OPEN_DELEGATE_READ)
925 		return;
926 	ReturnDelegation(false);
927 }
928 
929 
930 void
931 Inode::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 
949 void
950 Inode::ReleaseOpenState()
951 {
952 	ASSERT(fOpenState != NULL);
953 
954 	if (fOpenState->ReleaseReference() == 1) {
955 		ASSERT(fAIOCount == 0);
956 		fOpenState = NULL;
957 	}
958 }
959 
960 
961 status_t
962 Inode::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 
973 void
974 Inode::BeginAIOOp()
975 {
976 	MutexLocker _(fAIOLock);
977 	fAIOCount++;
978 	if (fAIOCount == 1)
979 		acquire_sem(fAIOWait);
980 }
981 
982 
983 void
984 Inode::EndAIOOp()
985 {
986 	MutexLocker _(fAIOLock);
987 	ASSERT(fAIOCount > 0);
988 	fAIOCount--;
989 	if (fAIOCount == 0)
990 		release_sem(fAIOWait);
991 }
992 
993