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