vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / file_systems / nfs4 / InodeRegular.cpp
blob30c5a55c0f97a1bfebe18a01f37872c181284c31
1 /*
2 * Copyright 2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Paweł Dziepak, pdziepak@quarnos.org
7 */
10 #include "Inode.h"
12 #include <string.h>
14 #include <AutoDeleter.h>
15 #include <fs_cache.h>
16 #include <NodeMonitor.h>
18 #include "IdMap.h"
19 #include "Request.h"
20 #include "RootInode.h"
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);
30 uint64 fileID;
31 FileHandle handle;
32 ChangeInfo changeInfo;
34 status_t result = CreateFile(name, mode, perms, state, &changeInfo,
35 &fileID, &handle, delegationData);
36 if (result != B_OK)
37 return result;
39 FileInfo fileInfo;
40 fileInfo.fFileId = fileID;
41 fileInfo.fHandle = handle;
43 fFileSystem->InoIdMap()->AddName(fileInfo, fInfo.fNames, name,
44 FileIdToInoT(fileID));
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();
55 fCache->Unlock();
57 state->fFileSystem = fFileSystem;
58 state->fInfo = fileInfo;
59 state->fMode = mode & O_RWMASK;
61 return B_OK;
65 status_t
66 Inode::Create(const char* name, int mode, int perms, OpenFileCookie* cookie,
67 OpenDelegationData* data, ino_t* id)
69 ASSERT(name != NULL);
70 ASSERT(cookie != NULL);
71 ASSERT(data != NULL);
73 cookie->fMode = mode;
74 cookie->fLocks = NULL;
76 OpenState* state = new(std::nothrow) OpenState;
77 if (state == NULL)
78 return B_NO_MEMORY;
80 status_t result = CreateState(name, mode, perms, state, data);
81 if (result != B_OK) {
82 delete state;
83 return result;
86 cookie->fOpenState = state;
88 *id = FileIdToInoT(state->fInfo.fFileId);
90 fFileSystem->AddOpenFile(state);
91 fFileSystem->Root()->MakeInfoInvalid();
93 notify_entry_created(fFileSystem->DevId(), ID(), name, *id);
95 return B_OK;
99 status_t
100 Inode::Open(int mode, OpenFileCookie* cookie)
102 ASSERT(cookie != NULL);
104 MutexLocker locker(fStateLock);
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;
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;
122 fFileSystem->AddOpenFile(state);
123 fOpenState = state;
124 cookie->fOpenState = state;
125 locker.Unlock();
127 RevalidateFileCache();
128 } else {
129 fOpenState->AcquireReference();
130 cookie->fOpenState = fOpenState;
131 locker.Unlock();
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();
139 status_t result = OpenFile(fOpenState, O_RDWR, &data);
140 if (result != B_OK) {
141 locker.Lock();
142 ReleaseOpenState();
143 return result;
145 fOpenState->fMode = O_RDWR;
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;
157 status_t result = Access(allowed);
158 if (result != B_OK) {
159 locker.Lock();
160 ReleaseOpenState();
161 return result;
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);
173 cookie->fMode = mode;
174 cookie->fLocks = NULL;
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);
186 return B_OK;
190 status_t
191 Inode::Close(OpenFileCookie* cookie)
193 ASSERT(cookie != NULL);
194 ASSERT(fOpenState == cookie->fOpenState);
196 int mode = cookie->fMode & O_RWMASK;
197 if (mode == O_RDWR || mode == O_WRONLY)
198 SyncAndCommit();
200 MutexLocker _(fStateLock);
201 ReleaseOpenState();
203 return B_OK;
207 char*
208 Inode::AttrToFileName(const char* path)
210 ASSERT(path != NULL);
212 char* name = strdup(path);
213 if (name == NULL)
214 return NULL;
216 char* current = strpbrk(name, "/:");
217 while (current != NULL) {
218 switch (*current) {
219 case '/':
220 *current = '#';
221 break;
222 case ':':
223 *current = '$';
224 break;
226 current = strpbrk(name, "/:");
229 return name;
233 status_t
234 Inode::OpenAttr(const char* _name, int mode, OpenAttrCookie* cookie,
235 bool create, int32 type)
237 ASSERT(_name != NULL);
238 ASSERT(cookie != NULL);
240 (void)type;
242 status_t result = LoadAttrDirHandle();
243 if (result != B_OK)
244 return result;
246 char* name = AttrToFileName(_name);
247 if (name == NULL)
248 return B_NO_MEMORY;
249 MemoryDeleter nameDeleter(name);
251 OpenDelegationData data;
252 data.fType = OPEN_DELEGATE_NONE;
254 OpenState* state = new OpenState;
255 if (state == NULL)
256 return B_NO_MEMORY;
258 state->fFileSystem = fFileSystem;
259 result = NFS4Inode::OpenAttr(state, name, mode, &data, create);
260 if (result != B_OK) {
261 delete state;
262 return result;
265 fFileSystem->AddOpenFile(state);
267 cookie->fOpenState = state;
268 cookie->fMode = mode;
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);
281 if (create || (mode & O_TRUNC) == O_TRUNC) {
282 struct stat st;
283 st.st_size = 0;
284 WriteStat(&st, B_STAT_SIZE, cookie);
287 return B_OK;
291 status_t
292 Inode::CloseAttr(OpenAttrCookie* cookie)
294 ASSERT(cookie != NULL);
296 if (cookie->fOpenState->fDelegation != NULL) {
297 cookie->fOpenState->fDelegation->GiveUp();
298 fFileSystem->RemoveDelegation(cookie->fOpenState->fDelegation);
301 delete cookie->fOpenState->fDelegation;
302 delete cookie->fOpenState;
303 return B_OK;
307 status_t
308 Inode::ReadDirect(OpenStateCookie* cookie, off_t pos, void* buffer,
309 size_t* _length, bool* eof)
311 ASSERT(cookie != NULL || fOpenState != NULL);
312 ASSERT(buffer != NULL);
313 ASSERT(_length != NULL);
314 ASSERT(eof != NULL);
316 *eof = false;
317 uint32 size = 0;
319 uint32 ioSize = fFileSystem->Root()->IOSize();
320 *_length = min_c(ioSize, *_length);
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;
335 size += len;
338 *_length = size;
340 return B_OK;
344 status_t
345 Inode::Read(OpenFileCookie* cookie, off_t pos, void* buffer, size_t* _length)
347 ASSERT(cookie != NULL);
348 ASSERT(buffer != NULL);
349 ASSERT(_length != NULL);
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);
358 status_t
359 Inode::WriteDirect(OpenStateCookie* cookie, off_t pos, const void* _buffer,
360 size_t* _length)
362 ASSERT(cookie != NULL || fOpenState != NULL);
363 ASSERT(_buffer != NULL);
364 ASSERT(_length != NULL);
366 uint32 size = 0;
367 const char* buffer = reinterpret_cast<const char*>(_buffer);
369 uint32 ioSize = fFileSystem->Root()->IOSize();
370 *_length = min_c(ioSize, *_length);
372 bool attribute = false;
373 OpenState* state = fOpenState;
374 if (cookie != NULL) {
375 attribute = cookie->fOpenState->fInfo.fHandle != fInfo.fHandle;
376 state = cookie->fOpenState;
379 if (!attribute) {
380 ReadLocker _(fWriteLock);
381 fWriteDirty = true;
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;
395 size += len;
398 *_length = size;
400 fMetaCache.GrowFile(size + pos);
401 fFileSystem->Root()->MakeInfoInvalid();
403 return B_OK;
407 status_t
408 Inode::Write(OpenFileCookie* cookie, off_t pos, const void* _buffer,
409 size_t* _length)
411 ASSERT(cookie != NULL);
412 ASSERT(_buffer != NULL);
413 ASSERT(_length != NULL);
415 if (pos < 0)
416 pos = 0;
418 if ((cookie->fMode & O_RWMASK) == O_RDONLY)
419 return B_NOT_ALLOWED;
421 if ((cookie->fMode & O_APPEND) != 0)
422 pos = fMaxFileSize;
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);
433 if ((cookie->fMode & O_NOCACHE) != 0) {
434 WriteDirect(cookie, pos, _buffer, _length);
435 Commit();
438 return file_cache_write(fFileCache, cookie, pos, _buffer, _length);
442 status_t
443 Inode::Commit()
445 if (!fWriteDirty)
446 return B_OK;
448 WriteLocker _(fWriteLock);
449 status_t result = CommitWrites();
450 if (result != B_OK)
451 return result;
452 fWriteDirty = false;
453 return B_OK;