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