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