1 // kernel_interface.cpp 2 // 3 // Copyright (c) 2003-2010, Ingo Weinhold (bonefish@cs.tu-berlin.de) 4 // 5 // This program is free software; you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation; either version 2 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with this program; if not, write to the Free Software 17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 // 19 // You can alternatively use *this file* under the terms of the the MIT 20 // license included in this package. 21 22 23 #include <new> 24 25 #include <ctype.h> 26 #include <dirent.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <time.h> 32 #include <unistd.h> 33 #include <stdio.h> 34 #include <sys/stat.h> 35 36 #include <fs_cache.h> 37 #include <fs_info.h> 38 #include <fs_interface.h> 39 #include <KernelExport.h> 40 41 #include "DirItem.h" 42 #include "Iterators.h" 43 #include "StatItem.h" 44 #include "Tree.h" 45 #include "VNode.h" 46 #include "Volume.h" 47 48 49 using std::nothrow; 50 51 static const size_t kOptimalIOSize = 65536; 52 53 extern fs_volume_ops gReiserFSVolumeOps; 54 extern fs_vnode_ops gReiserFSVnodeOps; 55 56 57 // #pragma mark - FS 58 59 60 // reiserfs_identify_partition 61 static float 62 reiserfs_identify_partition(int fd, partition_data *partition, void **cookie) 63 { 64 Volume* volume = new(nothrow) Volume(); 65 if (volume == NULL) 66 return -1.0; 67 68 status_t status = volume->Identify(fd, partition); 69 if (status != B_OK) { 70 delete volume; 71 return -1.0; 72 } 73 74 *cookie = (void*)volume; 75 return 0.8; 76 } 77 78 79 // reiserfs_scan_partition 80 static status_t 81 reiserfs_scan_partition(int fd, partition_data *partition, void *_cookie) 82 { 83 Volume* volume = (Volume*)_cookie; 84 85 partition->status = B_PARTITION_VALID; 86 partition->flags |= B_PARTITION_FILE_SYSTEM; 87 partition->content_size = volume->CountBlocks() 88 * volume->GetBlockSize(); 89 partition->block_size = volume->GetBlockSize(); 90 91 volume->UpdateName(partition->id); 92 // must be done after setting the content size 93 partition->content_name = strdup(volume->GetName()); 94 if (partition->content_name == NULL) 95 return B_NO_MEMORY; 96 97 return B_OK; 98 } 99 100 101 // reiserfs_free_identify_partition_cookie 102 static void 103 reiserfs_free_identify_partition_cookie(partition_data* partition, 104 void* _cookie) 105 { 106 delete (Volume*)_cookie; 107 } 108 109 110 // #pragma mark - 111 112 113 // reiserfs_mount 114 static status_t 115 reiserfs_mount(fs_volume *_volume, const char *device, uint32 flags, 116 const char *parameters, ino_t *rootID) 117 { 118 TOUCH(flags); TOUCH(parameters); 119 FUNCTION_START(); 120 // parameters are ignored for now 121 status_t error = B_OK; 122 123 // allocate and init the volume 124 Volume *volume = new(nothrow) Volume; 125 if (!volume) 126 error = B_NO_MEMORY; 127 if (error == B_OK) 128 error = volume->Mount(_volume, device); 129 130 // set the results 131 if (error == B_OK) { 132 *rootID = volume->GetRootVNode()->GetID(); 133 _volume->private_volume = volume; 134 _volume->ops = &gReiserFSVolumeOps; 135 } 136 137 // cleanup on failure 138 if (error != B_OK && volume) 139 delete volume; 140 RETURN_ERROR(error); 141 } 142 143 144 // reiserfs_unmount 145 static status_t 146 reiserfs_unmount(fs_volume* fs) 147 { 148 FUNCTION_START(); 149 Volume *volume = (Volume*)fs->private_volume; 150 status_t error = volume->Unmount(); 151 if (error == B_OK) 152 delete volume; 153 RETURN_ERROR(error); 154 } 155 156 // reiserfs_read_fs_info 157 static status_t 158 reiserfs_read_fs_info(fs_volume* fs, struct fs_info *info) 159 { 160 FUNCTION_START(); 161 Volume *volume = (Volume*)fs->private_volume; 162 info->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY; 163 info->block_size = volume->GetBlockSize(); 164 info->io_size = kOptimalIOSize; 165 info->total_blocks = volume->CountBlocks(); 166 info->free_blocks = volume->CountFreeBlocks(); 167 strlcpy(info->device_name, volume->GetDeviceName(), 168 sizeof(info->device_name)); 169 strlcpy(info->volume_name, volume->GetName(), sizeof(info->volume_name)); 170 return B_OK; 171 } 172 173 174 // #pragma mark - VNodes 175 176 177 // reiserfs_lookup 178 static status_t 179 reiserfs_lookup(fs_volume* fs, fs_vnode* _dir, const char *entryName, 180 ino_t *vnid) 181 { 182 // FUNCTION_START(); 183 Volume *volume = (Volume*)fs->private_volume; 184 VNode *dir = (VNode*)_dir->private_node; 185 FUNCTION(("dir: (%Ld: %lu, %lu), entry: `%s'\n", dir->GetID(), dir->GetDirID(), 186 dir->GetObjectID(), entryName)); 187 status_t error = B_OK; 188 VNode *entryNode = NULL; 189 190 // check for non-directories 191 if (!dir->IsDir()) { 192 error = B_ENTRY_NOT_FOUND; 193 194 // special entries: "." and ".." 195 } else if (!strcmp(entryName, ".")) { 196 *vnid = dir->GetID(); 197 if (volume->GetVNode(*vnid, &entryNode) != B_OK) 198 error = B_BAD_VALUE; 199 } else if (!strcmp(entryName, "..")) { 200 *vnid = dir->GetParentID(); 201 if (volume->GetVNode(*vnid, &entryNode) != B_OK) 202 error = B_BAD_VALUE; 203 204 // ordinary entries 205 } else { 206 // find the entry 207 VNode foundNode; 208 error = volume->FindDirEntry(dir, entryName, &foundNode, true); 209 210 // hide non-file/dir/symlink entries, if the user desires that, and 211 // those entries explicitly set to hidden 212 if (error == B_OK 213 && ((foundNode.IsEsoteric() && volume->GetHideEsoteric()) 214 || volume->IsNegativeEntry(foundNode.GetID()))) { 215 error = B_ENTRY_NOT_FOUND; 216 } 217 if (error == B_OK) { 218 *vnid = foundNode.GetID(); 219 error = volume->GetVNode(*vnid, &entryNode); 220 } 221 } 222 223 // add to the entry cache 224 if (error == B_OK) { 225 entry_cache_add(volume->GetID(), dir->GetID(), entryName, 226 *vnid); 227 } 228 229 RETURN_ERROR(error); 230 } 231 232 // reiserfs_read_vnode 233 static status_t 234 reiserfs_read_vnode(fs_volume *fs, ino_t vnid, fs_vnode *node, int *_type, 235 uint32 *_flags, bool reenter) 236 { 237 TOUCH(reenter); 238 // FUNCTION_START(); 239 FUNCTION(("(%Ld: %lu, %ld)\n", vnid, VNode::GetDirIDFor(vnid), 240 VNode::GetObjectIDFor(vnid))); 241 Volume *volume = (Volume*)fs->private_volume; 242 status_t error = B_OK; 243 VNode *foundNode = new(nothrow) VNode; 244 if (foundNode) { 245 error = volume->FindVNode(vnid, foundNode); 246 if (error == B_OK) { 247 node->private_node = foundNode; 248 node->ops = &gReiserFSVnodeOps; 249 *_type = foundNode->GetStatData()->GetMode() & S_IFMT; 250 *_flags = 0; 251 } else 252 delete foundNode; 253 } else 254 error = B_NO_MEMORY; 255 RETURN_ERROR(error); 256 } 257 258 // reiserfs_write_vnode 259 static status_t 260 reiserfs_write_vnode(fs_volume *fs, fs_vnode *_node, bool reenter) 261 { 262 TOUCH(reenter); 263 // DANGER: If dbg_printf() is used, this thread will enter another FS and 264 // even perform a write operation. The is dangerous here, since this hook 265 // may be called out of the other FSs, since, for instance a put_vnode() 266 // called from another FS may cause the VFS layer to free vnodes and thus 267 // invoke this hook. 268 // FUNCTION_START(); 269 Volume *volume = (Volume*)fs->private_volume; 270 VNode *node = (VNode*)_node->private_node; 271 status_t error = B_OK; 272 if (node != volume->GetRootVNode()) 273 delete node; 274 // RETURN_ERROR(error); 275 return error; 276 } 277 278 279 // #pragma mark - Nodes 280 281 282 // reiserfs_read_symlink 283 static status_t 284 reiserfs_read_symlink(fs_volume *fs, fs_vnode *_node, char *buffer, 285 size_t *bufferSize) 286 { 287 // FUNCTION_START(); 288 Volume *volume = (Volume*)fs->private_volume; 289 VNode *node = (VNode*)_node->private_node; 290 FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(), 291 node->GetObjectID())); 292 status_t error = B_OK; 293 // read symlinks only 294 if (!node->IsSymlink()) 295 error = B_BAD_VALUE; 296 // read 297 if (error == B_OK) 298 error = volume->ReadLink(node, buffer, *bufferSize, bufferSize); 299 RETURN_ERROR(error); 300 } 301 302 // reiserfs_access 303 static status_t 304 reiserfs_access(fs_volume *fs, fs_vnode *_node, int mode) 305 { 306 TOUCH(fs); 307 VNode *node = (VNode*)_node->private_node; 308 FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(), 309 node->GetObjectID())); 310 311 // write access requested? 312 if (mode & W_OK) 313 return B_READ_ONLY_DEVICE; 314 315 // get node permissions 316 StatData *statData = node->GetStatData(); 317 318 return check_access_permissions(mode, statData->GetMode(), 319 statData->GetGID(), statData->GetUID()); 320 } 321 322 // reiserfs_read_stat 323 static status_t 324 reiserfs_read_stat(fs_volume *fs, fs_vnode *_node, struct stat *st) 325 { 326 // FUNCTION_START(); 327 Volume *volume = (Volume*)fs->private_volume; 328 VNode *node = (VNode*)_node->private_node; 329 FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(), 330 node->GetObjectID())); 331 status_t error = B_OK; 332 StatData *statData = node->GetStatData(); 333 st->st_dev = volume->GetID(); 334 st->st_ino = node->GetID(); 335 st->st_mode = statData->GetMode(); 336 st->st_nlink = statData->GetNLink(); 337 st->st_uid = statData->GetUID(); 338 st->st_gid = statData->GetGID(); 339 st->st_size = statData->GetSize(); 340 st->st_blksize = kOptimalIOSize; 341 st->st_atime = statData->GetATime(); 342 st->st_mtime = st->st_ctime = statData->GetMTime(); 343 st->st_crtime = statData->GetCTime(); 344 RETURN_ERROR(error); 345 } 346 347 348 // #pragma mark - Files 349 350 351 // reiserfs_open 352 static status_t 353 reiserfs_open(fs_volume *fs, fs_vnode *_node, int openMode, void **cookie) 354 { 355 // FUNCTION_START(); 356 Volume *volume = (Volume*)fs->private_volume; 357 VNode *node = (VNode*)_node->private_node; 358 FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(), 359 node->GetObjectID())); 360 status_t error = B_OK; 361 // check the open mode 362 if ((openMode & O_RWMASK) == O_WRONLY || (openMode & O_RWMASK) == O_RDWR 363 || (openMode & (O_TRUNC | O_CREAT))) { 364 error = B_READ_ONLY_DEVICE; 365 } 366 // create a StreamReader 367 if (error == B_OK) { 368 StreamReader *reader = new(nothrow) StreamReader(volume->GetTree(), 369 node->GetDirID(), node->GetObjectID()); 370 if (reader) { 371 error = reader->Suspend(); 372 if (error == B_OK) 373 *cookie = reader; 374 else 375 delete reader; 376 } else 377 error = B_NO_MEMORY; 378 } 379 RETURN_ERROR(error); 380 } 381 382 // reiserfs_close 383 static status_t 384 reiserfs_close(fs_volume *fs, fs_vnode *_node, void *cookie) 385 { 386 TOUCH(fs); TOUCH(cookie); 387 // FUNCTION_START(); 388 VNode *node = (VNode*)_node->private_node; 389 FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(), 390 node->GetObjectID())); 391 TOUCH(node); 392 return B_OK; 393 } 394 395 // reiserfs_free_cookie 396 static status_t 397 reiserfs_free_cookie(fs_volume *fs, fs_vnode *_node, void *cookie) 398 { 399 TOUCH(fs); 400 // FUNCTION_START(); 401 VNode *node = (VNode*)_node->private_node; 402 FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(), 403 node->GetObjectID())); 404 TOUCH(node); 405 StreamReader *reader = (StreamReader*)cookie; 406 delete reader; 407 return B_OK; 408 } 409 410 // reiserfs_read 411 static status_t 412 reiserfs_read(fs_volume *fs, fs_vnode *_node, void *cookie, off_t pos, 413 void *buffer, size_t *bufferSize) 414 { 415 TOUCH(fs); 416 // FUNCTION_START(); 417 // Volume *volume = (Volume*)fs->private_volume; 418 VNode *node = (VNode*)_node->private_node; 419 FUNCTION(("((%Ld: %lu, %lu), %Ld, %p, %lu)\n", node->GetID(), 420 node->GetDirID(), node->GetObjectID(), pos, buffer, 421 *bufferSize)); 422 status_t error = B_OK; 423 // don't read anything but files 424 if (!node->IsFile()) { 425 if (node->IsDir()) 426 error = B_IS_A_DIRECTORY; 427 else 428 error = B_BAD_VALUE; 429 } 430 431 // read 432 StreamReader *reader = (StreamReader*)cookie; 433 if (error == B_OK) { 434 error = reader->Resume(); 435 if (error == B_OK) { 436 error = reader->ReadAt(pos, buffer, *bufferSize, bufferSize); 437 reader->Suspend(); 438 } 439 } 440 RETURN_ERROR(error); 441 } 442 443 444 class DirectoryCookie : public DirEntryIterator { 445 public: 446 DirectoryCookie(Tree *tree, uint32 dirID, uint32 objectID, 447 uint64 startOffset = 0, bool fixedHash = false) 448 : DirEntryIterator(tree, dirID, objectID, startOffset, 449 fixedHash), 450 fEncounteredDotDot(false) 451 { 452 } 453 454 bool EncounteredDotDot() const 455 { 456 return fEncounteredDotDot; 457 } 458 459 void SetEncounteredDotDot(bool flag) 460 { 461 fEncounteredDotDot = flag; 462 } 463 464 bool fEncounteredDotDot; 465 }; 466 467 468 // #pragma mark - Directories 469 470 471 // reiserfs_open_dir 472 static status_t 473 reiserfs_open_dir(fs_volume *fs, fs_vnode *_node, void **cookie) 474 { 475 // FUNCTION_START(); 476 Volume *volume = (Volume*)fs->private_volume; 477 VNode *node = (VNode*)_node->private_node; 478 FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(), 479 node->GetObjectID())); 480 status_t error = (node->IsDir() ? B_OK : B_NOT_A_DIRECTORY); 481 if (error == B_OK) { 482 DirectoryCookie *iterator = new(nothrow) DirectoryCookie( 483 volume->GetTree(), node->GetDirID(), node->GetObjectID()); 484 if (iterator) { 485 error = iterator->Suspend(); 486 if (error == B_OK) 487 *cookie = iterator; 488 else 489 delete iterator; 490 } else 491 error = B_NO_MEMORY; 492 } 493 FUNCTION_END(); 494 RETURN_ERROR(error); 495 } 496 497 // set_dirent_name 498 static status_t 499 set_dirent_name(struct dirent *buffer, size_t bufferSize, 500 const char *name, int32 nameLen) 501 { 502 size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer; 503 if (length <= bufferSize) { 504 memcpy(buffer->d_name, name, nameLen); 505 buffer->d_name[nameLen] = '\0'; 506 buffer->d_reclen = length; 507 return B_OK; 508 } else 509 RETURN_ERROR(B_BUFFER_OVERFLOW); 510 } 511 512 // reiserfs_close_dir 513 static status_t 514 reiserfs_close_dir(fs_volume *fs, fs_vnode *_node, void *cookie) 515 { 516 TOUCH(fs); TOUCH(cookie); 517 // FUNCTION_START(); 518 VNode *node = (VNode*)_node->private_node; 519 FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(), 520 node->GetObjectID())); 521 TOUCH(node); 522 return B_OK; 523 } 524 525 // reiserfs_free_dir_cookie 526 static status_t 527 reiserfs_free_dir_cookie(fs_volume *fs, fs_vnode *_node, void *cookie) 528 { 529 TOUCH(fs); 530 // FUNCTION_START(); 531 VNode *node = (VNode*)_node->private_node; 532 FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(), 533 node->GetObjectID())); 534 TOUCH(node); 535 DirectoryCookie *iterator = (DirectoryCookie*)cookie; 536 delete iterator; 537 return B_OK; 538 } 539 540 // reiserfs_read_dir 541 static status_t 542 reiserfs_read_dir(fs_volume *fs, fs_vnode *_node, void *cookie, 543 struct dirent *buffer, size_t bufferSize, uint32 *count) 544 { 545 // FUNCTION_START(); 546 Volume *volume = (Volume*)fs->private_volume; 547 VNode *node = (VNode*)_node->private_node; 548 FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(), 549 node->GetObjectID())); 550 DirectoryCookie *iterator = (DirectoryCookie*)cookie; 551 status_t error = iterator->Resume(); 552 if (error == B_OK) { 553 // read one entry 554 DirItem item; 555 int32 index = 0; 556 DirEntry *entry = NULL; 557 bool done = false; 558 while (error == B_OK && !done 559 && (error = iterator->GetNext(&item, &index, &entry)) == B_OK) { 560 uint32 dirID = entry->GetDirID(); 561 uint32 objectID = entry->GetObjectID(); 562 // skip hidden entries and entries the user specified to be hidden 563 if (entry->IsHidden() || volume->IsNegativeEntry(dirID, objectID)) 564 continue; 565 // skip entry, if we can't get the stat data, or it is neither a 566 // file, a dir nor a symlink and the user desired to hide those. 567 StatData statData; 568 StatItem statItem; 569 if (volume->GetTree()->FindStatItem(dirID, objectID, &statItem) 570 != B_OK 571 || statItem.GetStatData(&statData) != B_OK 572 || (statData.IsEsoteric() && volume->GetHideEsoteric())) { 573 continue; 574 } 575 if (error == B_OK) { 576 // get the name 577 size_t nameLen = 0; 578 const char *name = item.EntryNameAt(index, &nameLen); 579 if (!name || nameLen == 0) // bad data: skip it gracefully 580 continue; 581 // fill in the entry name -- checks whether the 582 // entry fits into the buffer 583 error = set_dirent_name(buffer, bufferSize, name, 584 nameLen); 585 if (error == B_OK) { 586 // fill in the other data 587 buffer->d_dev = volume->GetID(); 588 buffer->d_ino = VNode::GetIDFor(dirID, objectID); 589 *count = 1; 590 PRINT(("Successfully read entry: dir: (%Ld: %ld, %ld), name: `%s', " 591 "id: (%Ld, %ld, %ld), reclen: %hu\n", node->GetID(), node->GetDirID(), 592 node->GetObjectID(), buffer->d_name, buffer->d_ino, dirID, objectID, 593 buffer->d_reclen)); 594 if (!strcmp("..", buffer->d_name)) 595 iterator->SetEncounteredDotDot(true); 596 done = true; 597 } 598 } 599 } 600 if (error == B_ENTRY_NOT_FOUND) { 601 if (iterator->EncounteredDotDot()) { 602 error = B_OK; 603 *count = 0; 604 } else { 605 // this is necessary for the root directory 606 // it usually has no ".." entry, so we simulate one 607 // get the name 608 const char *name = ".."; 609 size_t nameLen = strlen(name); 610 // fill in the entry name -- checks whether the 611 // entry fits into the buffer 612 error = set_dirent_name(buffer, bufferSize, name, 613 nameLen); 614 if (error == B_OK) { 615 // fill in the other data 616 buffer->d_dev = volume->GetID(); 617 buffer->d_ino = node->GetID(); 618 // < That's not correct! 619 *count = 1; 620 PRINT(("faking `..' entry: dir: (%Ld: %ld, %ld), name: `%s', " 621 "id: (%Ld, %ld, %ld), reclen: %hu\n", node->GetID(), node->GetDirID(), 622 node->GetObjectID(), buffer->d_name, buffer->d_ino, node->GetDirID(), 623 node->GetObjectID(), buffer->d_reclen)); 624 iterator->SetEncounteredDotDot(true); 625 } 626 } 627 } 628 iterator->Suspend(); 629 } 630 PRINT(("returning %ld entries\n", *count)); 631 RETURN_ERROR(error); 632 } 633 634 // reiserfs_rewind_dir 635 static status_t 636 reiserfs_rewind_dir(fs_volume *fs, fs_vnode *_node, void *cookie) 637 { 638 TOUCH(fs); 639 // FUNCTION_START(); 640 VNode *node = (VNode*)_node->private_node; 641 FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(), 642 node->GetObjectID())); 643 TOUCH(node); 644 DirectoryCookie *iterator = (DirectoryCookie*)cookie; 645 status_t error = iterator->Rewind(); // no need to Resume() 646 if (error == B_OK) 647 error = iterator->Suspend(); 648 RETURN_ERROR(error); 649 } 650 651 652 // #pragma mark - Module Interface 653 654 655 // reiserfs_std_ops 656 static status_t 657 reiserfs_std_ops(int32 op, ...) 658 { 659 switch (op) { 660 case B_MODULE_INIT: 661 { 662 init_debugging(); 663 PRINT(("reiserfs_std_ops(): B_MODULE_INIT\n")); 664 return B_OK; 665 } 666 667 case B_MODULE_UNINIT: 668 PRINT(("reiserfs_std_ops(): B_MODULE_UNINIT\n")); 669 exit_debugging(); 670 return B_OK; 671 672 default: 673 return B_ERROR; 674 } 675 } 676 677 678 679 680 static file_system_module_info sReiserFSModuleInfo = { 681 { 682 "file_systems/reiserfs" B_CURRENT_FS_API_VERSION, 683 0, 684 reiserfs_std_ops, 685 }, 686 687 "reiserfs", // short_name 688 "Reiser File System", // pretty_name 689 0, // DDM flags 690 691 692 // scanning 693 &reiserfs_identify_partition, 694 &reiserfs_scan_partition, 695 &reiserfs_free_identify_partition_cookie, 696 NULL, // free_partition_content_cookie() 697 698 &reiserfs_mount 699 }; 700 701 702 fs_volume_ops gReiserFSVolumeOps = { 703 &reiserfs_unmount, 704 &reiserfs_read_fs_info, 705 NULL, // &reiserfs_write_fs_info, 706 NULL, // &reiserfs_sync, 707 708 &reiserfs_read_vnode 709 }; 710 711 712 fs_vnode_ops gReiserFSVnodeOps = { 713 /* vnode operations */ 714 &reiserfs_lookup, 715 NULL, // &reiserfs_get_vnode_name, 716 &reiserfs_write_vnode, 717 NULL, // &reiserfs_remove_vnode, 718 719 /* VM file access */ 720 NULL, // &reiserfs_can_page, 721 NULL, // &reiserfs_read_pages, 722 NULL, // &reiserfs_write_pages, 723 724 NULL, // io() 725 NULL, // cancel_io() 726 727 NULL, // &reiserfs_get_file_map, 728 729 NULL, // &reiserfs_ioctl, 730 NULL, // &reiserfs_set_flags, 731 NULL, // &reiserfs_select, 732 NULL, // &reiserfs_deselect, 733 NULL, // &reiserfs_fsync, 734 735 &reiserfs_read_symlink, 736 NULL, // &reiserfs_create_symlink, 737 738 NULL, // &reiserfs_link, 739 NULL, // &reiserfs_unlink, 740 NULL, // &reiserfs_rename, 741 742 &reiserfs_access, 743 &reiserfs_read_stat, 744 NULL, // &reiserfs_write_stat, 745 NULL, // &reiserfs_preallocate, 746 747 /* file operations */ 748 NULL, // &reiserfs_create, 749 &reiserfs_open, 750 &reiserfs_close, 751 &reiserfs_free_cookie, 752 &reiserfs_read, 753 NULL, // &reiserfs_write, 754 755 /* directory operations */ 756 NULL, // &reiserfs_create_dir, 757 NULL, // &reiserfs_remove_dir, 758 &reiserfs_open_dir, 759 &reiserfs_close_dir, 760 &reiserfs_free_dir_cookie, 761 &reiserfs_read_dir, 762 &reiserfs_rewind_dir 763 }; 764 765 766 module_info *modules[] = { 767 (module_info *)&sReiserFSModuleInfo, 768 NULL, 769 }; 770