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 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 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* 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 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 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 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 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 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 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 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