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 <string.h>
13
14#include <AutoDeleter.h>
15#include <fs_cache.h>
16#include <NodeMonitor.h>
17
18#include "IdMap.h"
19#include "Request.h"
20#include "RootInode.h"
21
22
23status_t
24Inode::CreateState(const char* name, int mode, int perms, OpenState* state,
25	OpenDelegationData* delegationData) {
26	ASSERT(name != NULL);
27	ASSERT(state != NULL);
28	ASSERT(delegationData != NULL);
29
30	uint64 fileID;
31	FileHandle handle;
32	ChangeInfo changeInfo;
33
34	status_t result = CreateFile(name, mode, perms, state, &changeInfo,
35		&fileID, &handle, delegationData);
36	if (result != B_OK)
37		return result;
38
39	FileInfo fileInfo;
40	fileInfo.fFileId = fileID;
41	fileInfo.fHandle = handle;
42
43	fFileSystem->InoIdMap()->AddName(fileInfo, fInfo.fNames, name,
44		FileIdToInoT(fileID));
45
46	fCache->Lock();
47	if (fCache->Valid()) {
48		if (changeInfo.fAtomic
49			&& fCache->ChangeInfo() == changeInfo.fBefore) {
50			fCache->AddEntry(name, fileID, true);
51			fCache->SetChangeInfo(changeInfo.fAfter);
52		} else
53			fCache->Trash();
54	}
55	fCache->Unlock();
56
57	state->fFileSystem = fFileSystem;
58	state->fInfo = fileInfo;
59	state->fMode = mode & O_RWMASK;
60
61	return B_OK;
62}
63
64
65status_t
66Inode::Create(const char* name, int mode, int perms, OpenFileCookie* cookie,
67	OpenDelegationData* data, ino_t* id)
68{
69	ASSERT(name != NULL);
70	ASSERT(cookie != NULL);
71	ASSERT(data != NULL);
72
73	cookie->fMode = mode;
74	cookie->fLocks = NULL;
75
76	OpenState* state = new(std::nothrow) OpenState;
77	if (state == NULL)
78		return B_NO_MEMORY;
79
80	status_t result = CreateState(name, mode, perms, state, data);
81	if (result != B_OK) {
82		delete state;
83		return result;
84	}
85
86	cookie->fOpenState = state;
87
88	*id = FileIdToInoT(state->fInfo.fFileId);
89
90	fFileSystem->AddOpenFile(state);
91	fFileSystem->Root()->MakeInfoInvalid();
92
93	notify_entry_created(fFileSystem->DevId(), ID(), name, *id);
94
95	return B_OK;
96}
97
98
99status_t
100Inode::Open(int mode, OpenFileCookie* cookie)
101{
102	ASSERT(cookie != NULL);
103
104	MutexLocker locker(fStateLock);
105
106	OpenDelegationData data;
107	data.fType = OPEN_DELEGATE_NONE;
108	if (fOpenState == NULL) {
109		OpenState* state = new(std::nothrow) OpenState;
110		if (state == NULL)
111			return B_NO_MEMORY;
112
113		state->fInfo = fInfo;
114		state->fFileSystem = fFileSystem;
115		state->fMode = mode & O_RWMASK;
116		status_t result = OpenFile(state, mode, &data);
117		if (result != B_OK) {
118			delete state;
119			return result;
120		}
121
122		fFileSystem->AddOpenFile(state);
123		fOpenState = state;
124		cookie->fOpenState = state;
125		locker.Unlock();
126
127		RevalidateFileCache();
128	} else {
129		fOpenState->AcquireReference();
130		cookie->fOpenState = fOpenState;
131		locker.Unlock();
132
133		int newMode = mode & O_RWMASK;
134		int oldMode = fOpenState->fMode & O_RWMASK;
135		if (oldMode != newMode && oldMode != O_RDWR) {
136			if (oldMode == O_RDONLY)
137				RecallReadDelegation();
138
139			status_t result = OpenFile(fOpenState, O_RDWR, &data);
140			if (result != B_OK) {
141				locker.Lock();
142				ReleaseOpenState();
143				return result;
144			}
145			fOpenState->fMode = O_RDWR;
146
147			if (oldMode == O_RDONLY)
148				RevalidateFileCache();
149		} else {
150			int newMode = mode & O_RWMASK;
151			uint32 allowed = 0;
152			if (newMode == O_RDWR || newMode == O_RDONLY)
153				allowed |= R_OK;
154			if (newMode == O_RDWR || newMode == O_WRONLY)
155				allowed |= W_OK;
156
157			status_t result = Access(allowed);
158			if (result != B_OK) {
159				locker.Lock();
160				ReleaseOpenState();
161				return result;
162			}
163		}
164	}
165
166	if ((mode & O_TRUNC) == O_TRUNC) {
167		struct stat st;
168		st.st_size = 0;
169		WriteStat(&st, B_STAT_SIZE);
170		file_cache_set_size(fFileCache, 0);
171	}
172
173	cookie->fMode = mode;
174	cookie->fLocks = NULL;
175
176	if (data.fType != OPEN_DELEGATE_NONE) {
177		Delegation* delegation
178			= new(std::nothrow) Delegation(data, this, fOpenState->fClientID);
179		if (delegation != NULL) {
180			delegation->fInfo = fOpenState->fInfo;
181			delegation->fFileSystem = fFileSystem;
182			SetDelegation(delegation);
183		}
184	}
185
186	return B_OK;
187}
188
189
190status_t
191Inode::Close(OpenFileCookie* cookie)
192{
193	ASSERT(cookie != NULL);
194	ASSERT(fOpenState == cookie->fOpenState);
195
196	int mode = cookie->fMode & O_RWMASK;
197	if (mode == O_RDWR || mode == O_WRONLY)
198		SyncAndCommit();
199
200	MutexLocker _(fStateLock);
201	ReleaseOpenState();
202
203	return B_OK;
204}
205
206
207char*
208Inode::AttrToFileName(const char* path)
209{
210	ASSERT(path != NULL);
211
212	char* name = strdup(path);
213	if (name == NULL)
214		return NULL;
215
216	char* current = strpbrk(name, "/:");
217	while (current != NULL) {
218		switch (*current) {
219			case '/':
220				*current = '#';
221				break;
222			case ':':
223				*current = '$';
224				break;
225		}
226		current = strpbrk(name, "/:");
227	}
228
229	return name;
230}
231
232
233status_t
234Inode::OpenAttr(const char* _name, int mode, OpenAttrCookie* cookie,
235	bool create, int32 type)
236{
237	ASSERT(_name != NULL);
238	ASSERT(cookie != NULL);
239
240	(void)type;
241
242	status_t result = LoadAttrDirHandle();
243	if (result != B_OK)
244		return result;
245
246	char* name = AttrToFileName(_name);
247	if (name == NULL)
248		return B_NO_MEMORY;
249	MemoryDeleter nameDeleter(name);
250
251	OpenDelegationData data;
252	data.fType = OPEN_DELEGATE_NONE;
253
254	OpenState* state = new OpenState;
255	if (state == NULL)
256		return B_NO_MEMORY;
257
258	state->fFileSystem = fFileSystem;
259	result = NFS4Inode::OpenAttr(state, name, mode, &data, create);
260	if (result != B_OK) {
261		delete state;
262		return result;
263	}
264
265	fFileSystem->AddOpenFile(state);
266
267	cookie->fOpenState = state;
268	cookie->fMode = mode;
269
270	if (data.fType != OPEN_DELEGATE_NONE) {
271		Delegation* delegation
272			= new(std::nothrow) Delegation(data, this, state->fClientID, true);
273		if (delegation != NULL) {
274			delegation->fInfo = state->fInfo;
275			delegation->fFileSystem = fFileSystem;
276			state->fDelegation = delegation;
277			fFileSystem->AddDelegation(delegation);
278		}
279	}
280
281	if (create || (mode & O_TRUNC) == O_TRUNC) {
282		struct stat st;
283		st.st_size = 0;
284		WriteStat(&st, B_STAT_SIZE, cookie);
285	}
286
287	return B_OK;
288}
289
290
291status_t
292Inode::CloseAttr(OpenAttrCookie* cookie)
293{
294	ASSERT(cookie != NULL);
295
296	if (cookie->fOpenState->fDelegation != NULL) {
297		cookie->fOpenState->fDelegation->GiveUp();
298		fFileSystem->RemoveDelegation(cookie->fOpenState->fDelegation);
299	}
300
301	delete cookie->fOpenState->fDelegation;
302	delete cookie->fOpenState;
303	return B_OK;
304}
305
306
307status_t
308Inode::ReadDirect(OpenStateCookie* cookie, off_t pos, void* buffer,
309	size_t* _length, bool* eof)
310{
311	ASSERT(cookie != NULL || fOpenState != NULL);
312	ASSERT(buffer != NULL);
313	ASSERT(_length != NULL);
314	ASSERT(eof != NULL);
315
316	*eof = false;
317	uint32 size = 0;
318
319	uint32 ioSize = fFileSystem->Root()->IOSize();
320	*_length = min_c(ioSize, *_length);
321
322	status_t result;
323	OpenState* state = cookie != NULL ? cookie->fOpenState : fOpenState;
324	while (size < *_length && !*eof) {
325		uint32 len = *_length - size;
326		result = ReadFile(cookie, state, pos + size, &len,
327			reinterpret_cast<char*>(buffer) + size, eof);
328		if (result != B_OK) {
329			if (size == 0)
330				return result;
331			else
332				break;
333		}
334
335		size += len;
336	}
337
338	*_length = size;
339
340	return B_OK;
341}
342
343
344status_t
345Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length)
346{
347	ASSERT(cookie != NULL);
348	ASSERT(buffer != NULL);
349	ASSERT(_length != NULL);
350
351	bool eof = false;
352	if ((cookie->fMode & O_NOCACHE) != 0)
353		return ReadDirect(cookie, pos, buffer, _length, &eof);
354	return file_cache_read(fFileCache, cookie, pos, buffer, _length);
355}
356
357
358status_t
359Inode::WriteDirect(OpenStateCookie* cookie, off_t pos, const void* _buffer,
360	size_t* _length)
361{
362	ASSERT(cookie != NULL || fOpenState != NULL);
363	ASSERT(_buffer != NULL);
364	ASSERT(_length != NULL);
365
366	uint32 size = 0;
367	const char* buffer = reinterpret_cast<const char*>(_buffer);
368
369	uint32 ioSize = fFileSystem->Root()->IOSize();
370	*_length = min_c(ioSize, *_length);
371
372	bool attribute = false;
373	OpenState* state = fOpenState;
374	if (cookie != NULL) {
375		attribute = cookie->fOpenState->fInfo.fHandle != fInfo.fHandle;
376		state = cookie->fOpenState;
377	}
378
379	if (!attribute) {
380		ReadLocker _(fWriteLock);
381		fWriteDirty = true;
382	}
383
384	while (size < *_length) {
385		uint32 len = *_length - size;
386		status_t result = WriteFile(cookie, state, pos + size, &len,
387			buffer + size, attribute);
388		if (result != B_OK) {
389			if (size == 0)
390				return result;
391			else
392				break;
393		}
394
395		size += len;
396	}
397
398	*_length = size;
399
400	fMetaCache.GrowFile(size + pos);
401	fFileSystem->Root()->MakeInfoInvalid();
402
403	return B_OK;
404}
405
406
407status_t
408Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer,
409	size_t* _length)
410{
411	ASSERT(cookie != NULL);
412	ASSERT(_buffer != NULL);
413	ASSERT(_length != NULL);
414
415	if (pos < 0)
416		pos = 0;
417
418	if ((cookie->fMode & O_RWMASK) == O_RDONLY)
419		return B_NOT_ALLOWED;
420
421	if ((cookie->fMode & O_APPEND) != 0)
422		pos = fMaxFileSize;
423
424	uint64 fileSize = pos + *_length;
425	if (fileSize > fMaxFileSize) {
426		status_t result = file_cache_set_size(fFileCache, fileSize);
427		if (result != B_OK)
428			return result;
429		fMaxFileSize = fileSize;
430		fMetaCache.GrowFile(fMaxFileSize);
431	}
432
433	if ((cookie->fMode & O_NOCACHE) != 0) {
434		WriteDirect(cookie, pos, _buffer, _length);
435		Commit();
436	}
437
438	return file_cache_write(fFileCache, cookie, pos, _buffer, _length);
439}
440
441
442status_t
443Inode::Commit()
444{
445	if (!fWriteDirty)
446		return B_OK;
447
448	WriteLocker _(fWriteLock);
449	status_t result = CommitWrites();
450	if (result != B_OK)
451		return result;
452	fWriteDirty = false;
453	return B_OK;
454}
455
456