1 // kernel_interface.cpp 2 // 3 // Copyright (c) 2003-2008, Axel Dörfler (axeld@pinc-software.de) 4 // Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) 5 // 6 // This program is free software; you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation; either version 2 of the License, or 9 // (at your option) any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program; if not, write to the Free Software 18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 // 20 // You can alternatively use *this file* under the terms of the the MIT 21 // license included in this package. 22 23 #include <ctype.h> 24 #include <dirent.h> 25 #include <errno.h> 26 #include <fcntl.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <time.h> 30 #include <unistd.h> 31 #include <stdio.h> 32 #include <sys/stat.h> 33 34 #include <fs_index.h> 35 #include <fs_info.h> 36 #include <fs_interface.h> 37 #include <fs_query.h> 38 #include <fs_volume.h> 39 #include <KernelExport.h> 40 #include <NodeMonitor.h> 41 #include <TypeConstants.h> 42 43 #include <AutoDeleter.h> 44 45 #include "AllocationInfo.h" 46 #include "AttributeIndex.h" 47 #include "AttributeIterator.h" 48 #include "Debug.h" 49 #include "Directory.h" 50 #include "Entry.h" 51 #include "EntryIterator.h" 52 #include "File.h" 53 #include "Index.h" 54 #include "IndexDirectory.h" 55 #include "Locking.h" 56 #include "Misc.h" 57 #include "Node.h" 58 #include "Query.h" 59 #include "ramfs_ioctl.h" 60 #include "SymLink.h" 61 #include "Volume.h" 62 63 64 static const size_t kOptimalIOSize = 65536; 65 static const bigtime_t kNotificationInterval = 1000000LL; 66 67 // notify_if_stat_changed 68 void 69 notify_if_stat_changed(Volume *volume, Node *node) 70 { 71 if (volume && node && node->IsModified()) { 72 uint32 statFields = node->MarkUnmodified(); 73 notify_stat_changed(volume->GetID(), node->GetID(), statFields); 74 } 75 } 76 77 78 // #pragma mark - FS 79 80 81 // ramfs_mount 82 static status_t 83 ramfs_mount(dev_t nsid, const char* /*device*/, uint32 flags, 84 const char* /*args*/, fs_volume* _volume, ino_t* rootID) 85 { 86 FUNCTION_START(); 87 // parameters are ignored for now 88 status_t error = B_OK; 89 // fail, if read-only mounting is requested 90 if (flags & B_MOUNT_READ_ONLY) 91 error = B_BAD_VALUE; 92 // allocate and init the volume 93 Volume *volume = NULL; 94 if (error == B_OK) { 95 volume = new(nothrow) Volume; 96 if (!volume) 97 SET_ERROR(error, B_NO_MEMORY); 98 if (error == B_OK) 99 error = volume->Mount(nsid); 100 } 101 // set the results 102 if (error == B_OK) { 103 *rootID = volume->GetRootDirectory()->GetID(); 104 *_volume = volume; 105 } 106 // cleanup on failure 107 if (error != B_OK && volume) 108 delete volume; 109 110 RETURN_ERROR(error); 111 } 112 113 // ramfs_unmount 114 static status_t 115 ramfs_unmount(fs_volume fs) 116 { 117 FUNCTION_START(); 118 Volume *volume = (Volume*)fs; 119 status_t error = volume->Unmount(); 120 if (error == B_OK) 121 delete volume; 122 if (error != B_OK) 123 REPORT_ERROR(error); 124 return error; 125 } 126 127 // ramfs_read_fs_info 128 static status_t 129 ramfs_read_fs_info(fs_volume fs, struct fs_info *info) 130 { 131 FUNCTION_START(); 132 Volume *volume = (Volume*)fs; 133 status_t error = B_OK; 134 if (VolumeReadLocker locker = volume) { 135 info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME 136 | B_FS_HAS_QUERY; 137 info->block_size = volume->GetBlockSize(); 138 info->io_size = kOptimalIOSize; 139 info->total_blocks = volume->CountBlocks(); 140 info->free_blocks = volume->CountFreeBlocks(); 141 info->device_name[0] = '\0'; 142 strncpy(info->volume_name, volume->GetName(), sizeof(info->volume_name)); 143 strcpy(info->fsh_name, "ramfs"); 144 } else 145 SET_ERROR(error, B_ERROR); 146 return B_OK; 147 } 148 149 150 // ramfs_write_fs_info 151 static status_t 152 ramfs_write_fs_info(fs_volume fs, const struct fs_info *info, uint32 mask) 153 154 { 155 FUNCTION_START(); 156 Volume *volume = (Volume*)fs; 157 status_t error = B_OK; 158 if (VolumeWriteLocker locker = volume) { 159 if (mask & FS_WRITE_FSINFO_NAME) 160 error = volume->SetName(info->volume_name); 161 } else 162 SET_ERROR(error, B_ERROR); 163 RETURN_ERROR(error); 164 } 165 166 // ramfs_sync 167 static status_t 168 ramfs_sync(fs_volume /*fs*/) 169 { 170 FUNCTION_START(); 171 return B_OK; 172 } 173 174 175 // #pragma mark - VNodes 176 177 178 // ramfs_lookup 179 static status_t 180 ramfs_lookup(fs_volume fs, fs_vnode _dir, const char *entryName, ino_t *vnid, 181 int *type) 182 { 183 // FUNCTION_START(); 184 Volume *volume = (Volume*)fs; 185 Directory *dir = dynamic_cast<Directory*>((Node*)_dir); 186 FUNCTION(("dir: (%llu), entry: `%s'\n", (dir ? dir->GetID() : -1), entryName)); 187 188 // check for non-directories 189 if (!dir) 190 RETURN_ERROR(B_NOT_A_DIRECTORY); 191 192 status_t error = B_OK; 193 if (VolumeReadLocker locker = volume) { 194 Node *node = NULL; 195 196 // special entries: "." and ".." 197 if (!strcmp(entryName, ".")) { 198 *vnid = dir->GetID(); 199 if (volume->GetVNode(*vnid, &node) != B_OK) 200 error = B_BAD_VALUE; 201 } else if (!strcmp(entryName, "..")) { 202 Directory *parent = dir->GetParent(); 203 if (parent && volume->GetVNode(parent->GetID(), &node) == B_OK) 204 *vnid = node->GetID(); 205 else 206 error = B_BAD_VALUE; 207 208 // ordinary entries 209 } else { 210 // find the entry 211 error = dir->FindAndGetNode(entryName, &node); 212 SET_ERROR(error, error); 213 if (error == B_OK) 214 *vnid = node->GetID(); 215 } 216 217 // get the node's type 218 if (error == B_OK) 219 *type = node->GetMode() & S_IFMT; 220 } else 221 SET_ERROR(error, B_ERROR); 222 RETURN_ERROR(error); 223 } 224 225 // ramfs_read_vnode 226 static status_t 227 ramfs_read_vnode(fs_volume fs, ino_t vnid, fs_vnode *node, bool reenter) 228 { 229 // FUNCTION_START(); 230 FUNCTION(("node: %Ld\n", vnid)); 231 Volume *volume = (Volume*)fs; 232 Node *foundNode = NULL; 233 status_t error = B_OK; 234 if (VolumeReadLocker locker = volume) { 235 error = volume->FindNode(vnid, &foundNode); 236 if (error == B_OK) 237 *node = foundNode; 238 } else 239 SET_ERROR(error, B_ERROR); 240 RETURN_ERROR(error); 241 } 242 243 // ramfs_write_vnode 244 static status_t 245 ramfs_write_vnode(fs_volume /*fs*/, fs_vnode DARG(_node), bool /*reenter*/) 246 { 247 // DANGER: If dbg_printf() is used, this thread will enter another FS and 248 // even perform a write operation. The is dangerous here, since this hook 249 // may be called out of the other FSs, since, for instance a put_vnode() 250 // called from another FS may cause the VFS layer to free vnodes and thus 251 // invoke this hook. 252 // FUNCTION_START(); 253 //FUNCTION(("node: %Ld\n", ((Node*)_node)->GetID())); 254 status_t error = B_OK; 255 RETURN_ERROR(error); 256 } 257 258 // ramfs_remove_vnode 259 static status_t 260 ramfs_remove_vnode(fs_volume fs, fs_vnode _node, bool /*reenter*/) 261 { 262 FUNCTION(("node: %Ld\n", ((Node*)_node)->GetID())); 263 Volume *volume = (Volume*)fs; 264 Node *node = (Node*)_node; 265 status_t error = B_OK; 266 if (VolumeWriteLocker locker = volume) { 267 volume->NodeRemoved(node); 268 delete node; 269 } else 270 SET_ERROR(error, B_ERROR); 271 RETURN_ERROR(error); 272 } 273 274 275 // #pragma mark - Nodes 276 277 278 // ramfs_ioctl 279 static status_t 280 ramfs_ioctl(fs_volume fs, fs_vnode /*node*/, fs_cookie /*cookie*/, ulong cmd, 281 void *buffer, size_t /*length*/) 282 283 { 284 FUNCTION_START(); 285 Volume *volume = (Volume*)fs; 286 status_t error = B_OK; 287 switch (cmd) { 288 case RAMFS_IOCTL_GET_ALLOCATION_INFO: 289 { 290 if (buffer) { 291 if (VolumeReadLocker locker = volume) { 292 AllocationInfo *info = (AllocationInfo*)buffer; 293 volume->GetAllocationInfo(*info); 294 } else 295 SET_ERROR(error, B_ERROR); 296 } else 297 SET_ERROR(error, B_BAD_VALUE); 298 break; 299 } 300 case RAMFS_IOCTL_DUMP_INDEX: 301 { 302 if (buffer) { 303 if (VolumeReadLocker locker = volume) { 304 const char *name = (const char*)buffer; 305 PRINT((" RAMFS_IOCTL_DUMP_INDEX, `%s'\n", name)); 306 IndexDirectory *indexDir = volume->GetIndexDirectory(); 307 if (indexDir) { 308 if (Index *index = indexDir->FindIndex(name)) 309 index->Dump(); 310 else 311 SET_ERROR(error, B_ENTRY_NOT_FOUND); 312 } else 313 SET_ERROR(error, B_ENTRY_NOT_FOUND); 314 } else 315 SET_ERROR(error, B_ERROR); 316 } else 317 SET_ERROR(error, B_BAD_VALUE); 318 break; 319 } 320 default: 321 error = B_BAD_VALUE; 322 break; 323 } 324 RETURN_ERROR(error); 325 } 326 327 // ramfs_set_flags 328 static status_t 329 ramfs_set_flags(fs_volume /*fs*/, fs_vnode /*node*/, fs_cookie /*cookie*/, 330 int /*flags*/) 331 { 332 FUNCTION_START(); 333 // TODO:... 334 return B_OK; 335 } 336 337 // ramfs_fsync 338 static status_t 339 ramfs_fsync(fs_volume /*fs*/, fs_vnode /*node*/) 340 { 341 FUNCTION_START(); 342 return B_OK; 343 } 344 345 // ramfs_read_symlink 346 static status_t 347 ramfs_read_symlink(fs_volume fs, fs_vnode _node, char *buffer, 348 size_t *bufferSize) 349 { 350 FUNCTION_START(); 351 Volume *volume = (Volume*)fs; 352 Node *node = (Node*)_node; 353 status_t error = B_OK; 354 if (VolumeReadLocker locker = volume) { 355 // read symlinks only 356 if (!node->IsSymLink()) 357 error = B_BAD_VALUE; 358 if (error == B_OK) { 359 if (SymLink *symLink = dynamic_cast<SymLink*>(node)) { 360 // copy the link contents 361 size_t toRead = min(*bufferSize, 362 symLink->GetLinkedPathLength()); 363 if (toRead > 0) 364 memcpy(buffer, symLink->GetLinkedPath(), toRead); 365 *bufferSize = toRead; 366 } else { 367 FATAL(("Node %Ld pretends to be a SymLink, but isn't!\n", 368 node->GetID())); 369 error = B_BAD_VALUE; 370 } 371 } 372 } else 373 SET_ERROR(error, B_ERROR); 374 RETURN_ERROR(error); 375 } 376 377 // ramfs_create_symlink 378 static status_t 379 ramfs_create_symlink(fs_volume fs, fs_vnode _dir, const char *name, 380 const char *path, int mode) 381 { 382 FUNCTION(("name: `%s', path: `%s'\n", name, path)); 383 Volume *volume = (Volume*)fs; 384 Directory *dir = dynamic_cast<Directory*>((Node*)_dir); 385 status_t error = B_OK; 386 // check name 387 if (!name || *name == '\0') { 388 SET_ERROR(error, B_BAD_VALUE); 389 // check directory 390 } else if (!dir) { 391 SET_ERROR(error, B_BAD_VALUE); 392 } else if (VolumeWriteLocker locker = volume) { 393 NodeMTimeUpdater mTimeUpdater(dir); 394 // directory deleted? 395 bool removed; 396 if (get_vnode_removed(volume->GetID(), dir->GetID(), &removed) != B_OK 397 || removed) { 398 SET_ERROR(error, B_NOT_ALLOWED); 399 } 400 // check directory write permissions 401 error = dir->CheckPermissions(ACCESS_W); 402 Node *node = NULL; 403 if (error == B_OK) { 404 // check if entry does already exist 405 if (dir->FindNode(name, &node) == B_OK) { 406 SET_ERROR(error, B_FILE_EXISTS); 407 } else { 408 // entry doesn't exist: create a symlink 409 SymLink *symLink = NULL; 410 error = dir->CreateSymLink(name, path, &symLink); 411 if (error == B_OK) { 412 node = symLink; 413 // set permissions, owner and group 414 node->SetMode(mode); 415 node->SetUID(geteuid()); 416 node->SetGID(getegid()); 417 // put the node 418 volume->PutVNode(node->GetID()); 419 } 420 } 421 } 422 NodeMTimeUpdater mTimeUpdater2(node); 423 // notify listeners 424 if (error == B_OK) { 425 notify_entry_created(volume->GetID(), dir->GetID(), name, 426 node->GetID()); 427 } 428 } else 429 SET_ERROR(error, B_ERROR); 430 RETURN_ERROR(error); 431 } 432 433 // ramfs_link 434 static status_t 435 ramfs_link(fs_volume fs, fs_vnode _dir, const char *name, fs_vnode _node) 436 { 437 FUNCTION(("name: `%s'\n", name)); 438 Volume *volume = (Volume*)fs; 439 Directory *dir = dynamic_cast<Directory*>((Node*)_dir); 440 Node *node = (Node*)_node; 441 status_t error = B_OK; 442 // check directory 443 if (!dir) { 444 SET_ERROR(error, B_BAD_VALUE); 445 } else if (VolumeWriteLocker locker = volume) { 446 NodeMTimeUpdater mTimeUpdater(dir); 447 // directory deleted? 448 bool removed; 449 if (get_vnode_removed(volume->GetID(), dir->GetID(), &removed) != B_OK 450 || removed) { 451 SET_ERROR(error, B_NOT_ALLOWED); 452 } 453 // check directory write permissions 454 error = dir->CheckPermissions(ACCESS_W); 455 Entry *entry = NULL; 456 if (error == B_OK) { 457 // check if entry does already exist 458 if (dir->FindEntry(name, &entry) == B_OK) { 459 SET_ERROR(error, B_FILE_EXISTS); 460 } else { 461 // entry doesn't exist: create a link 462 error = dir->CreateEntry(node, name); 463 } 464 } 465 // notify listeners 466 if (error == B_OK) { 467 notify_entry_created(volume->GetID(), dir->GetID(), name, 468 node->GetID()); 469 } 470 } else 471 SET_ERROR(error, B_ERROR); 472 RETURN_ERROR(error); 473 } 474 475 // ramfs_unlink 476 static status_t 477 ramfs_unlink(fs_volume fs, fs_vnode _dir, const char *name) 478 { 479 FUNCTION(("name: `%s'\n", name)); 480 Volume *volume = (Volume*)fs; 481 Directory *dir = dynamic_cast<Directory*>((Node*)_dir); 482 status_t error = B_OK; 483 // check name 484 if (!name || *name == '\0' || !strcmp(name, ".") || !strcmp(name, "..")) { 485 SET_ERROR(error, B_BAD_VALUE); 486 // check node 487 } else if (!dir) { 488 SET_ERROR(error, B_BAD_VALUE); 489 } else if (VolumeWriteLocker locker = volume) { 490 NodeMTimeUpdater mTimeUpdater(dir); 491 // check directory write permissions 492 error = dir->CheckPermissions(ACCESS_W); 493 ino_t nodeID = -1; 494 if (error == B_OK) { 495 // check if entry exists 496 Node *node = NULL; 497 Entry *entry = NULL; 498 if (dir->FindAndGetNode(name, &node, &entry) == B_OK) { 499 nodeID = node->GetID(); 500 // unlink the entry, if it isn't a non-empty directory 501 if (node->IsDirectory() 502 && !dynamic_cast<Directory*>(node)->IsEmpty()) { 503 SET_ERROR(error, B_DIRECTORY_NOT_EMPTY); 504 } else 505 error = dir->DeleteEntry(entry); 506 volume->PutVNode(node); 507 } else 508 SET_ERROR(error, B_ENTRY_NOT_FOUND); 509 } 510 // notify listeners 511 if (error == B_OK) 512 notify_entry_removed(volume->GetID(), dir->GetID(), name, nodeID); 513 } else 514 SET_ERROR(error, B_ERROR); 515 RETURN_ERROR(error); 516 } 517 518 // ramfs_rename 519 static status_t 520 ramfs_rename(fs_volume fs, fs_vnode _oldDir, const char *oldName, 521 fs_vnode _newDir, const char *newName) 522 { 523 Volume *volume = (Volume*)fs; 524 Directory *oldDir = dynamic_cast<Directory*>((Node*)_oldDir); 525 Directory *newDir = dynamic_cast<Directory*>((Node*)_newDir); 526 status_t error = B_OK; 527 528 if (VolumeWriteLocker locker = volume) { 529 FUNCTION(("old dir: %Ld, old name: `%s', new dir: %Ld, new name: `%s'\n", 530 oldDir->GetID(), oldName, newDir->GetID(), newName)); 531 NodeMTimeUpdater mTimeUpdater1(oldDir); 532 NodeMTimeUpdater mTimeUpdater2(newDir); 533 534 // target directory deleted? 535 bool removed; 536 if (get_vnode_removed(volume->GetID(), newDir->GetID(), &removed) 537 != B_OK 538 || removed) { 539 SET_ERROR(error, B_NOT_ALLOWED); 540 } 541 542 // check directory write permissions 543 if (error == B_OK) 544 error = oldDir->CheckPermissions(ACCESS_W); 545 if (error == B_OK) 546 error = newDir->CheckPermissions(ACCESS_W); 547 548 Node *node = NULL; 549 Entry *entry = NULL; 550 if (error == B_OK) { 551 // check if entry exists 552 if (oldDir->FindAndGetNode(oldName, &node, &entry) != B_OK) { 553 SET_ERROR(error, B_ENTRY_NOT_FOUND); 554 } else { 555 if (oldDir != newDir) { 556 // check whether the entry is a descendent of the target 557 // directory 558 for (Directory *parent = newDir; 559 parent; 560 parent = parent->GetParent()) { 561 if (parent == node) { 562 error = B_BAD_VALUE; 563 break; 564 } else if (parent == oldDir) 565 break; 566 } 567 } 568 } 569 570 // check the target directory situation 571 Node *clobberNode = NULL; 572 Entry *clobberEntry = NULL; 573 if (error == B_OK) { 574 if (newDir->FindAndGetNode(newName, &clobberNode, 575 &clobberEntry) == B_OK) { 576 if (clobberNode->IsDirectory() 577 && !dynamic_cast<Directory*>(clobberNode)->IsEmpty()) { 578 SET_ERROR(error, B_NAME_IN_USE); 579 } 580 } 581 } 582 583 // do the job 584 if (error == B_OK) { 585 // temporarily acquire an additional reference to make 586 // sure the node isn't deleted when we remove the entry 587 error = node->AddReference(); 588 if (error == B_OK) { 589 // delete the original entry 590 error = oldDir->DeleteEntry(entry); 591 if (error == B_OK) { 592 // create the new one/relink the target entry 593 if (clobberEntry) 594 error = clobberEntry->Link(node); 595 else 596 error = newDir->CreateEntry(node, newName); 597 598 if (error == B_OK) { 599 // send a "removed" notification for the clobbered 600 // entry 601 if (clobberEntry) { 602 notify_entry_removed(volume->GetID(), 603 newDir->GetID(), newName, 604 clobberNode->GetID()); 605 } 606 } else { 607 // try to recreate the original entry, in case of 608 // failure 609 newDir->CreateEntry(node, oldName); 610 } 611 } 612 node->RemoveReference(); 613 } 614 } 615 616 // release the entries 617 if (clobberEntry) 618 volume->PutVNode(clobberNode); 619 if (entry) 620 volume->PutVNode(node); 621 } 622 623 // notify listeners 624 if (error == B_OK) { 625 notify_entry_moved(volume->GetID(), oldDir->GetID(), oldName, 626 newDir->GetID(), newName, node->GetID()); 627 } 628 } else 629 SET_ERROR(error, B_ERROR); 630 631 RETURN_ERROR(error); 632 } 633 634 // ramfs_access 635 static status_t 636 ramfs_access(fs_volume fs, fs_vnode _node, int mode) 637 { 638 FUNCTION_START(); 639 Volume *volume = (Volume*)fs; 640 Node *node = (Node*)_node; 641 status_t error = B_OK; 642 if (VolumeReadLocker locker = volume) { 643 error = node->CheckPermissions(mode); 644 } else 645 SET_ERROR(error, B_ERROR); 646 RETURN_ERROR(error); 647 } 648 649 // ramfs_read_stat 650 static status_t 651 ramfs_read_stat(fs_volume fs, fs_vnode _node, struct stat *st) 652 { 653 // FUNCTION_START(); 654 Volume *volume = (Volume*)fs; 655 Node *node = (Node*)_node; 656 FUNCTION(("node: %Ld\n", node->GetID())); 657 status_t error = B_OK; 658 if (VolumeReadLocker locker = volume) { 659 st->st_dev = volume->GetID(); 660 st->st_ino = node->GetID(); 661 st->st_mode = node->GetMode(); 662 st->st_nlink = node->GetRefCount(); 663 st->st_uid = node->GetUID(); 664 st->st_gid = node->GetGID(); 665 st->st_size = node->GetSize(); 666 st->st_blksize = kOptimalIOSize; 667 st->st_atime = node->GetATime(); 668 st->st_mtime = node->GetMTime(); 669 st->st_ctime = node->GetCTime(); 670 st->st_crtime = node->GetCrTime(); 671 } else 672 SET_ERROR(error, B_ERROR); 673 RETURN_ERROR(error); 674 } 675 676 // ramfs_write_stat 677 static status_t 678 ramfs_write_stat(fs_volume fs, fs_vnode _node, const struct stat *st, 679 uint32 mask) 680 { 681 FUNCTION(("mask: %lx\n", mask)); 682 Volume *volume = (Volume*)fs; 683 Node *node = (Node*)_node; 684 status_t error = B_OK; 685 if (VolumeWriteLocker locker = volume) { 686 NodeMTimeUpdater mTimeUpdater(node); 687 // check permissions 688 error = node->CheckPermissions(ACCESS_W); 689 // size 690 if (error == B_OK && (mask & B_STAT_SIZE)) 691 error = node->SetSize(st->st_size); 692 if (error == B_OK) { 693 // permissions 694 if (mask & B_STAT_MODE) { 695 node->SetMode(node->GetMode() & ~S_IUMSK 696 | st->st_mode & S_IUMSK); 697 } 698 // UID 699 if (mask & B_STAT_UID) 700 node->SetUID(st->st_uid); 701 // GID 702 if (mask & B_STAT_GID) 703 node->SetGID(st->st_gid); 704 // mtime 705 if (mask & B_STAT_MODIFICATION_TIME) 706 node->SetMTime(st->st_mtime); 707 // crtime 708 if (mask & B_STAT_CREATION_TIME) 709 node->SetCrTime(st->st_crtime); 710 } 711 // notify listeners 712 if (error == B_OK) 713 notify_if_stat_changed(volume, node); 714 } else 715 SET_ERROR(error, B_ERROR); 716 RETURN_ERROR(error); 717 } 718 719 720 // #pragma mark - Files 721 722 723 // FileCookie 724 class FileCookie { 725 public: 726 FileCookie(int openMode) : fOpenMode(openMode), fLastNotificationTime(0) {} 727 728 inline int GetOpenMode() { return fOpenMode; } 729 730 inline bigtime_t GetLastNotificationTime() 731 { return fLastNotificationTime; } 732 733 inline bool NotificationIntervalElapsed(bool set = false) 734 { 735 bigtime_t currentTime = system_time(); 736 bool result = (currentTime - fLastNotificationTime 737 > kNotificationInterval); 738 if (set && result) 739 fLastNotificationTime = currentTime; 740 return result; 741 } 742 743 private: 744 int fOpenMode; 745 bigtime_t fLastNotificationTime; 746 }; 747 748 // ramfs_create 749 static status_t 750 ramfs_create(fs_volume fs, fs_vnode _dir, const char *name, int openMode, 751 int mode, fs_cookie *_cookie, ino_t *vnid) 752 { 753 // FUNCTION_START(); 754 FUNCTION(("name: `%s', open mode: %x, mode: %x\n", name, openMode, mode)); 755 Volume *volume = (Volume*)fs; 756 Directory *dir = dynamic_cast<Directory*>((Node*)_dir); 757 status_t error = B_OK; 758 // check name 759 if (!name || *name == '\0') { 760 SET_ERROR(error, B_BAD_VALUE); 761 // check directory 762 } else if (!dir) { 763 SET_ERROR(error, B_BAD_VALUE); 764 } else if (VolumeWriteLocker locker = volume) { 765 NodeMTimeUpdater mTimeUpdater(dir); 766 // directory deleted? 767 bool removed; 768 if (get_vnode_removed(volume->GetID(), dir->GetID(), &removed) != B_OK 769 || removed) { 770 SET_ERROR(error, B_NOT_ALLOWED); 771 } 772 // create the file cookie 773 FileCookie *cookie = NULL; 774 if (error == B_OK) { 775 cookie = new(nothrow) FileCookie(openMode); 776 if (!cookie) 777 SET_ERROR(error, B_NO_MEMORY); 778 } 779 Node *node = NULL; 780 if (error == B_OK) { 781 // check if entry does already exist 782 if (dir->FindNode(name, &node) == B_OK) { 783 // entry does already exist 784 // fail, if we shall fail, when the file exists 785 if (openMode & O_EXCL) { 786 SET_ERROR(error, B_FILE_EXISTS); 787 // don't create a file over an existing directory or symlink 788 } else if (!node->IsFile()) { 789 SET_ERROR(error, B_NOT_ALLOWED); 790 // the user must have write permission for an existing entry 791 } else if ((error = node->CheckPermissions(ACCESS_W)) 792 == B_OK) { 793 // truncate, if requested 794 if (openMode & O_TRUNC) 795 error = node->SetSize(0); 796 // we ignore the supplied permissions in this case 797 // get vnode 798 if (error == B_OK) { 799 *vnid = node->GetID(); 800 error = volume->GetVNode(node->GetID(), &node); 801 } 802 } 803 // the user must have dir write permission to create a new entry 804 } else if ((error = dir->CheckPermissions(ACCESS_W)) == B_OK) { 805 // entry doesn't exist: create a file 806 File *file = NULL; 807 error = dir->CreateFile(name, &file); 808 if (error == B_OK) { 809 node = file; 810 *vnid = node->GetID(); 811 // set permissions, owner and group 812 node->SetMode(mode); 813 node->SetUID(geteuid()); 814 node->SetGID(getegid()); 815 } 816 } 817 // set result / cleanup on failure 818 if (error == B_OK) 819 *_cookie = cookie; 820 else if (cookie) 821 delete cookie; 822 } 823 NodeMTimeUpdater mTimeUpdater2(node); 824 // notify listeners 825 if (error == B_OK) 826 notify_entry_created(volume->GetID(), dir->GetID(), name, *vnid); 827 } else 828 SET_ERROR(error, B_ERROR); 829 RETURN_ERROR(error); 830 } 831 832 // ramfs_open 833 static status_t 834 ramfs_open(fs_volume fs, fs_vnode _node, int openMode, fs_cookie *_cookie) 835 { 836 // FUNCTION_START(); 837 Volume *volume = (Volume*)fs; 838 Node *node = (Node*)_node; 839 FUNCTION(("node: %Ld\n", node->GetID())); 840 status_t error = B_OK; 841 if (VolumeReadLocker locker = volume) { 842 // directory can be opened read-only 843 if (node->IsDirectory() && (openMode & O_RWMASK)) 844 openMode &= ~O_RWMASK; 845 int accessMode = open_mode_to_access(openMode); 846 // truncating requires write permission 847 if (error == B_OK && (openMode & O_TRUNC)) 848 accessMode |= ACCESS_W; 849 // check open mode against permissions 850 if (error == B_OK) 851 error = node->CheckPermissions(accessMode); 852 // create the cookie 853 FileCookie *cookie = NULL; 854 if (error == B_OK) { 855 cookie = new(nothrow) FileCookie(openMode); 856 if (!cookie) 857 SET_ERROR(error, B_NO_MEMORY); 858 } 859 // truncate if requested 860 if (error == B_OK && (openMode & O_TRUNC)) 861 error = node->SetSize(0); 862 NodeMTimeUpdater mTimeUpdater(node); 863 // set result / cleanup on failure 864 if (error == B_OK) 865 *_cookie = cookie; 866 else if (cookie) 867 delete cookie; 868 } else 869 SET_ERROR(error, B_ERROR); 870 RETURN_ERROR(error); 871 } 872 873 // ramfs_close 874 static status_t 875 ramfs_close(fs_volume fs, fs_vnode _node, fs_cookie /*cookie*/) 876 { 877 // FUNCTION_START(); 878 Volume *volume = (Volume*)fs; 879 Node *node = (Node*)_node; 880 FUNCTION(("node: %Ld\n", node->GetID())); 881 status_t error = B_OK; 882 // notify listeners 883 if (VolumeReadLocker locker = volume) { 884 notify_if_stat_changed(volume, node); 885 } else 886 SET_ERROR(error, B_ERROR); 887 return B_OK; 888 889 } 890 891 // ramfs_free_cookie 892 static status_t 893 ramfs_free_cookie(fs_volume /*fs*/, fs_vnode /*_node*/, fs_cookie _cookie) 894 { 895 FUNCTION_START(); 896 FileCookie *cookie = (FileCookie*)_cookie; 897 delete cookie; 898 return B_OK; 899 } 900 901 // ramfs_read 902 static status_t 903 ramfs_read(fs_volume fs, fs_vnode _node, fs_cookie _cookie, off_t pos, 904 void *buffer, size_t *bufferSize) 905 { 906 // FUNCTION_START(); 907 Volume *volume = (Volume*)fs; 908 Node *node = (Node*)_node; 909 FileCookie *cookie = (FileCookie*)_cookie; 910 // FUNCTION(("((%lu, %lu), %Ld, %p, %lu)\n", node->GetDirID(), 911 // node->GetObjectID(), pos, buffer, *bufferSize)); 912 status_t error = B_OK; 913 if (VolumeReadLocker locker = volume) { 914 // don't read anything but files 915 if (!node->IsFile()) 916 SET_ERROR(error, B_BAD_VALUE); 917 // check, if reading is allowed 918 int rwMode = cookie->GetOpenMode() & O_RWMASK; 919 if (error == B_OK && rwMode != O_RDONLY && rwMode != O_RDWR) 920 SET_ERROR(error, B_FILE_ERROR); 921 // read 922 if (error == B_OK) { 923 if (File *file = dynamic_cast<File*>(node)) 924 error = file->ReadAt(pos, buffer, *bufferSize, bufferSize); 925 else { 926 FATAL(("Node %Ld pretends to be a File, but isn't!\n", 927 node->GetID())); 928 error = B_BAD_VALUE; 929 } 930 } 931 } else 932 SET_ERROR(error, B_ERROR); 933 RETURN_ERROR(error); 934 } 935 936 // ramfs_write 937 static status_t 938 ramfs_write(fs_volume fs, fs_vnode _node, fs_cookie _cookie, off_t pos, 939 const void *buffer, size_t *bufferSize) 940 { 941 // FUNCTION_START(); 942 Volume *volume = (Volume*)fs; 943 Node *node = (Node*)_node; 944 FileCookie *cookie = (FileCookie*)_cookie; 945 // FUNCTION(("((%lu, %lu), %Ld, %p, %lu)\n", node->GetDirID(), 946 // node->GetObjectID(), pos, buffer, *bufferSize)); 947 status_t error = B_OK; 948 if (VolumeWriteLocker locker = volume) { 949 // don't write anything but files 950 if (!node->IsFile()) 951 SET_ERROR(error, B_BAD_VALUE); 952 if (error == B_OK) { 953 // check, if reading is allowed 954 int rwMode = cookie->GetOpenMode() & O_RWMASK; 955 if (error == B_OK && rwMode != O_WRONLY && rwMode != O_RDWR) 956 SET_ERROR(error, B_FILE_ERROR); 957 if (error == B_OK) { 958 // reset the position, if opened in append mode 959 if (cookie->GetOpenMode() & O_APPEND) 960 pos = node->GetSize(); 961 // write 962 if (File *file = dynamic_cast<File*>(node)) { 963 error = file->WriteAt(pos, buffer, *bufferSize, 964 bufferSize); 965 } else { 966 FATAL(("Node %Ld pretends to be a File, but isn't!\n", 967 node->GetID())); 968 error = B_BAD_VALUE; 969 } 970 } 971 } 972 // notify listeners 973 if (error == B_OK && cookie->NotificationIntervalElapsed(true)) 974 notify_if_stat_changed(volume, node); 975 NodeMTimeUpdater mTimeUpdater(node); 976 } else 977 SET_ERROR(error, B_ERROR); 978 RETURN_ERROR(error); 979 } 980 981 982 // #pragma mark - Directories 983 984 985 // DirectoryCookie 986 class DirectoryCookie { 987 public: 988 DirectoryCookie(Directory *directory = NULL) 989 : fIterator(directory), 990 fDotIndex(DOT_INDEX), 991 // debugging 992 fIteratorID(atomic_add(&fNextIteratorID, 1)), 993 fGetNextCounter(0) 994 { 995 } 996 997 void Unset() { fIterator.Unset(); } 998 999 // EntryIterator *GetIterator() const { return &fIterator; } 1000 1001 status_t GetNext(ino_t *nodeID, const char **entryName) 1002 { 1003 fGetNextCounter++; 1004 status_t error = B_OK; 1005 if (fDotIndex == DOT_INDEX) { 1006 // "." 1007 Node *entry = fIterator.GetDirectory(); 1008 *nodeID = entry->GetID(); 1009 *entryName = "."; 1010 fDotIndex++; 1011 } else if (fDotIndex == DOT_DOT_INDEX) { 1012 // ".." 1013 Directory *dir = fIterator.GetDirectory(); 1014 if (dir->GetParent()) 1015 *nodeID = dir->GetParent()->GetID(); 1016 else 1017 *nodeID = dir->GetID(); 1018 *entryName = ".."; 1019 fDotIndex++; 1020 } else { 1021 // ordinary entries 1022 Entry *entry = NULL; 1023 error = fIterator.GetNext(&entry); 1024 if (error == B_OK) { 1025 *nodeID = entry->GetNode()->GetID(); 1026 *entryName = entry->GetName(); 1027 } 1028 } 1029 PRINT(("EntryIterator %ld, GetNext() counter: %ld, entry: %p (%Ld)\n", 1030 fIteratorID, fGetNextCounter, fIterator.GetCurrent(), 1031 (fIterator.GetCurrent() ? fIterator.GetCurrent()->GetNode()->GetID() : -1))); 1032 return error; 1033 } 1034 1035 status_t Rewind() 1036 { 1037 fDotIndex = DOT_INDEX; 1038 return fIterator.Rewind(); 1039 } 1040 1041 status_t Suspend() { return fIterator.Suspend(); } 1042 status_t Resume() { return fIterator.Resume(); } 1043 1044 private: 1045 enum { 1046 DOT_INDEX = 0, 1047 DOT_DOT_INDEX = 1, 1048 ENTRY_INDEX = 2, 1049 }; 1050 1051 private: 1052 EntryIterator fIterator; 1053 uint32 fDotIndex; 1054 1055 // debugging 1056 int32 fIteratorID; 1057 int32 fGetNextCounter; 1058 static vint32 fNextIteratorID; 1059 }; 1060 vint32 DirectoryCookie::fNextIteratorID = 0; 1061 1062 // ramfs_create_dir 1063 static status_t 1064 ramfs_create_dir(fs_volume fs, fs_vnode _dir, const char *name, int mode) 1065 { 1066 FUNCTION(("name: `%s', mode: %x\n", name, mode)); 1067 Volume *volume = (Volume*)fs; 1068 Directory *dir = dynamic_cast<Directory*>((Node*)_dir); 1069 status_t error = B_OK; 1070 // check name 1071 if (!name || *name == '\0') { 1072 SET_ERROR(error, B_BAD_VALUE); 1073 // check directory 1074 } else if (!dir) { 1075 SET_ERROR(error, B_BAD_VALUE); 1076 } else if (VolumeWriteLocker locker = volume) { 1077 NodeMTimeUpdater mTimeUpdater(dir); 1078 // directory deleted? 1079 bool removed; 1080 if (get_vnode_removed(volume->GetID(), dir->GetID(), &removed) != B_OK 1081 || removed) { 1082 SET_ERROR(error, B_NOT_ALLOWED); 1083 } 1084 // check directory write permissions 1085 error = dir->CheckPermissions(ACCESS_W); 1086 Node *node = NULL; 1087 if (error == B_OK) { 1088 // check if entry does already exist 1089 if (dir->FindNode(name, &node) == B_OK) { 1090 SET_ERROR(error, B_FILE_EXISTS); 1091 } else { 1092 // entry doesn't exist: create a directory 1093 Directory *newDir = NULL; 1094 error = dir->CreateDirectory(name, &newDir); 1095 if (error == B_OK) { 1096 node = newDir; 1097 // set permissions, owner and group 1098 node->SetMode(mode); 1099 node->SetUID(geteuid()); 1100 node->SetGID(getegid()); 1101 // put the node 1102 volume->PutVNode(node->GetID()); 1103 } 1104 } 1105 } 1106 NodeMTimeUpdater mTimeUpdater2(node); 1107 // notify listeners 1108 if (error == B_OK) { 1109 notify_entry_created(volume->GetID(), dir->GetID(), name, 1110 node->GetID()); 1111 } 1112 } else 1113 SET_ERROR(error, B_ERROR); 1114 RETURN_ERROR(error); 1115 } 1116 1117 // ramfs_remove_dir 1118 static status_t 1119 ramfs_remove_dir(fs_volume fs, fs_vnode _dir, const char *name) 1120 { 1121 FUNCTION(("name: `%s'\n", name)); 1122 Volume *volume = (Volume*)fs; 1123 Directory *dir = dynamic_cast<Directory*>((Node*)_dir); 1124 status_t error = B_OK; 1125 // check name 1126 if (!name || *name == '\0' || !strcmp(name, ".") || !strcmp(name, "..")) { 1127 SET_ERROR(error, B_BAD_VALUE); 1128 // check node 1129 } else if (!dir) { 1130 SET_ERROR(error, B_BAD_VALUE); 1131 } else if (VolumeWriteLocker locker = volume) { 1132 NodeMTimeUpdater mTimeUpdater(dir); 1133 // check directory write permissions 1134 error = dir->CheckPermissions(ACCESS_W); 1135 ino_t nodeID = -1; 1136 if (error == B_OK) { 1137 // check if entry exists 1138 Node *node = NULL; 1139 Entry *entry = NULL; 1140 if (dir->FindAndGetNode(name, &node, &entry) == B_OK) { 1141 nodeID = node->GetID(); 1142 if (!node->IsDirectory()) { 1143 SET_ERROR(error, B_NOT_A_DIRECTORY); 1144 } else if (!dynamic_cast<Directory*>(node)->IsEmpty()) { 1145 SET_ERROR(error, B_DIRECTORY_NOT_EMPTY); 1146 } else 1147 error = dir->DeleteEntry(entry); 1148 volume->PutVNode(node); 1149 } else 1150 SET_ERROR(error, B_ENTRY_NOT_FOUND); 1151 } 1152 // notify listeners 1153 if (error == B_OK) 1154 notify_entry_removed(volume->GetID(), dir->GetID(), name, nodeID); 1155 } else 1156 SET_ERROR(error, B_ERROR); 1157 RETURN_ERROR(error); 1158 } 1159 1160 // ramfs_open_dir 1161 static status_t 1162 ramfs_open_dir(fs_volume /*fs*/, fs_vnode _node, fs_cookie *_cookie) 1163 { 1164 // FUNCTION_START(); 1165 // Volume *volume = (Volume*)fs; 1166 Node *node = (Node*)_node; 1167 FUNCTION(("dir: (%Lu)\n", node->GetID())); 1168 // get the Directory 1169 status_t error = (node->IsDirectory() ? B_OK : B_NOT_A_DIRECTORY); 1170 Directory *dir = NULL; 1171 if (error == B_OK) { 1172 dir = dynamic_cast<Directory*>(node); 1173 if (!dir) { 1174 FATAL(("Node %Ld pretends to be a Directory, but isn't!\n", 1175 node->GetID())); 1176 error = B_NOT_A_DIRECTORY; 1177 } 1178 } 1179 // create a DirectoryCookie 1180 if (error == B_OK) { 1181 DirectoryCookie *cookie = new(nothrow) DirectoryCookie(dir); 1182 if (cookie) { 1183 error = cookie->Suspend(); 1184 if (error == B_OK) 1185 *_cookie = cookie; 1186 else 1187 delete cookie; 1188 } else 1189 SET_ERROR(error, B_NO_MEMORY); 1190 } 1191 FUNCTION_END(); 1192 RETURN_ERROR(error); 1193 } 1194 1195 // ramfs_close_dir 1196 static status_t 1197 ramfs_close_dir(fs_volume /*fs*/, fs_vnode DARG(_node), fs_cookie _cookie) 1198 { 1199 FUNCTION_START(); 1200 FUNCTION(("dir: (%Lu)\n", ((Node*)_node)->GetID())); 1201 // No locking needed, since the Directory is guaranteed to live at this 1202 // time and for iterators there is a separate locking. 1203 DirectoryCookie *cookie = (DirectoryCookie*)_cookie; 1204 cookie->Unset(); 1205 return B_OK; 1206 } 1207 1208 // ramfs_free_dir_cookie 1209 static status_t 1210 ramfs_free_dir_cookie(fs_volume /*fs*/, fs_vnode /*_node*/, fs_cookie _cookie) 1211 { 1212 FUNCTION_START(); 1213 DirectoryCookie *cookie = (DirectoryCookie*)_cookie; 1214 delete cookie; 1215 return B_OK; 1216 } 1217 1218 // ramfs_read_dir 1219 static status_t 1220 ramfs_read_dir(fs_volume fs, fs_vnode DARG(_node), fs_cookie _cookie, 1221 struct dirent *buffer, size_t bufferSize, uint32 *count) 1222 { 1223 FUNCTION_START(); 1224 Volume *volume = (Volume*)fs; 1225 DARG(Node *node = (Node*)_node; ) 1226 FUNCTION(("dir: (%Lu)\n", node->GetID())); 1227 DirectoryCookie *cookie = (DirectoryCookie*)_cookie; 1228 status_t error = B_OK; 1229 if (VolumeReadLocker locker = volume) { 1230 error = cookie->Resume(); 1231 if (error == B_OK) { 1232 // read one entry 1233 ino_t nodeID = -1; 1234 const char *name = NULL; 1235 if (cookie->GetNext(&nodeID, &name) == B_OK) { 1236 PRINT((" entry: `%s'\n", name)); 1237 size_t nameLen = strlen(name); 1238 // check, whether the entry fits into the buffer, 1239 // and fill it in 1240 size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer; 1241 if (length <= bufferSize) { 1242 buffer->d_dev = volume->GetID(); 1243 buffer->d_ino = nodeID; 1244 memcpy(buffer->d_name, name, nameLen); 1245 buffer->d_name[nameLen] = '\0'; 1246 buffer->d_reclen = length; 1247 *count = 1; 1248 } else { 1249 SET_ERROR(error, B_BUFFER_OVERFLOW); 1250 } 1251 } else 1252 *count = 0; 1253 cookie->Suspend(); 1254 } 1255 } else 1256 SET_ERROR(error, B_ERROR); 1257 RETURN_ERROR(error); 1258 } 1259 1260 // ramfs_rewind_dir 1261 static status_t 1262 ramfs_rewind_dir(fs_volume /*fs*/, fs_vnode /*_node*/, fs_cookie _cookie) 1263 { 1264 FUNCTION_START(); 1265 // No locking needed, since the Directory is guaranteed to live at this 1266 // time and for iterators there is a separate locking. 1267 DirectoryCookie *cookie = (DirectoryCookie*)_cookie; 1268 // no need to Resume(), iterator remains suspended 1269 status_t error = cookie->Rewind(); 1270 RETURN_ERROR(error); 1271 } 1272 1273 1274 // #pragma mark - Attribute Directories 1275 1276 1277 // ramfs_open_attr_dir 1278 static status_t 1279 ramfs_open_attr_dir(fs_volume fs, fs_vnode _node, fs_cookie *cookie) 1280 { 1281 FUNCTION_START(); 1282 Volume *volume = (Volume*)fs; 1283 Node *node = (Node*)_node; 1284 status_t error = B_OK; 1285 if (VolumeReadLocker locker = volume) { 1286 // check permissions 1287 error = node->CheckPermissions(ACCESS_R); 1288 // create iterator 1289 AttributeIterator *iterator = NULL; 1290 if (error == B_OK) { 1291 iterator = new(nothrow) AttributeIterator(node); 1292 if (iterator) 1293 error = iterator->Suspend(); 1294 else 1295 SET_ERROR(error, B_NO_MEMORY); 1296 } 1297 // set result / cleanup on failure 1298 if (error == B_OK) 1299 *cookie = iterator; 1300 else 1301 delete iterator; 1302 } else 1303 SET_ERROR(error, B_ERROR); 1304 RETURN_ERROR(error); 1305 } 1306 1307 // ramfs_close_attr_dir 1308 static status_t 1309 ramfs_close_attr_dir(fs_volume /*fs*/, fs_vnode /*_node*/, fs_cookie cookie) 1310 { 1311 FUNCTION_START(); 1312 // No locking needed, since the Node is guaranteed to live at this time 1313 // and for iterators there is a separate locking. 1314 AttributeIterator *iterator = (AttributeIterator*)cookie; 1315 iterator->Unset(); 1316 return B_OK; 1317 } 1318 1319 // ramfs_free_attr_dir_cookie 1320 static status_t 1321 ramfs_free_attr_dir_cookie(fs_volume /*fs*/, fs_vnode /*_node*/, 1322 fs_cookie cookie) 1323 { 1324 FUNCTION_START(); 1325 // No locking needed, since the Node is guaranteed to live at this time 1326 // and for iterators there is a separate locking. 1327 AttributeIterator *iterator = (AttributeIterator*)cookie; 1328 delete iterator; 1329 return B_OK; 1330 } 1331 1332 // ramfs_read_attr_dir 1333 static status_t 1334 ramfs_read_attr_dir(fs_volume fs, fs_vnode _node, fs_cookie cookie, 1335 struct dirent *buffer, size_t bufferSize, uint32 *count) 1336 { 1337 FUNCTION_START(); 1338 Volume *volume = (Volume*)fs; 1339 AttributeIterator *iterator = (AttributeIterator*)cookie; 1340 status_t error = B_OK; 1341 if (VolumeReadLocker locker = volume) { 1342 error = iterator->Resume(); 1343 if (error == B_OK) { 1344 // get next attribute 1345 Attribute *attribute = NULL; 1346 if (iterator->GetNext(&attribute) == B_OK) { 1347 const char *name = attribute->GetName(); 1348 size_t nameLen = strlen(name); 1349 // check, whether the entry fits into the buffer, 1350 // and fill it in 1351 size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer; 1352 if (length <= bufferSize) { 1353 buffer->d_dev = volume->GetID(); 1354 buffer->d_ino = -1; // attributes don't have a node ID 1355 memcpy(buffer->d_name, name, nameLen); 1356 buffer->d_name[nameLen] = '\0'; 1357 buffer->d_reclen = length; 1358 *count = 1; 1359 } else { 1360 SET_ERROR(error, B_BUFFER_OVERFLOW); 1361 } 1362 } else 1363 *count = 0; 1364 iterator->Suspend(); 1365 } 1366 } else 1367 SET_ERROR(error, B_ERROR); 1368 RETURN_ERROR(error); 1369 } 1370 1371 // ramfs_rewind_attr_dir 1372 static status_t 1373 ramfs_rewind_attr_dir(fs_volume /*fs*/, fs_vnode /*_node*/, fs_cookie cookie) 1374 { 1375 FUNCTION_START(); 1376 // No locking needed, since the Node is guaranteed to live at this time 1377 // and for iterators there is a separate locking. 1378 AttributeIterator *iterator = (AttributeIterator*)cookie; 1379 // no need to Resume(), iterator remains suspended 1380 status_t error = iterator->Rewind(); 1381 RETURN_ERROR(error); 1382 } 1383 1384 1385 // #pragma mark - Attributes 1386 1387 1388 // AttributeCookie 1389 class AttributeCookie { 1390 public: 1391 AttributeCookie() : fOpenMode(0), fLastNotificationTime(0) {} 1392 1393 status_t Init(const char* name, int openMode) 1394 { 1395 if (!fName.SetTo(name)) 1396 return B_NO_MEMORY; 1397 fOpenMode = openMode; 1398 1399 return B_OK; 1400 } 1401 1402 inline const char* GetName() const { return fName.GetString(); } 1403 1404 inline int GetOpenMode() const { return fOpenMode; } 1405 1406 inline bigtime_t GetLastNotificationTime() const 1407 { return fLastNotificationTime; } 1408 1409 inline bool NotificationIntervalElapsed(bool set = false) 1410 { 1411 bigtime_t currentTime = system_time(); 1412 bool result = (currentTime - fLastNotificationTime 1413 > kNotificationInterval); 1414 if (set && result) 1415 fLastNotificationTime = currentTime; 1416 return result; 1417 } 1418 1419 private: 1420 String fName; 1421 int fOpenMode; 1422 bigtime_t fLastNotificationTime; 1423 }; 1424 1425 // ramfs_create_attr 1426 static status_t 1427 ramfs_create_attr(fs_volume fs, fs_vnode _node, const char *name, uint32 type, 1428 int openMode, fs_cookie *_cookie) 1429 { 1430 1431 Volume *volume = (Volume*)fs; 1432 Node *node = (Node*)_node; 1433 1434 if (VolumeWriteLocker locker = volume) { 1435 // try to find the attribute 1436 Attribute *attribute = NULL; 1437 node->FindAttribute(name, &attribute); 1438 1439 // in case the attribute exists we fail if required by the openMode 1440 if (attribute && (openMode & O_EXCL)) 1441 RETURN_ERROR(B_FILE_EXISTS); 1442 1443 // creating and truncating require write permission 1444 int accessMode = open_mode_to_access(openMode); 1445 if (!attribute || (openMode & O_TRUNC)) 1446 accessMode |= ACCESS_W; 1447 1448 // check required permissions against node permissions 1449 status_t error = node->CheckPermissions(accessMode); 1450 if (error != B_OK) 1451 RETURN_ERROR(error); 1452 1453 // create the cookie 1454 AttributeCookie *cookie = new(nothrow) AttributeCookie(); 1455 if (!cookie) 1456 return B_NO_MEMORY; 1457 ObjectDeleter<AttributeCookie> cookieDeleter(cookie); 1458 1459 // init the cookie 1460 error = cookie->Init(name, openMode); 1461 if (error != B_OK) 1462 RETURN_ERROR(error); 1463 1464 // if not existing yet, create the attribute and set its type 1465 if (!attribute) { 1466 error = node->CreateAttribute(name, &attribute); 1467 if (error != B_OK) 1468 RETURN_ERROR(error); 1469 1470 attribute->SetType(type); 1471 1472 notify_attribute_changed(volume->GetID(), node->GetID(), name, 1473 B_ATTR_CREATED); 1474 1475 // else truncate if requested 1476 } else if (openMode & O_TRUNC) { 1477 error = attribute->SetSize(0); 1478 if (error != B_OK) 1479 return error; 1480 1481 notify_attribute_changed(volume->GetID(), node->GetID(), name, 1482 B_ATTR_CHANGED); 1483 } 1484 NodeMTimeUpdater mTimeUpdater(node); 1485 1486 // success 1487 cookieDeleter.Detach(); 1488 *_cookie = cookie; 1489 } else 1490 RETURN_ERROR(B_ERROR); 1491 1492 return B_OK; 1493 } 1494 1495 // ramfs_open_attr 1496 static status_t 1497 ramfs_open_attr(fs_volume fs, fs_vnode _node, const char *name, int openMode, 1498 fs_cookie *_cookie) 1499 { 1500 // FUNCTION_START(); 1501 Volume *volume = (Volume*)fs; 1502 Node *node = (Node*)_node; 1503 FUNCTION(("node: %lld\n", node->GetID())); 1504 status_t error = B_OK; 1505 1506 if (VolumeWriteLocker locker = volume) { 1507 // find the attribute 1508 Attribute *attribute = NULL; 1509 if (error == B_OK) 1510 error = node->FindAttribute(name, &attribute); 1511 1512 // truncating requires write permission 1513 int accessMode = open_mode_to_access(openMode); 1514 if (error == B_OK && (openMode & O_TRUNC)) 1515 accessMode |= ACCESS_W; 1516 1517 // check open mode against permissions 1518 if (error == B_OK) 1519 error = node->CheckPermissions(accessMode); 1520 1521 // create the cookie 1522 AttributeCookie *cookie = NULL; 1523 if (error == B_OK) { 1524 cookie = new(nothrow) AttributeCookie(); 1525 if (cookie) { 1526 SET_ERROR(error, cookie->Init(name, openMode)); 1527 } else { 1528 SET_ERROR(error, B_NO_MEMORY); 1529 } 1530 } 1531 1532 // truncate if requested 1533 if (error == B_OK && (openMode & O_TRUNC)) { 1534 error = attribute->SetSize(0); 1535 1536 if (error == B_OK) { 1537 notify_attribute_changed(volume->GetID(), node->GetID(), name, 1538 B_ATTR_CHANGED); 1539 } 1540 } 1541 NodeMTimeUpdater mTimeUpdater(node); 1542 1543 // set result / cleanup on failure 1544 if (error == B_OK) 1545 *_cookie = cookie; 1546 else if (cookie) 1547 delete cookie; 1548 } else 1549 SET_ERROR(error, B_ERROR); 1550 RETURN_ERROR(error); 1551 } 1552 1553 // ramfs_close_attr 1554 static status_t 1555 ramfs_close_attr(fs_volume fs, fs_vnode _node, fs_cookie cookie) 1556 { 1557 // FUNCTION_START(); 1558 Volume *volume = (Volume*)fs; 1559 Node *node = (Node*)_node; 1560 FUNCTION(("node: %lld\n", node->GetID())); 1561 status_t error = B_OK; 1562 1563 // notify listeners 1564 if (VolumeReadLocker locker = volume) { 1565 notify_if_stat_changed(volume, node); 1566 } else 1567 SET_ERROR(error, B_ERROR); 1568 return B_OK; 1569 1570 } 1571 1572 // ramfs_free_attr_cookie 1573 static status_t 1574 ramfs_free_attr_cookie(fs_volume /*fs*/, fs_vnode /*_node*/, fs_cookie _cookie) 1575 { 1576 FUNCTION_START(); 1577 AttributeCookie *cookie = (AttributeCookie*)_cookie; 1578 delete cookie; 1579 return B_OK; 1580 } 1581 1582 // ramfs_read_attr 1583 static status_t 1584 ramfs_read_attr(fs_volume fs, fs_vnode _node, fs_cookie _cookie, off_t pos, 1585 void *buffer, size_t *bufferSize) 1586 { 1587 // FUNCTION_START(); 1588 Volume *volume = (Volume*)fs; 1589 Node *node = (Node*)_node; 1590 AttributeCookie *cookie = (AttributeCookie*)_cookie; 1591 status_t error = B_OK; 1592 1593 if (VolumeReadLocker locker = volume) { 1594 // find the attribute 1595 Attribute *attribute = NULL; 1596 if (error == B_OK) 1597 error = node->FindAttribute(cookie->GetName(), &attribute); 1598 1599 // check permissions 1600 int accessMode = open_mode_to_access(cookie->GetOpenMode()); 1601 if (error == B_OK && !(accessMode & ACCESS_R)) 1602 SET_ERROR(error, B_NOT_ALLOWED); 1603 1604 // read 1605 if (error == B_OK) 1606 error = attribute->ReadAt(pos, buffer, *bufferSize, bufferSize); 1607 } else 1608 SET_ERROR(error, B_ERROR); 1609 RETURN_ERROR(error); 1610 } 1611 1612 // ramfs_write_attr 1613 static status_t 1614 ramfs_write_attr(fs_volume fs, fs_vnode _node, fs_cookie _cookie, off_t pos, 1615 const void *buffer, size_t *bufferSize) 1616 { 1617 // FUNCTION_START(); 1618 Volume *volume = (Volume*)fs; 1619 Node *node = (Node*)_node; 1620 AttributeCookie *cookie = (AttributeCookie*)_cookie; 1621 status_t error = B_OK; 1622 1623 // Don't allow writing the reserved attributes. 1624 const char *name = cookie->GetName(); 1625 if (name[0] == '\0' || !strcmp(name, "name") 1626 || !strcmp(name, "last_modified") || !strcmp(name, "size")) { 1627 //FUNCTION(("failed: node: %s, attribute: %s\n", node->GetName(), name)); 1628 RETURN_ERROR(B_NOT_ALLOWED); 1629 } 1630 1631 if (VolumeWriteLocker locker = volume) { 1632 NodeMTimeUpdater mTimeUpdater(node); 1633 1634 // find the attribute 1635 Attribute *attribute = NULL; 1636 if (error == B_OK) 1637 error = node->FindAttribute(cookie->GetName(), &attribute); 1638 1639 // check permissions 1640 int accessMode = open_mode_to_access(cookie->GetOpenMode()); 1641 if (error == B_OK && !(accessMode & ACCESS_W)) 1642 SET_ERROR(error, B_NOT_ALLOWED); 1643 1644 // write the data 1645 if (error == B_OK) 1646 error = attribute->WriteAt(pos, buffer, *bufferSize, bufferSize); 1647 1648 // notify listeners 1649 if (error == B_OK) { 1650 notify_attribute_changed(volume->GetID(), node->GetID(), name, 1651 B_ATTR_CHANGED); 1652 } 1653 } else 1654 SET_ERROR(error, B_ERROR); 1655 RETURN_ERROR(error); 1656 } 1657 1658 // ramfs_read_attr_stat 1659 static status_t 1660 ramfs_read_attr_stat(fs_volume fs, fs_vnode _node, fs_cookie _cookie, 1661 struct stat *st) 1662 { 1663 // FUNCTION_START(); 1664 Volume *volume = (Volume*)fs; 1665 Node *node = (Node*)_node; 1666 AttributeCookie *cookie = (AttributeCookie*)_cookie; 1667 status_t error = B_OK; 1668 1669 if (VolumeReadLocker locker = volume) { 1670 // find the attribute 1671 Attribute *attribute = NULL; 1672 if (error == B_OK) 1673 error = node->FindAttribute(cookie->GetName(), &attribute); 1674 1675 // check permissions 1676 int accessMode = open_mode_to_access(cookie->GetOpenMode()); 1677 if (error == B_OK && !(accessMode & ACCESS_R)) 1678 SET_ERROR(error, B_NOT_ALLOWED); 1679 1680 // read 1681 if (error == B_OK) { 1682 st->st_type = attribute->GetType(); 1683 st->st_size = attribute->GetSize(); 1684 } 1685 } else 1686 SET_ERROR(error, B_ERROR); 1687 RETURN_ERROR(error); 1688 } 1689 1690 // ramfs_rename_attr 1691 static status_t 1692 ramfs_rename_attr(fs_volume /*fs*/, fs_vnode /*_fromNode*/, 1693 const char */*fromName*/, fs_vnode /*_toNode*/, const char */*toName*/) 1694 { 1695 // TODO:... 1696 return B_BAD_VALUE; 1697 } 1698 1699 // ramfs_remove_attr 1700 static status_t 1701 ramfs_remove_attr(fs_volume fs, fs_vnode _node, const char *name) 1702 1703 { 1704 FUNCTION_START(); 1705 Volume *volume = (Volume*)fs; 1706 Node *node = (Node*)_node; 1707 status_t error = B_OK; 1708 if (VolumeWriteLocker locker = volume) { 1709 NodeMTimeUpdater mTimeUpdater(node); 1710 1711 // check permissions 1712 error = node->CheckPermissions(ACCESS_W); 1713 1714 // find the attribute 1715 Attribute *attribute = NULL; 1716 if (error == B_OK) 1717 error = node->FindAttribute(name, &attribute); 1718 1719 // delete it 1720 if (error == B_OK) 1721 error = node->DeleteAttribute(attribute); 1722 1723 // notify listeners 1724 if (error == B_OK) { 1725 notify_attribute_changed(volume->GetID(), node->GetID(), name, 1726 B_ATTR_REMOVED); 1727 } 1728 } else 1729 SET_ERROR(error, B_ERROR); 1730 RETURN_ERROR(error); 1731 } 1732 1733 1734 // #pragma mark - Indices 1735 1736 1737 // IndexDirCookie 1738 class IndexDirCookie { 1739 public: 1740 IndexDirCookie() : index_index(0) {} 1741 1742 int32 index_index; 1743 }; 1744 1745 // ramfs_open_index_dir 1746 static status_t 1747 ramfs_open_index_dir(fs_volume fs, fs_cookie *_cookie) 1748 { 1749 FUNCTION_START(); 1750 Volume *volume = (Volume*)fs; 1751 status_t error = B_OK; 1752 if (VolumeReadLocker locker = volume) { 1753 // check whether an index directory exists 1754 if (volume->GetIndexDirectory()) { 1755 IndexDirCookie *cookie = new(nothrow) IndexDirCookie; 1756 if (cookie) 1757 *_cookie = cookie; 1758 else 1759 SET_ERROR(error, B_NO_MEMORY); 1760 } else 1761 SET_ERROR(error, B_ENTRY_NOT_FOUND); 1762 } else 1763 SET_ERROR(error, B_ERROR); 1764 RETURN_ERROR(error); 1765 } 1766 1767 // ramfs_close_index_dir 1768 static status_t 1769 ramfs_close_index_dir(fs_volume /*fs*/, fs_cookie /*_cookie*/) 1770 { 1771 FUNCTION_START(); 1772 return B_OK; 1773 } 1774 1775 // ramfs_free_index_dir_cookie 1776 static status_t 1777 ramfs_free_index_dir_cookie(fs_volume /*fs*/, fs_cookie _cookie) 1778 { 1779 FUNCTION_START(); 1780 IndexDirCookie *cookie = (IndexDirCookie*)_cookie; 1781 delete cookie; 1782 return B_OK; 1783 } 1784 1785 // ramfs_read_index_dir 1786 static status_t 1787 ramfs_read_index_dir(fs_volume fs, fs_cookie _cookie, struct dirent *buffer, 1788 size_t bufferSize, uint32 *count) 1789 { 1790 FUNCTION_START(); 1791 Volume *volume = (Volume*)fs; 1792 IndexDirCookie *cookie = (IndexDirCookie*)_cookie; 1793 status_t error = B_OK; 1794 if (VolumeReadLocker locker = volume) { 1795 // get the next index 1796 Index *index = volume->GetIndexDirectory()->IndexAt( 1797 cookie->index_index++); 1798 if (index) { 1799 const char *name = index->GetName(); 1800 size_t nameLen = strlen(name); 1801 // check, whether the entry fits into the buffer, 1802 // and fill it in 1803 size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer; 1804 if (length <= bufferSize) { 1805 buffer->d_dev = volume->GetID(); 1806 buffer->d_ino = -1; // indices don't have a node ID 1807 memcpy(buffer->d_name, name, nameLen); 1808 buffer->d_name[nameLen] = '\0'; 1809 buffer->d_reclen = length; 1810 *count = 1; 1811 } else { 1812 SET_ERROR(error, B_BUFFER_OVERFLOW); 1813 } 1814 } else 1815 *count = 0; 1816 } else 1817 SET_ERROR(error, B_ERROR); 1818 RETURN_ERROR(error); 1819 } 1820 1821 // ramfs_rewind_index_dir 1822 static status_t 1823 ramfs_rewind_index_dir(fs_volume /*fs*/, fs_cookie _cookie) 1824 { 1825 FUNCTION_START(); 1826 IndexDirCookie *cookie = (IndexDirCookie*)_cookie; 1827 cookie->index_index = 0; 1828 return B_OK; 1829 } 1830 1831 // ramfs_create_index 1832 static status_t 1833 ramfs_create_index(fs_volume fs, const char *name, uint32 type, 1834 uint32 /*flags*/) 1835 { 1836 FUNCTION_START(); 1837 Volume *volume = (Volume*)fs; 1838 status_t error = B_OK; 1839 // only root is allowed to manipulate the indices 1840 if (geteuid() != 0) { 1841 SET_ERROR(error, B_NOT_ALLOWED); 1842 } else if (VolumeWriteLocker locker = volume) { 1843 // get the index directory 1844 if (IndexDirectory *indexDir = volume->GetIndexDirectory()) { 1845 // check whether an index with that name does already exist 1846 if (indexDir->FindIndex(name)) { 1847 SET_ERROR(error, B_FILE_EXISTS); 1848 } else { 1849 // create the index 1850 AttributeIndex *index; 1851 error = indexDir->CreateIndex(name, type, &index); 1852 } 1853 } else 1854 SET_ERROR(error, B_ENTRY_NOT_FOUND); 1855 } else 1856 SET_ERROR(error, B_ERROR); 1857 RETURN_ERROR(error); 1858 } 1859 1860 // ramfs_remove_index 1861 static status_t 1862 ramfs_remove_index(fs_volume fs, const char *name) 1863 { 1864 FUNCTION_START(); 1865 Volume *volume = (Volume*)fs; 1866 status_t error = B_OK; 1867 // only root is allowed to manipulate the indices 1868 if (geteuid() != 0) { 1869 SET_ERROR(error, B_NOT_ALLOWED); 1870 } else if (VolumeWriteLocker locker = volume) { 1871 // get the index directory 1872 if (IndexDirectory *indexDir = volume->GetIndexDirectory()) { 1873 // check whether an index with that name does exist 1874 if (Index *index = indexDir->FindIndex(name)) { 1875 // don't delete a special index 1876 if (indexDir->IsSpecialIndex(index)) { 1877 SET_ERROR(error, B_BAD_VALUE); 1878 } else 1879 indexDir->DeleteIndex(index); 1880 } else 1881 SET_ERROR(error, B_ENTRY_NOT_FOUND); 1882 } else 1883 SET_ERROR(error, B_ENTRY_NOT_FOUND); 1884 } else 1885 SET_ERROR(error, B_ERROR); 1886 RETURN_ERROR(error); 1887 } 1888 1889 // ramfs_read_index_stat 1890 static status_t 1891 ramfs_read_index_stat(fs_volume fs, const char *name, struct stat *st) 1892 { 1893 FUNCTION_START(); 1894 Volume *volume = (Volume*)fs; 1895 status_t error = B_OK; 1896 if (VolumeReadLocker locker = volume) { 1897 // get the index directory 1898 if (IndexDirectory *indexDir = volume->GetIndexDirectory()) { 1899 // find the index 1900 if (Index *index = indexDir->FindIndex(name)) { 1901 st->st_type = index->GetType(); 1902 if (index->HasFixedKeyLength()) 1903 st->st_size = index->GetKeyLength(); 1904 else 1905 st->st_size = kMaxIndexKeyLength; 1906 st->st_atime = 0; // TODO: index times 1907 st->st_mtime = 0; // ... 1908 st->st_ctime = 0; // ... 1909 st->st_crtime = 0; // ... 1910 st->st_uid = 0; // root owns the indices 1911 st->st_gid = 0; // 1912 } else 1913 SET_ERROR(error, B_ENTRY_NOT_FOUND); 1914 } else 1915 SET_ERROR(error, B_ENTRY_NOT_FOUND); 1916 } else 1917 SET_ERROR(error, B_ERROR); 1918 RETURN_ERROR(error); 1919 } 1920 1921 1922 // #pragma mark - Queries 1923 1924 // Query implementation by Axel Dörfler. Slightly adjusted. 1925 1926 // ramfs_open_query 1927 static status_t 1928 ramfs_open_query(fs_volume fs, const char *queryString, uint32 flags, 1929 port_id port, uint32 token, fs_cookie *cookie) 1930 { 1931 FUNCTION_START(); 1932 PRINT(("query = \"%s\", flags = %lu, port_id = %ld, token = %ld\n", queryString, flags, port, token)); 1933 1934 Volume *volume = (Volume *)fs; 1935 1936 // lock the volume 1937 VolumeReadLocker locker(volume); 1938 if (!locker.IsLocked()) 1939 RETURN_ERROR(B_ERROR); 1940 1941 // parse the query expression 1942 Expression *expression = new Expression((char *)queryString); 1943 if (expression == NULL) 1944 RETURN_ERROR(B_NO_MEMORY); 1945 ObjectDeleter<Expression> expressionDeleter(expression); 1946 1947 if (expression->InitCheck() < B_OK) { 1948 WARN(("Could not parse query, stopped at: \"%s\"\n", 1949 expression->Position())); 1950 RETURN_ERROR(B_BAD_VALUE); 1951 } 1952 1953 // create the query 1954 Query *query = new Query(volume, expression, flags); 1955 if (query == NULL) 1956 RETURN_ERROR(B_NO_MEMORY); 1957 expressionDeleter.Detach(); 1958 // TODO: The Query references an Index, but nothing prevents the Index 1959 // from being deleted, while the Query is in existence. 1960 1961 if (flags & B_LIVE_QUERY) 1962 query->SetLiveMode(port, token); 1963 1964 *cookie = (void *)query; 1965 1966 return B_OK; 1967 } 1968 1969 // ramfs_close_query 1970 static status_t 1971 ramfs_close_query(fs_volume /*fs*/, fs_cookie /*cookie*/) 1972 { 1973 FUNCTION_START(); 1974 return B_OK; 1975 } 1976 1977 // ramfs_free_query_cookie 1978 static status_t 1979 ramfs_free_query_cookie(fs_volume fs, fs_cookie cookie) 1980 { 1981 FUNCTION_START(); 1982 1983 Volume *volume = (Volume *)fs; 1984 1985 // lock the volume 1986 VolumeReadLocker locker(volume); 1987 if (!locker.IsLocked()) 1988 RETURN_ERROR(B_ERROR); 1989 1990 Query *query = (Query *)cookie; 1991 Expression *expression = query->GetExpression(); 1992 delete query; 1993 delete expression; 1994 1995 return B_OK; 1996 } 1997 1998 // ramfs_read_query 1999 static status_t 2000 ramfs_read_query(fs_volume fs, fs_cookie cookie, struct dirent *buffer, 2001 size_t bufferSize, uint32 *count) 2002 { 2003 FUNCTION_START(); 2004 Query *query = (Query *)cookie; 2005 Volume *volume = (Volume *)fs; 2006 2007 // lock the volume 2008 VolumeReadLocker locker(volume); 2009 if (!locker.IsLocked()) 2010 RETURN_ERROR(B_ERROR); 2011 2012 status_t status = query->GetNextEntry(buffer, bufferSize); 2013 if (status == B_OK) 2014 *count = 1; 2015 else if (status == B_ENTRY_NOT_FOUND) 2016 *count = 0; 2017 else 2018 return status; 2019 2020 return B_OK; 2021 } 2022 2023 // TODO: status_t (*rewind_query)(fs_volume fs, fs_cookie cookie); 2024 2025 2026 // #pragma mark - Module Interface 2027 2028 2029 static status_t 2030 ramfs_std_ops(int32 op, ...) 2031 { 2032 switch (op) { 2033 case B_MODULE_INIT: 2034 { 2035 init_debugging(); 2036 PRINT(("ramfs_std_ops(): B_MODULE_INIT\n")); 2037 return B_OK; 2038 } 2039 2040 case B_MODULE_UNINIT: 2041 PRINT(("ramfs_std_ops(): B_MODULE_UNINIT\n")); 2042 exit_debugging(); 2043 return B_OK; 2044 2045 default: 2046 return B_ERROR; 2047 } 2048 } 2049 2050 static file_system_module_info sRamFSModuleInfo = { 2051 { 2052 "file_systems/ramfs" B_CURRENT_FS_API_VERSION, 2053 0, 2054 ramfs_std_ops, 2055 }, 2056 2057 "ramfs", // short_name 2058 "RAM File System", // pretty_name 2059 B_DISK_SYSTEM_SUPPORTS_WRITING, // DDM flags 2060 2061 // scanning 2062 NULL, // identify_partition() 2063 NULL, // scan_partition() 2064 NULL, // free_identify_partition_cookie() 2065 NULL, // free_partition_content_cookie() 2066 2067 &ramfs_mount, 2068 &ramfs_unmount, 2069 &ramfs_read_fs_info, 2070 &ramfs_write_fs_info, 2071 &ramfs_sync, 2072 2073 /* vnode operations */ 2074 &ramfs_lookup, 2075 NULL, // &ramfs_get_vnode_name, 2076 &ramfs_read_vnode, 2077 &ramfs_write_vnode, 2078 &ramfs_remove_vnode, 2079 2080 /* VM file access */ 2081 NULL, // &ramfs_can_page, 2082 NULL, // &ramfs_read_pages, 2083 NULL, // &ramfs_write_pages, 2084 2085 NULL, // &ramfs_get_file_map, 2086 2087 &ramfs_ioctl, 2088 &ramfs_set_flags, 2089 NULL, // &ramfs_select, 2090 NULL, // &ramfs_deselect, 2091 &ramfs_fsync, 2092 2093 &ramfs_read_symlink, 2094 &ramfs_create_symlink, 2095 2096 &ramfs_link, 2097 &ramfs_unlink, 2098 &ramfs_rename, 2099 2100 &ramfs_access, 2101 &ramfs_read_stat, 2102 &ramfs_write_stat, 2103 2104 /* file operations */ 2105 &ramfs_create, 2106 &ramfs_open, 2107 &ramfs_close, 2108 &ramfs_free_cookie, 2109 &ramfs_read, 2110 &ramfs_write, 2111 2112 /* directory operations */ 2113 &ramfs_create_dir, 2114 &ramfs_remove_dir, 2115 &ramfs_open_dir, 2116 &ramfs_close_dir, 2117 &ramfs_free_dir_cookie, 2118 &ramfs_read_dir, 2119 &ramfs_rewind_dir, 2120 2121 /* attribute directory operations */ 2122 &ramfs_open_attr_dir, 2123 &ramfs_close_attr_dir, 2124 &ramfs_free_attr_dir_cookie, 2125 &ramfs_read_attr_dir, 2126 &ramfs_rewind_attr_dir, 2127 2128 /* attribute operations */ 2129 &ramfs_create_attr, 2130 &ramfs_open_attr, 2131 &ramfs_close_attr, 2132 &ramfs_free_attr_cookie, 2133 &ramfs_read_attr, 2134 &ramfs_write_attr, 2135 2136 &ramfs_read_attr_stat, 2137 NULL, // &ramfs_write_attr_stat, 2138 &ramfs_rename_attr, 2139 &ramfs_remove_attr, 2140 2141 /* index directory & index operations */ 2142 &ramfs_open_index_dir, 2143 &ramfs_close_index_dir, 2144 &ramfs_free_index_dir_cookie, 2145 &ramfs_read_index_dir, 2146 &ramfs_rewind_index_dir, 2147 2148 &ramfs_create_index, 2149 &ramfs_remove_index, 2150 &ramfs_read_index_stat, 2151 2152 /* query operations */ 2153 &ramfs_open_query, 2154 &ramfs_close_query, 2155 &ramfs_free_query_cookie, 2156 &ramfs_read_query, 2157 NULL, // &ramfs_rewind_query, 2158 }; 2159 2160 module_info *modules[] = { 2161 (module_info *)&sRamFSModuleInfo, 2162 NULL, 2163 }; 2164