xref: /haiku/src/add-ons/kernel/file_systems/nfs4/Inode.cpp (revision 17889a8c70dbb3d59c1412f6431968753c767bab)
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 
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 	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 
189 status_t
190 Inode::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 
225 status_t
226 Inode::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 
256 status_t
257 Inode::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 
307 status_t
308 Inode::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 
398 status_t
399 Inode::CreateLink(const char* name, const char* path, int mode, ino_t* id)
400 {
401 	return CreateObject(name, path, mode, NF4LNK, id);
402 }
403 
404 
405 status_t
406 Inode::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 
445 status_t
446 Inode::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 
479 status_t
480 Inode::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 
505 status_t
506 Inode::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 
594 status_t
595 Inode::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 
667 inline status_t
668 Inode::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 
690 status_t
691 Inode::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 
730 status_t
731 Inode::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 
777 status_t
778 Inode::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 
830 status_t
831 Inode::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 
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