1 /* 2 * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 #ifndef EXT2_H 6 #define EXT2_H 7 8 9 #include <sys/stat.h> 10 11 #include <ByteOrder.h> 12 #include <fs_interface.h> 13 #include <KernelExport.h> 14 15 16 //#define TRACE_EXT2 17 18 #define EXT2_SUPER_BLOCK_OFFSET 1024 19 20 struct ext2_super_block { 21 uint32 num_inodes; 22 uint32 num_blocks; 23 uint32 reserved_blocks; 24 uint32 free_blocks; 25 uint32 free_inodes; 26 uint32 first_data_block; 27 uint32 block_shift; 28 uint32 fragment_shift; 29 uint32 blocks_per_group; 30 uint32 fragments_per_group; 31 uint32 inodes_per_group; 32 uint32 mount_time; 33 uint32 write_time; 34 uint16 mount_count; 35 uint16 max_mount_count; 36 uint16 magic; 37 uint16 state; 38 uint16 error_handling; 39 uint16 minor_revision_level; 40 uint32 last_check_time; 41 uint32 check_interval; 42 uint32 creator_os; 43 uint32 revision_level; 44 uint16 reserved_blocks_uid; 45 uint16 reserved_blocks_gid; 46 uint32 first_inode; 47 uint16 inode_size; 48 uint16 block_group; 49 uint32 compatible_features; 50 uint32 incompatible_features; 51 uint32 read_only_features; 52 uint8 uuid[16]; 53 char name[16]; 54 char last_mount_point[64]; 55 uint32 algorithm_usage_bitmap; 56 uint8 preallocated_blocks; 57 uint8 preallocated_directory_blocks; 58 uint16 _padding; 59 60 // journaling ext3 support 61 uint8 journal_uuid[16]; 62 uint32 journal_inode; 63 uint32 journal_device; 64 uint32 last_orphan; 65 uint32 hash_seed[4]; 66 uint8 default_hash_version; 67 uint8 _reserved1; 68 uint16 group_descriptor_size; 69 uint32 default_mount_options; 70 uint32 first_meta_block_group; 71 uint32 fs_creation_time; 72 uint32 journal_inode_backup[17]; 73 74 // ext4 support 75 uint32 num_blocks_hi; 76 uint32 reserved_blocks_hi; 77 uint32 free_blocks_hi; 78 uint16 min_inode_size; 79 uint16 want_inode_size; 80 uint32 flags; 81 uint16 raid_stride; 82 uint16 mmp_interval; 83 uint64 mmp_block; 84 uint32 raid_stripe_width; 85 uint8 groups_per_flex_shift; 86 uint8 _reserved3; 87 uint16 _reserved4; 88 uint32 _reserved5[162]; 89 90 uint16 Magic() const { return B_LENDIAN_TO_HOST_INT16(magic); } 91 uint16 State() const { return B_LENDIAN_TO_HOST_INT16(state); } 92 uint32 RevisionLevel() const { return B_LENDIAN_TO_HOST_INT16(revision_level); } 93 uint32 BlockShift() const { return B_LENDIAN_TO_HOST_INT32(block_shift) + 10; } 94 uint32 NumInodes() const { return B_LENDIAN_TO_HOST_INT32(num_inodes); } 95 uint32 NumBlocks() const { return B_LENDIAN_TO_HOST_INT32(num_blocks); } 96 uint32 FreeInodes() const { return B_LENDIAN_TO_HOST_INT32(free_inodes); } 97 uint32 FreeBlocks() const { return B_LENDIAN_TO_HOST_INT32(free_blocks); } 98 uint16 InodeSize() const { return B_LENDIAN_TO_HOST_INT16(inode_size); } 99 uint32 FirstDataBlock() const 100 { return B_LENDIAN_TO_HOST_INT32(first_data_block); } 101 uint32 BlocksPerGroup() const 102 { return B_LENDIAN_TO_HOST_INT32(blocks_per_group); } 103 uint32 InodesPerGroup() const 104 { return B_LENDIAN_TO_HOST_INT32(inodes_per_group); } 105 uint32 FirstMetaBlockGroup() const 106 { return B_LENDIAN_TO_HOST_INT32(first_meta_block_group); } 107 uint32 CompatibleFeatures() const 108 { return B_LENDIAN_TO_HOST_INT32(compatible_features); } 109 uint32 ReadOnlyFeatures() const 110 { return B_LENDIAN_TO_HOST_INT32(read_only_features); } 111 uint32 IncompatibleFeatures() const 112 { return B_LENDIAN_TO_HOST_INT32(incompatible_features); } 113 ino_t JournalInode() const 114 { return B_LENDIAN_TO_HOST_INT32(journal_inode); } 115 ino_t LastOrphan() const 116 { return (ino_t)B_LENDIAN_TO_HOST_INT32(last_orphan); } 117 uint32 HashSeed(uint8 i) const 118 { return B_LENDIAN_TO_HOST_INT32(hash_seed[i]); } 119 uint16 GroupDescriptorSize() const 120 { return B_LENDIAN_TO_HOST_INT16(group_descriptor_size); } 121 122 void SetFreeInodes(uint32 freeInodes) 123 { free_inodes = B_HOST_TO_LENDIAN_INT32(freeInodes); } 124 void SetFreeBlocks(uint32 freeBlocks) 125 { free_blocks = B_HOST_TO_LENDIAN_INT32(freeBlocks); } 126 void SetLastOrphan(ino_t id) 127 { last_orphan = B_HOST_TO_LENDIAN_INT32((uint32)id); } 128 void SetReadOnlyFeatures(uint32 readOnlyFeatures) const 129 { readOnlyFeatures = B_HOST_TO_LENDIAN_INT32(readOnlyFeatures); } 130 131 bool IsValid(); 132 // implemented in Volume.cpp 133 } _PACKED; 134 135 #define EXT2_OLD_REVISION 0 136 #define EXT2_DYNAMIC_REVISION 1 137 138 #define EXT2_MAX_REVISION EXT2_DYNAMIC_REVISION 139 140 #define EXT2_FS_STATE_VALID 1 // File system was cleanly unmounted 141 #define EXT2_FS_STATE_ERROR 2 // File system has errors 142 #define EXT2_FS_STATE_ORPHAN 3 // Orphans are being recovered 143 144 // compatible features 145 #define EXT2_FEATURE_DIRECTORY_PREALLOCATION 0x0001 146 #define EXT2_FEATURE_IMAGIC_INODES 0x0002 147 #define EXT2_FEATURE_HAS_JOURNAL 0x0004 148 #define EXT2_FEATURE_EXT_ATTR 0x0008 149 #define EXT2_FEATURE_RESIZE_INODE 0x0010 150 #define EXT2_FEATURE_DIRECTORY_INDEX 0x0020 151 152 // read-only compatible features 153 #define EXT2_READ_ONLY_FEATURE_SPARSE_SUPER 0x0001 154 #define EXT2_READ_ONLY_FEATURE_LARGE_FILE 0x0002 155 #define EXT2_READ_ONLY_FEATURE_BTREE_DIRECTORY 0x0004 156 #define EXT2_READ_ONLY_FEATURE_HUGE_FILE 0x0008 157 #define EXT2_READ_ONLY_FEATURE_GDT_CSUM 0x0010 158 #define EXT2_READ_ONLY_FEATURE_DIR_NLINK 0x0020 159 #define EXT2_READ_ONLY_FEATURE_EXTRA_ISIZE 0x0040 160 161 // incompatible features 162 #define EXT2_INCOMPATIBLE_FEATURE_COMPRESSION 0x0001 163 #define EXT2_INCOMPATIBLE_FEATURE_FILE_TYPE 0x0002 164 #define EXT2_INCOMPATIBLE_FEATURE_RECOVER 0x0004 165 #define EXT2_INCOMPATIBLE_FEATURE_JOURNAL 0x0008 166 #define EXT2_INCOMPATIBLE_FEATURE_META_GROUP 0x0010 167 #define EXT2_INCOMPATIBLE_FEATURE_EXTENTS 0x0040 168 #define EXT2_INCOMPATIBLE_FEATURE_64BIT 0x0080 169 #define EXT2_INCOMPATIBLE_FEATURE_MMP 0x0100 170 #define EXT2_INCOMPATIBLE_FEATURE_FLEX_GROUP 0x0200 171 172 // states 173 #define EXT2_STATE_VALID 0x01 174 #define EXT2_STATE_INVALID 0x02 175 176 struct ext2_block_group { 177 uint32 block_bitmap; 178 uint32 inode_bitmap; 179 uint32 inode_table; 180 uint16 free_blocks; 181 uint16 free_inodes; 182 uint16 used_directories; 183 uint16 _padding; 184 uint32 _reserved[3]; 185 186 uint32 BlockBitmap() const 187 { return B_LENDIAN_TO_HOST_INT32(block_bitmap); } 188 uint32 InodeBitmap() const 189 { return B_LENDIAN_TO_HOST_INT32(inode_bitmap); } 190 uint32 InodeTable() const 191 { return B_LENDIAN_TO_HOST_INT32(inode_table); } 192 uint16 FreeBlocks() const 193 { return B_LENDIAN_TO_HOST_INT16(free_blocks); } 194 uint16 FreeInodes() const 195 { return B_LENDIAN_TO_HOST_INT16(free_inodes); } 196 uint16 UsedDirectories() const 197 { return B_LENDIAN_TO_HOST_INT16(used_directories); } 198 199 void SetFreeBlocks(uint16 freeBlocks) 200 { free_blocks = B_HOST_TO_LENDIAN_INT16(freeBlocks); } 201 202 void SetFreeInodes(uint16 freeInodes) 203 { free_inodes = B_HOST_TO_LENDIAN_INT16(freeInodes); } 204 205 void SetUsedDirectories(uint16 usedDirectories) 206 { used_directories = B_HOST_TO_LENDIAN_INT16(usedDirectories); } 207 } _PACKED; 208 209 #define EXT2_DIRECT_BLOCKS 12 210 #define EXT2_ROOT_NODE 2 211 #define EXT2_SHORT_SYMLINK_LENGTH 60 212 213 struct ext2_data_stream { 214 uint32 direct[EXT2_DIRECT_BLOCKS]; 215 uint32 indirect; 216 uint32 double_indirect; 217 uint32 triple_indirect; 218 } _PACKED; 219 220 #define EXT2_INODE_NORMAL_SIZE 128 221 222 struct ext2_inode { 223 uint16 mode; 224 uint16 uid; 225 uint32 size; 226 uint32 access_time; 227 uint32 change_time; 228 uint32 modification_time; 229 uint32 deletion_time; 230 uint16 gid; 231 uint16 num_links; 232 uint32 num_blocks; 233 uint32 flags; 234 uint32 version; 235 union { 236 ext2_data_stream stream; 237 char symlink[EXT2_SHORT_SYMLINK_LENGTH]; 238 }; 239 uint32 generation; 240 uint32 file_access_control; 241 union { 242 // for directories vs. files 243 uint32 directory_access_control; 244 uint32 size_high; 245 }; 246 uint32 fragment; 247 union { 248 struct { 249 uint8 fragment_number; 250 uint8 fragment_size; 251 }; 252 uint16 num_blocks_high; 253 }; 254 uint16 _padding; 255 uint16 uid_high; 256 uint16 gid_high; 257 uint32 _reserved2; 258 259 // extra attributes 260 uint16 extra_inode_size; 261 uint16 _padding2; 262 uint32 change_time_extra; 263 uint32 modification_time_extra; 264 uint32 access_time_extra; 265 uint32 creation_time; 266 uint32 creation_time_extra; 267 uint32 version_high; 268 269 uint16 Mode() const { return B_LENDIAN_TO_HOST_INT16(mode); } 270 uint32 Flags() const { return B_LENDIAN_TO_HOST_INT32(flags); } 271 uint16 NumLinks() const { return B_LENDIAN_TO_HOST_INT16(num_links); } 272 uint32 NumBlocks() const { return B_LENDIAN_TO_HOST_INT32(num_blocks); } 273 uint64 NumBlocks64() const { return B_LENDIAN_TO_HOST_INT32(num_blocks) 274 | ((uint64)B_LENDIAN_TO_HOST_INT32(num_blocks_high) << 32); } 275 276 static void _DecodeTime(struct timespec *timespec, uint32 time, 277 uint32 time_extra, bool extra) 278 { 279 timespec->tv_sec = B_LENDIAN_TO_HOST_INT32(time); 280 if (extra && sizeof(timespec->tv_sec) > 4) 281 timespec->tv_sec |= 282 (uint64)(B_LENDIAN_TO_HOST_INT32(time_extra) & 0x2) << 32; 283 if (extra) 284 timespec->tv_nsec = B_LENDIAN_TO_HOST_INT32(time_extra) >> 2; 285 else 286 timespec->tv_nsec = 0; 287 } 288 289 void GetModificationTime(struct timespec *timespec, bool extra) const 290 { _DecodeTime(timespec, modification_time, modification_time_extra, 291 extra); } 292 void GetAccessTime(struct timespec *timespec, bool extra) const 293 { _DecodeTime(timespec, access_time, access_time_extra, extra); } 294 void GetChangeTime(struct timespec *timespec, bool extra) const 295 { _DecodeTime(timespec, change_time, change_time_extra, extra); } 296 void GetCreationTime(struct timespec *timespec, bool extra) const 297 { 298 if (extra) 299 _DecodeTime(timespec, creation_time, creation_time_extra, extra); 300 else { 301 timespec->tv_sec = 0; 302 timespec->tv_nsec = 0; 303 } 304 } 305 time_t DeletionTime() const 306 { return B_LENDIAN_TO_HOST_INT32(deletion_time); } 307 308 static uint32 _EncodeTime(const struct timespec *timespec) 309 { 310 uint32 time = (timespec->tv_nsec << 2) & 0xfffffffc; 311 if (sizeof(timespec->tv_sec) > 4) 312 time |= (uint64)timespec->tv_sec >> 32; 313 return B_HOST_TO_LENDIAN_INT32(time); 314 } 315 316 void SetModificationTime(const struct timespec *timespec, bool extra) 317 { 318 modification_time = B_HOST_TO_LENDIAN_INT32((uint32)timespec->tv_sec); 319 if (extra) 320 modification_time_extra = _EncodeTime(timespec); 321 } 322 void SetAccessTime(const struct timespec *timespec, bool extra) 323 { 324 access_time = B_HOST_TO_LENDIAN_INT32((uint32)timespec->tv_sec); 325 if (extra) 326 access_time_extra = _EncodeTime(timespec); 327 } 328 void SetChangeTime(const struct timespec *timespec, bool extra) 329 { 330 change_time = B_HOST_TO_LENDIAN_INT32((uint32)timespec->tv_sec); 331 if (extra) 332 change_time_extra = _EncodeTime(timespec); 333 } 334 void SetCreationTime(const struct timespec *timespec, bool extra) 335 { 336 if (extra) { 337 creation_time = B_HOST_TO_LENDIAN_INT32((uint32)timespec->tv_sec); 338 creation_time_extra = 339 B_HOST_TO_LENDIAN_INT32((uint32)timespec->tv_nsec); 340 } 341 } 342 void SetDeletionTime(time_t deletionTime) 343 { 344 deletion_time = B_HOST_TO_LENDIAN_INT32((uint32)deletionTime); 345 } 346 347 ino_t NextOrphan() const { return (ino_t)DeletionTime(); } 348 349 off_t Size() const 350 { 351 if (S_ISREG(Mode())) { 352 return B_LENDIAN_TO_HOST_INT32(size) 353 | ((off_t)B_LENDIAN_TO_HOST_INT32(size_high) << 32); 354 } 355 356 return B_LENDIAN_TO_HOST_INT32(size); 357 } 358 359 uint32 ExtendedAttributesBlock() const 360 { return B_LENDIAN_TO_HOST_INT32(file_access_control);} 361 362 uint16 ExtraInodeSize() const 363 { return B_LENDIAN_TO_HOST_INT16(extra_inode_size); } 364 365 uint32 UserID() const 366 { 367 return B_LENDIAN_TO_HOST_INT16(uid) 368 | (B_LENDIAN_TO_HOST_INT16(uid_high) << 16); 369 } 370 371 uint32 GroupID() const 372 { 373 return B_LENDIAN_TO_HOST_INT16(gid) 374 | (B_LENDIAN_TO_HOST_INT16(gid_high) << 16); 375 } 376 377 void SetMode(uint16 newMode) 378 { 379 mode = B_LENDIAN_TO_HOST_INT16(newMode); 380 } 381 382 void UpdateMode(uint16 newMode, uint16 mask) 383 { 384 SetMode((Mode() & ~mask) | (newMode & mask)); 385 } 386 387 void ClearFlag(uint32 mask) 388 { 389 flags &= ~B_HOST_TO_LENDIAN_INT32(mask); 390 } 391 392 void SetFlag(uint32 mask) 393 { 394 flags |= B_HOST_TO_LENDIAN_INT32(mask); 395 } 396 397 void SetFlags(uint32 newFlags) 398 { 399 flags = B_HOST_TO_LENDIAN_INT32(newFlags); 400 } 401 402 void SetNumLinks(uint16 numLinks) 403 { 404 num_links = B_HOST_TO_LENDIAN_INT16(numLinks); 405 } 406 407 void SetNumBlocks(uint32 numBlocks) 408 { 409 num_blocks = B_HOST_TO_LENDIAN_INT32(numBlocks); 410 } 411 412 void SetNumBlocks64(uint64 numBlocks) 413 { 414 num_blocks = B_HOST_TO_LENDIAN_INT32(numBlocks & 0xffffffff); 415 num_blocks_high = B_HOST_TO_LENDIAN_INT32(numBlocks >> 32); 416 } 417 418 void SetNextOrphan(ino_t id) 419 { 420 deletion_time = B_HOST_TO_LENDIAN_INT32((uint32)id); 421 } 422 423 void SetSize(off_t newSize) 424 { 425 size = B_HOST_TO_LENDIAN_INT32(newSize & 0xFFFFFFFF); 426 if (S_ISREG(Mode())) 427 size_high = B_HOST_TO_LENDIAN_INT32(newSize >> 32); 428 } 429 430 void SetUserID(uint32 newUID) 431 { 432 uid = B_HOST_TO_LENDIAN_INT16(newUID & 0xFFFF); 433 uid_high = B_HOST_TO_LENDIAN_INT16(newUID >> 16); 434 } 435 436 void SetGroupID(uint32 newGID) 437 { 438 gid = B_HOST_TO_LENDIAN_INT16(newGID & 0xFFFF); 439 gid_high = B_HOST_TO_LENDIAN_INT16(newGID >> 16); 440 } 441 442 void SetExtendedAttributesBlock(uint32 block) 443 { 444 file_access_control = B_HOST_TO_LENDIAN_INT32(block); 445 } 446 447 void SetExtraInodeSize(uint16 newSize) 448 { 449 extra_inode_size = B_HOST_TO_LENDIAN_INT16(newSize); 450 } 451 } _PACKED; 452 453 #define EXT2_SUPER_BLOCK_MAGIC 0xef53 454 455 // flags 456 #define EXT2_INODE_SECURE_DELETION 0x00000001 457 #define EXT2_INODE_UNDELETE 0x00000002 458 #define EXT2_INODE_COMPRESSED 0x00000004 459 #define EXT2_INODE_SYNCHRONOUS 0x00000008 460 #define EXT2_INODE_IMMUTABLE 0x00000010 461 #define EXT2_INODE_APPEND_ONLY 0x00000020 462 #define EXT2_INODE_NO_DUMP 0x00000040 463 #define EXT2_INODE_NO_ACCESS_TIME 0x00000080 464 #define EXT2_INODE_DIRTY 0x00000100 465 #define EXT2_INODE_COMPRESSED_BLOCKS 0x00000200 466 #define EXT2_INODE_DO_NOT_COMPRESS 0x00000400 467 #define EXT2_INODE_COMPRESSION_ERROR 0x00000800 468 #define EXT2_INODE_BTREE 0x00001000 469 #define EXT2_INODE_INDEXED 0x00001000 470 #define EXT2_INODE_HUGE_FILE 0x00040000 471 472 #define EXT2_NAME_LENGTH 255 473 474 struct ext2_dir_entry { 475 uint32 inode_id; 476 uint16 length; 477 uint8 name_length; 478 uint8 file_type; 479 char name[EXT2_NAME_LENGTH]; 480 481 uint32 InodeID() const { return B_LENDIAN_TO_HOST_INT32(inode_id); } 482 uint16 Length() const { return B_LENDIAN_TO_HOST_INT16(length); } 483 uint8 NameLength() const { return name_length; } 484 uint8 FileType() const { return file_type; } 485 486 void SetInodeID(uint32 id) { inode_id = B_HOST_TO_LENDIAN_INT32(id); } 487 488 void SetLength(uint16 newLength/*uint8 nameLength*/) 489 { 490 length = B_HOST_TO_LENDIAN_INT16(newLength); 491 /*name_length = nameLength; 492 493 if (nameLength % 4 == 0) { 494 length = B_HOST_TO_LENDIAN_INT16( 495 (short)(nameLength + MinimumSize())); 496 } else { 497 length = B_HOST_TO_LENDIAN_INT16( 498 (short)(nameLength % 4 + 1 + MinimumSize())); 499 }*/ 500 } 501 502 bool IsValid() const 503 { 504 return Length() > MinimumSize(); 505 // There is no maximum size, as the last entry spans until the 506 // end of the block 507 } 508 509 static size_t MinimumSize() 510 { 511 return sizeof(ext2_dir_entry) - EXT2_NAME_LENGTH; 512 } 513 } _PACKED; 514 515 // file types 516 #define EXT2_TYPE_UNKNOWN 0 517 #define EXT2_TYPE_FILE 1 518 #define EXT2_TYPE_DIRECTORY 2 519 #define EXT2_TYPE_CHAR_DEVICE 3 520 #define EXT2_TYPE_BLOCK_DEVICE 4 521 #define EXT2_TYPE_FIFO 5 522 #define EXT2_TYPE_SOCKET 6 523 #define EXT2_TYPE_SYMLINK 7 524 525 #define EXT2_XATTR_MAGIC 0xea020000 526 #define EXT2_XATTR_ROUND ((1 << 2) - 1) 527 #define EXT2_XATTR_NAME_LENGTH 255 528 529 #define EXT2_XATTR_INDEX_USER 1 530 531 struct ext2_xattr_header { 532 uint32 magic; 533 uint32 refcount; 534 uint32 blocks; // must be 1 for ext2 535 uint32 hash; 536 uint32 reserved[4]; // zero 537 538 bool IsValid() const 539 { 540 return B_LENDIAN_TO_HOST_INT32(magic) == EXT2_XATTR_MAGIC 541 && B_LENDIAN_TO_HOST_INT32(blocks) == 1 542 && refcount <= 1024; 543 } 544 545 void Dump() const { 546 for (unsigned int i = 0; i < Length(); i++) 547 dprintf("%02x ", ((uint8 *)this)[i]); 548 dprintf("\n"); 549 } 550 551 static size_t Length() 552 { 553 return sizeof(ext2_xattr_header); 554 } 555 }; 556 557 struct ext2_xattr_entry { 558 uint8 name_length; 559 uint8 name_index; 560 uint16 value_offset; 561 uint32 value_block; // must be zero for ext2 562 uint32 value_size; 563 uint32 hash; 564 char name[EXT2_XATTR_NAME_LENGTH]; 565 566 uint8 NameLength() const { return name_length; } 567 uint8 NameIndex() const { return name_index; } 568 uint16 ValueOffset() const { return 569 B_LENDIAN_TO_HOST_INT16(value_offset); } 570 uint32 ValueSize() const { return 571 B_LENDIAN_TO_HOST_INT32(value_size); } 572 573 // padded sizes 574 uint32 Length() const { return (MinimumSize() + NameLength() 575 + EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND; } 576 577 bool IsValid() const 578 { 579 return NameLength() > 0 && value_block == 0; 580 // There is no maximum size, as the last entry spans until the 581 // end of the block 582 } 583 584 void Dump(bool full=false) const { 585 for (unsigned int i = 0; i < (full ? sizeof(this) : MinimumSize()); i++) 586 dprintf("%02x ", ((uint8 *)this)[i]); 587 dprintf("\n"); 588 } 589 590 static size_t MinimumSize() 591 { 592 return sizeof(ext2_xattr_entry) - EXT2_XATTR_NAME_LENGTH; 593 } 594 } _PACKED; 595 596 597 struct file_cookie { 598 bigtime_t last_notification; 599 off_t last_size; 600 int open_mode; 601 }; 602 603 #define EXT2_OPEN_MODE_USER_MASK 0x7fffffff 604 605 #define INODE_NOTIFICATION_INTERVAL 10000000LL 606 607 608 extern fs_volume_ops gExt2VolumeOps; 609 extern fs_vnode_ops gExt2VnodeOps; 610 611 #endif // EXT2_H 612