xref: /haiku/src/add-ons/kernel/file_systems/nfs4/InodeRegular.cpp (revision 8d2bf6953e851d431fc67de1bc970c40afa79e9f)
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
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
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
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 		RevalidateFileCache();
110 
111 		OpenState* state = new(std::nothrow) OpenState;
112 		if (state == NULL)
113 			return B_NO_MEMORY;
114 
115 		state->fInfo = fInfo;
116 		state->fFileSystem = fFileSystem;
117 		state->fMode = mode & O_RWMASK;
118 		status_t result = OpenFile(state, mode, &data);
119 		if (result != B_OK) {
120 			delete state;
121 			return result;
122 		}
123 
124 		fFileSystem->AddOpenFile(state);
125 		fOpenState = state;
126 		cookie->fOpenState = state;
127 		locker.Unlock();
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 		} else {
147 			int newMode = mode & O_RWMASK;
148 			uint32 allowed = 0;
149 			if (newMode == O_RDWR || newMode == O_RDONLY)
150 				allowed |= R_OK;
151 			if (newMode == O_RDWR || newMode == O_WRONLY)
152 				allowed |= W_OK;
153 
154 			status_t result = Access(allowed);
155 			if (result != B_OK) {
156 				locker.Lock();
157 				ReleaseOpenState();
158 				return result;
159 			}
160 		}
161 	}
162 
163 	if ((mode & O_TRUNC) == O_TRUNC) {
164 		struct stat st;
165 		st.st_size = 0;
166 		WriteStat(&st, B_STAT_SIZE);
167 		file_cache_set_size(fFileCache, 0);
168 	}
169 
170 	cookie->fMode = mode;
171 	cookie->fLocks = NULL;
172 
173 	if (data.fType != OPEN_DELEGATE_NONE) {
174 		Delegation* delegation
175 			= new(std::nothrow) Delegation(data, this, fOpenState->fClientID);
176 		if (delegation != NULL) {
177 			delegation->fInfo = fOpenState->fInfo;
178 			delegation->fFileSystem = fFileSystem;
179 			SetDelegation(delegation);
180 		}
181 	}
182 
183 	return B_OK;
184 }
185 
186 
187 status_t
188 Inode::Close(OpenFileCookie* cookie)
189 {
190 	ASSERT(cookie != NULL);
191 	ASSERT(fOpenState == cookie->fOpenState);
192 
193 	SyncAndCommit();
194 
195 	MutexLocker _(fStateLock);
196 	ReleaseOpenState();
197 
198 	return B_OK;
199 }
200 
201 
202 char*
203 Inode::AttrToFileName(const char* path)
204 {
205 	ASSERT(path != NULL);
206 
207 	char* name = strdup(path);
208 	if (name == NULL)
209 		return NULL;
210 
211 	char* current = strpbrk(name, "/:");
212 	while (current != NULL) {
213 		switch (*current) {
214 			case '/':
215 				*current = '#';
216 				break;
217 			case ':':
218 				*current = '$';
219 				break;
220 		}
221 		current = strpbrk(name, "/:");
222 	}
223 
224 	return name;
225 }
226 
227 
228 status_t
229 Inode::OpenAttr(const char* _name, int mode, OpenAttrCookie* cookie,
230 	bool create, int32 type)
231 {
232 	ASSERT(_name != NULL);
233 	ASSERT(cookie != NULL);
234 
235 	(void)type;
236 
237 	status_t result = LoadAttrDirHandle();
238 	if (result != B_OK)
239 		return result;
240 
241 	char* name = AttrToFileName(_name);
242 	if (name == NULL)
243 		return B_NO_MEMORY;
244 	MemoryDeleter nameDeleter(name);
245 
246 	OpenDelegationData data;
247 	data.fType = OPEN_DELEGATE_NONE;
248 
249 	OpenState* state = new OpenState;
250 	if (state == NULL)
251 		return B_NO_MEMORY;
252 
253 	state->fFileSystem = fFileSystem;
254 	result = NFS4Inode::OpenAttr(state, name, mode, &data, create);
255 	if (result != B_OK) {
256 		delete state;
257 		return result;
258 	}
259 
260 	fFileSystem->AddOpenFile(state);
261 
262 	cookie->fOpenState = state;
263 	cookie->fMode = mode;
264 
265 	if (data.fType != OPEN_DELEGATE_NONE) {
266 		Delegation* delegation
267 			= new(std::nothrow) Delegation(data, this, state->fClientID, true);
268 		if (delegation != NULL) {
269 			delegation->fInfo = state->fInfo;
270 			delegation->fFileSystem = fFileSystem;
271 			state->fDelegation = delegation;
272 			fFileSystem->AddDelegation(delegation);
273 		}
274 	}
275 
276 	if (create || (mode & O_TRUNC) == O_TRUNC) {
277 		struct stat st;
278 		st.st_size = 0;
279 		WriteStat(&st, B_STAT_SIZE, cookie);
280 	}
281 
282 	return B_OK;
283 }
284 
285 
286 status_t
287 Inode::CloseAttr(OpenAttrCookie* cookie)
288 {
289 	ASSERT(cookie != NULL);
290 
291 	if (cookie->fOpenState->fDelegation != NULL) {
292 		cookie->fOpenState->fDelegation->GiveUp();
293 		fFileSystem->RemoveDelegation(cookie->fOpenState->fDelegation);
294 	}
295 
296 	delete cookie->fOpenState->fDelegation;
297 	delete cookie->fOpenState;
298 	return B_OK;
299 }
300 
301 
302 status_t
303 Inode::ReadDirect(OpenStateCookie* cookie, off_t pos, void* buffer,
304 	size_t* _length, bool* eof)
305 {
306 	ASSERT(cookie != NULL || fOpenState != NULL);
307 	ASSERT(buffer != NULL);
308 	ASSERT(_length != NULL);
309 	ASSERT(eof != NULL);
310 
311 	*eof = false;
312 	uint32 size = 0;
313 
314 	uint32 ioSize = fFileSystem->Root()->IOSize();
315 	*_length = min_c(ioSize, *_length);
316 
317 	status_t result;
318 	OpenState* state = cookie != NULL ? cookie->fOpenState : fOpenState;
319 	while (size < *_length && !*eof) {
320 		uint32 len = *_length - size;
321 		result = ReadFile(cookie, state, pos + size, &len,
322 			reinterpret_cast<char*>(buffer) + size, eof);
323 		if (result != B_OK) {
324 			if (size == 0)
325 				return result;
326 			else
327 				break;
328 		}
329 
330 		size += len;
331 	}
332 
333 	*_length = size;
334 
335 	return B_OK;
336 }
337 
338 
339 status_t
340 Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length)
341 {
342 	ASSERT(cookie != NULL);
343 	ASSERT(buffer != NULL);
344 	ASSERT(_length != NULL);
345 
346 	bool eof = false;
347 	if ((cookie->fMode & O_NOCACHE) != 0)
348 		return ReadDirect(cookie, pos, buffer, _length, &eof);
349 	return file_cache_read(fFileCache, cookie, pos, buffer, _length);
350 }
351 
352 
353 status_t
354 Inode::WriteDirect(OpenStateCookie* cookie, off_t pos, const void* _buffer,
355 	size_t* _length)
356 {
357 	ASSERT(cookie != NULL || fOpenState != NULL);
358 	ASSERT(_buffer != NULL);
359 	ASSERT(_length != NULL);
360 
361 	uint32 size = 0;
362 	const char* buffer = reinterpret_cast<const char*>(_buffer);
363 
364 	uint32 ioSize = fFileSystem->Root()->IOSize();
365 	*_length = min_c(ioSize, *_length);
366 
367 	bool attribute = false;
368 	OpenState* state = fOpenState;
369 	if (cookie != NULL) {
370 		attribute = cookie->fOpenState->fInfo.fHandle != fInfo.fHandle;
371 		state = cookie->fOpenState;
372 	}
373 
374 	if (!attribute) {
375 		ReadLocker _(fWriteLock);
376 		fWriteDirty = true;
377 	}
378 
379 	while (size < *_length) {
380 		uint32 len = *_length - size;
381 		status_t result = WriteFile(cookie, state, pos + size, &len,
382 			buffer + size, attribute);
383 		if (result != B_OK) {
384 			if (size == 0)
385 				return result;
386 			else
387 				break;
388 		}
389 
390 		size += len;
391 	}
392 
393 	*_length = size;
394 
395 	fMetaCache.GrowFile(size + pos);
396 	fFileSystem->Root()->MakeInfoInvalid();
397 
398 	return B_OK;
399 }
400 
401 
402 status_t
403 Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer,
404 	size_t* _length)
405 {
406 	ASSERT(cookie != NULL);
407 	ASSERT(_buffer != NULL);
408 	ASSERT(_length != NULL);
409 
410 	if (pos < 0)
411 		pos = 0;
412 
413 	if ((cookie->fMode & O_RWMASK) == O_RDONLY)
414 		return B_NOT_ALLOWED;
415 
416 	if ((cookie->fMode & O_APPEND) != 0)
417 		pos = fMaxFileSize;
418 
419 	uint64 fileSize = pos + *_length;
420 	if (fileSize > fMaxFileSize) {
421 		status_t result = file_cache_set_size(fFileCache, fileSize);
422 		if (result != B_OK)
423 			return result;
424 		fMaxFileSize = fileSize;
425 		fMetaCache.GrowFile(fMaxFileSize);
426 	}
427 
428 	if ((cookie->fMode & O_NOCACHE) != 0) {
429 		WriteDirect(cookie, pos, _buffer, _length);
430 		Commit();
431 	}
432 
433 	return file_cache_write(fFileCache, cookie, pos, _buffer, _length);
434 }
435 
436 
437 status_t
438 Inode::Commit()
439 {
440 	WriteLocker _(fWriteLock);
441 
442 	if (!fWriteDirty)
443 		return B_OK;
444 	status_t result = CommitWrites();
445 	if (result != B_OK)
446 		return result;
447 	fWriteDirty = false;
448 	return B_OK;
449 }
450 
451