xref: /haiku/src/add-ons/kernel/file_systems/nfs4/InodeRegular.cpp (revision d072da23441ac6843722b30a8d2df68cbee16b5e)
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 
23 status_t
CreateState(const char * name,int mode,int perms,OpenState * state,OpenDelegationData * delegationData)24 Inode::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 
65 status_t
Create(const char * name,int mode,int perms,OpenFileCookie * cookie,OpenDelegationData * data,ino_t * id)66 Inode::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 
99 status_t
Open(int mode,OpenFileCookie * cookie)100 Inode::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 
190 status_t
Close(OpenFileCookie * cookie)191 Inode::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 
207 char*
AttrToFileName(const char * path)208 Inode::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 
233 status_t
OpenAttr(const char * _name,int mode,OpenAttrCookie * cookie,bool create,int32 type)234 Inode::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 
291 status_t
CloseAttr(OpenAttrCookie * cookie)292 Inode::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 
307 status_t
ReadDirect(OpenStateCookie * cookie,off_t pos,void * buffer,size_t * _length,bool * eof)308 Inode::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 
344 status_t
Read(OpenFileCookie * cookie,off_t pos,void * buffer,size_t * _length)345 Inode::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 
358 status_t
WriteDirect(OpenStateCookie * cookie,off_t pos,const void * _buffer,size_t * _length)359 Inode::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 
407 status_t
Write(OpenFileCookie * cookie,off_t pos,const void * _buffer,size_t * _length)408 Inode::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 
442 status_t
Commit()443 Inode::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