Module: NTFS
- Included in:
- FileObject
- Defined in:
- lib/fs/ntfs/boot_sect.rb,
lib/fs/ntfs/utils.rb,
lib/fs/ntfs/data_run.rb,
lib/fs/ntfs/mft_entry.rb,
lib/fs/ntfs/attrib_data.rb,
lib/fs/ntfs/attrib_type.rb,
lib/fs/MiqFS/modules/NTFS.rb,
lib/fs/ntfs/attrib_bitmap.rb,
lib/fs/ntfs/attrib_header.rb,
lib/fs/ntfs/attrib_file_name.rb,
lib/fs/ntfs/attrib_object_id.rb,
lib/fs/ntfs/attrib_index_root.rb,
lib/fs/ntfs/index_node_header.rb,
lib/fs/ntfs/attrib_volume_name.rb,
lib/fs/ntfs/index_record_header.rb,
lib/fs/ntfs/directory_index_node.rb,
lib/fs/ntfs/attrib_attribute_list.rb,
lib/fs/ntfs/attrib_index_allocation.rb,
lib/fs/ntfs/attrib_volume_information.rb,
lib/fs/ntfs/attrib_standard_information.rb
Overview
NTFS file system interface to MiqFS.
Defined Under Namespace
Modules: Utils Classes: AttribData, AttribHeader, AttributeList, Bitmap, BootSect, DataRun, DirectoryIndexNode, FileName, FileObject, IndexAllocation, IndexNodeHeader, IndexRecordHeader, IndexRoot, MftEntry, ObjectId, StandardInformation, VolumeInformation, VolumeName
Constant Summary collapse
- BOOT_PARAMETERS_BLOCK =
The boot parameters block, sector 0 byte 0 of a bootable volume.
BinaryStruct.new([ 'a3', 'jmp_boot_loader', # Jump to boot loader 'a8', 'oem_name', # OEM Name (should be 'NTFS ') # BIOS Parameter Block 'S', 'bytes_per_sector', # Bytes per sector. The size of a hardware sector. For most disks used in the United States, the value of this field is 512. 'C1', 'sectors_per_cluster', # Sectors per cluster 'S', 'reserved_sectors', # Reserved sectors. Always 0 because NTFS places the boot sector at the beginning of the partition. If the value is not 0, NTFS fails to mount the volume. 'C1', 'fats', # Value must be 0 or NTFS fails to mount the volume 'S', 'root_entries', # Value must be 0 or NTFS fails to mount the volume 'S', 'sectors', # Value must be 0 or NTFS fails to mount the volume 'C1', 'media_descriptor', # Provides information about the media being used. A value of 0xF8 indicates a hard disk and 0xF0 indicates a high-density 3.5-inch floppy disk. Media descriptor entries are a legacy of MS-DOS FAT16 disks and are not used in Windows Server 2003. 'S', 'sectors_per_fat', # Value must be 0 or NTFS fails to mount the volume 'S', 'sectors_per_track', # Required to boot Windows 'S', 'number_of_heads', # Required to boot Windows 'L', 'hidden_sectors', # Offset to the start of the partition relative to the disk in sectors. Required to boot Windows 'L', 'large_sectors', # Must be 0 # Extended BIOS Parameter Block 'C1', 'physical_drive', # 0x00 floppy, 0x80 hard disk 'C1', 'current_head', # Must be 0 'C1', 'extended_boot_signature', # 0x80 'C1', 'reserved2', 'Q', 'sectors_per_volume', # Number of sectors in volume. Gives maximum volume size of 2^63 sectors. Assuming standard sector size of 512 bytes, the maximum byte size is approx. 4.7x10^21 bytes. 'Q', 'mft_lcn', # Logical Cluster Number for the File $MFT. Identifies the location of the MFT by using its logical cluster number. 'Q', 'mftmirr_lcn', # Logical Cluster Number for the File $MFTMirr. Identifies the location of the mirrored copy of the MFT by using its logical cluster number. 'c1', 'clusters_per_mft_record', # Mft record size in clusters. NTFS creates a file record for each file and a folder record for each folder that is created on an NTFS volume. Files and folders smaller than this size are contained within the MFT. If this number is positive (up to 7F), then it represents clusters per MFT record. If the number is negative (80 to FF), then the size of the file record is 2 raised to the absolute value of this number. 'a3', 'reserved0', 'c1', 'clusters_per_index_record', # Index block size in clusters. The size of each index buffer, which is used to allocate space for directories. If this number is positive (up to 7F), then it represents clusters per MFT record. If the number is negative (80 to FF), then the size of the file record is 2 raised to the absolute value of this number. 'a3', 'reserved1', 'Q', 'volume_serial_number', # Seems like only the low 32-bits are used 'L', 'checksum', # Boot sector checksum 'a426', 'boot_code', # Boot loader 'S', 'signature', # Sanity check: always 0xaa55 ])
- SIZEOF_BOOT_PARAMETERS_BLOCK =
BOOT_PARAMETERS_BLOCK.size
- NTFS_MAGIC =
0xaa55
- FILE_RECORD =
One MFT file record, also called MFT Entry.
BinaryStruct.new([ 'a4', 'signature', # Always 'FILE' 'S', 'usa_offset', # Offset to the Update Sequence Array (usa) from the start of the ntfs record. 'S', 'usa_count', # Number of u16 sized entries in the usa including the Update Sequence Number (usn), # thus the number of fixups is the usa_count minus 1. 'Q', 'lsn', # $LogFile sequence number for this record. # Changed every time the record is modified 'S', 'seq_num', # Number of times this MFT record has been reused 'S', 'hard_link_count', # Number of links to this file 'S', 'offset_to_attrib', # Byte offset to the first attribute in this mft record from the start of the mft record. # NOTE: Must be aligned to 8-byte boundary. 'S', 'flags', # File record flags 'L', 'bytes_in_use', # Number of bytes used in this mft record. # NOTE: Must be aligned to 8-byte boundary. 'L', 'bytes_allocated', # Number of bytes allocated for this mft record. This should be equal # to the mft record size 'Q', 'base_mft_record', # This is zero for base mft records. When it is not zero it is a mft reference # pointing to the base mft record to which this record belongs (this is then # used to locate the attribute list attribute present in the base record which # describes this extension record and hence might need modification when the # extension record itself is modified, also locating the attribute list also # means finding the other potential extents, belonging to the non-base mft record). 'S', 'next_attrib_id', # The instance number that will be assigned to the next attribute added to this # mft record. # NOTE: Incremented each time after it is used. # NOTE: Every time the mft record is reused this number is set to zero. # NOTE: The first instance number is always 0 # # The 2 fields below are specific to NTFS 3.1+ (Windows XP and above): # 'S', 'unused1', # Reserved/alignment 'L', 'mft_rec_num', # Number of this mft record. # When (re)using the mft record, we place the update sequence array at this # offset, i.e. before we start with the attributes. This also makes sense, # otherwise we could run into problems with the update sequence array # containing in itself the last two bytes of a sector which would mean that # multi sector transfer protection wouldn't work. As you can't protect data # by overwriting it since you then can't get it back... # When reading we obviously use the data from the ntfs record header. # 'S', 'fixup_seq_num', # Magic word at end of sector ])
- SIZEOF_FILE_RECORD =
Here follows the fixup array (WORD).
FILE_RECORD.size
- AT_STANDARD_INFORMATION =
The _ATTRIB_TYPE enumeration. Every entry in the MFT is made of a list of attributes. These are the attrib types.
0x00000010
- AT_ATTRIBUTE_LIST =
Base data: MAC times, version, owner, security, quota, permissions
0x00000020
- AT_FILE_NAME =
Used for a list of attributes that spans one MFT file record
0x00000030
- AT_VOLUME_VERSION =
MAC times, size, flags, (and of course) file name
0x00000040
- AT_OBJECT_ID =
No information
0x00000040
- AT_SECURITY_DESCRIPTOR =
Object GUID, birth GUIDs & domain GUID
0x00000050
- AT_VOLUME_NAME =
User & Group SIDs, system and descr ACLs
0x00000060
- AT_VOLUME_INFORMATION =
Vol name (label)
0x00000070
- AT_DATA =
Major, minor & flags (chkdsk & others)
0x00000080
- AT_INDEX_ROOT =
File data, aux stream data
0x00000090
- AT_INDEX_ALLOCATION =
Root of an index - usually root of directory
0x000000a0
- AT_BITMAP =
Larger index structures use index allocation for overflow
0x000000b0
- AT_SYMBOLIC_LINK =
Cluster allocation bitmap
0x000000c0
- AT_REPARSE_POINT =
Haven’t seen it yet
0x000000c0
- AT_EA_INFORMATION =
Reparse data
0x000000d0
- AT_EA =
Extended attribute information
0x000000e0
- AT_PROPERTY_SET =
Extended attribute data
0x000000f0
- AT_LOGGED_UTILITY_STREAM =
Haven’t seen it
0x00000100
- AT_END =
Encrypted File System data
0xffffffff
- TypeName =
NT names for the attributes.
{ AT_STANDARD_INFORMATION => '$STANDARD_INFORMATION', AT_ATTRIBUTE_LIST => '$ATTRIBUTE_LIST', AT_FILE_NAME => '$FILE_NAME', # AT_VOLUME_VERSION => '$VOLUME_VERSION', # This type is depreciated. AT_OBJECT_ID => '$OBJECT_ID', AT_SECURITY_DESCRIPTOR => '$SECURITY_DESCRIPTOR', AT_VOLUME_NAME => '$VOLUME_NAME', AT_VOLUME_INFORMATION => '$VOLUME_INFORMATION', AT_DATA => '$DATA', AT_INDEX_ROOT => '$INDEX_ROOT', AT_INDEX_ALLOCATION => '$INDEX_ALLOCATION', AT_BITMAP => '$BITMAP', # AT_SYMBOLIC_LINK => '$SYMBOLIC_LINK', # This type is depreciated. AT_REPARSE_POINT => '$REPARSE_POINT', AT_EA_INFORMATION => '$EA_INFORMATION', AT_EA => '$EA', AT_PROPERTY_SET => '$PROPERTY_SET', AT_LOGGED_UTILITY_STREAM => '$LOGGED_UTILITY_STREAM' }
- DEF_CACHE_SIZE =
Default index cache size
50
- STANDARD_ATTRIBUTE_HEADER =
Standard attribute header. Each attribute begins with one of these.
BinaryStruct.new([ 'L', 'attrib_type', # The (32-bit) type of the attribute 'L', 'length', # Byte size of the resident part of the attribute # (aligned to 8-byte boundary). Used to get to the next attribute 'C', 'non_resident', # If 0, attribute is resident. # If 1, attribute is non-resident 'C', 'name_length', # Unicode character size of name of attribute. # 0 if unnamed 'S', 'name_offset', # If name_length != 0, the byte offset to the beginning of the name # from the attribute record. Note that the name is stored as a # Unicode string. 'S', 'flags', # Attribute flags (see AF_ below) 'S', 'attrib_id', # The instance of this attribute record. This number is unique within this mft record ])
- SIZEOF_STANDARD_ATTRIBUTE_HEADER =
STANDARD_ATTRIBUTE_HEADER.size
- SAH_RESIDENT =
The resident struct.
BinaryStruct.new([ 'L', 'value_length', # Byte size of attribute value 'S', 'value_offset', # Byte offset of the attribute value from the start of the attribute record 'C', 'resident_flags', # Flags of resident attributes (8-bit) 'C', nil, # Reserved/alignment to 8-byte boundary ])
- SIZEOF_SAH_RESIDENT =
SAH_RESIDENT.size
- SAH_NONRESIDENT =
The non-resident struct.
BinaryStruct.new([ 'Q', 'first_vcn', # Lowest valid virtual cluster number for this portion of the attribute value # or 0 if this is the only extent (usually the case). - Only when an attribute # list is used does lowest_vcn != 0 ever occur 'Q', 'last_vcn', # Highest valid vcn of this extent of the attribute value. - Usually there is # only one portion, so this usually equals the attribute value size in clusters # minus 1. Can be -1 for zero length files. Can be 0 for "single extent" attributes 'S', 'mapping_pairs_offset', # Byte offset from the beginning of the structure to the mapping pairs array # which contains the mappings between the vcns and the logical cluster numbers (lcns). # When creating, place this at the end of this record header aligned to 8-byte boundary. 'S', 'compression_unit', # The compression unit expressed as the log to the base 2 of the number of # clusters in a compression unit. 0 means not compressed. (This effectively limits the # compression unit size to be a power of two clusters.) WinNT4 only uses a value of 4. 'L', nil, # Align to 8-byte boundary # The sizes below are only used when lowest_vcn is zero, as otherwise it would # be difficult to keep them up-to-date. 'Q', 'allocated_size', # Byte size of disk space allocated to hold the attribute value. Always is a # multiple of the cluster size. When a file is compressed, this field is a # multiple of the compression block size (2^compression_unit) and it # represents the logically allocated space rather than the actual on disk usage. 'Q', 'data_size', # Byte size of the attribute value. # Can be larger than allocated_size if attribute value is compressed or sparse 'Q', 'initialized_size', # Byte size of initialized portion of the attribute value. Usually equals data_size ])
- SIZEOF_SAH_NONRESIDENT =
If the attribute is named (name_length is not 0), then the name (in UNICODE) follows here. Here follows the attribute data (if resident) or the data runs (if non-resident).
SAH_NONRESIDENT.size
- ATTRIB_FILE_NAME =
FILE_NAME_ATTR - Attribute: Filename (0x30)
NOTE: Always resident. NOTE: All fields, except the parent_directory, are only updated when the
filename is changed. Until then, they just become out of sync with reality and the more up to date values are present in the standard information attribute.
NOTE: There is conflicting information about the meaning of each of the time
fields but the meaning as defined below has been verified to be correct by practical experimentation on Windows NT4 SP6a and is hence assumed to be the one and only correct interpretation.
The $FILE_NAME attribute.
BinaryStruct.new([ 'Q', 'ref_to_parent_dir', # Directory this filename is referenced from 'Q', 'time_created', # Time file was created 'Q', 'time_altered', # Time the data attribute was last modified 'Q', 'time_mft_changed', # Time this mft record was last modified 'Q', 'time_read', # Last time this mft record was accessed 'Q', 'allocated_size', # Byte size of on-disk allocated space for the data attribute. # So for normal $DATA, this is the allocated_size from the unnamed # $DATA attribute and for compressed and/or sparse $DATA, this is # the compressed_size from the unnamed $DATA attribute. # NOTE: This is a multiple of the cluster size. 'Q', 'data_size', # Byte size of actual data in data attribute 'L', 'flags', # Flags describing the file 'L', 'reparse_point_tag', # Type of reparse point, present only in reparse points and only if there are no EAs 'C1', 'name_length', # Length of file name in (Unicode) characters 'C1', 'namespace', # Namespace of the file name ])
- SIZEOF_ATTRIB_FILE_NAME =
File name (in UNICODE) is appended to the previous structure.
ATTRIB_FILE_NAME.size
- ATTRIB_INDEX_ROOT =
INDEX_ROOT - Attribute: Index root (0x90).
NOTE: Always resident.
This is followed by a sequence of index entries (INDEX_ENTRY structures) as described by the index header.
When a directory is small enough to fit inside the index root then this is the only attribute describing the directory. When the directory is too large to fit in the index root, on the other hand, two additional attributes are present: an index allocation attribute, containing sub-nodes of the B+ directory tree (see below), and a bitmap attribute, describing which virtual cluster numbers (vcns) in the index allocation attribute are in use by an index block.
NOTE: The root directory (FILE_root) contains an entry for itself. Other directories do not contain entries for themselves, though.
BinaryStruct.new([ 'L', 'type', # Type of the indexed attribute. Is FILE_NAME for directories, zero # for view indexes. No other values allowed. 'L', 'collation_rule', # Collation rule used to sort the index entries. If type is $FILE_NAME, # this must be COLLATION_FILE_NAME 'L', 'index_block_size', # Size of index block in bytes (in the index allocation attribute) 'C', 'clusters_per_index_block', # Size of index block in clusters (in the index allocation attribute), # when an index block is >= than a cluster, otherwise sectors per index block 'a3', nil, # Reserved/align to 8-byte boundary ])
- SIZEOF_ATTRIB_INDEX_ROOT =
Here follows a node header.
ATTRIB_INDEX_ROOT.size
- INDEX_NODE_HEADER =
INDEX_HEADER
This is the header for indexes, describing the INDEX_ENTRY records, which follow the INDEX_HEADER. Together the index header and the index entries make up a complete index.
IMPORTANT NOTE: The offset, length and size structure members are counted relative to the start of the index header structure and not relative to the start of the index root or index allocation structures themselves.
BinaryStruct.new([ 'L', 'entries_offset', # Byte offset from the INDEX_HEADER to first INDEX_ENTRY, aligned to 8-byte boundary 'L', 'index_length', # Data size in byte of the INDEX_ENTRY's, including the INDEX_HEADER, aligned to 8. 'L', 'allocated_size', # Offset to end of allocated index entry list buffer (relative to start of node header). # # For the index root attribute, the above two numbers are always # equal, as the attribute is resident and it is resized as needed. # # For the index allocation attribute, the attribute is not resident # and the allocated_size is equal to the index_block_size specified # by the corresponding INDEX_ROOT attribute minus the INDEX_BLOCK # size not counting the INDEX_HEADER part (i.e. minus -24). # 'L', 'flags', # See NH_ below. ])
- SIZEOF_INDEX_NODE_HEADER =
INDEX_NODE_HEADER.size
- INDEX_RECORD_HEADER =
INDEX_BLOCK - Attribute: Index allocation (0xa0).
NOTE: Always non-resident (doesn’t make sense to be resident anyway!).
This is an array of index blocks. Each index block starts with an INDEX_BLOCK structure containing an index header, followed by a sequence of index entries (INDEX_ENTRY structures), as described by the INDEX_HEADER.
BinaryStruct.new([ 'a4', 'signature', # Always 'INDX' 'S', 'usa_offset', # Offset to the Update Sequence Array (usa) from the start of the ntfs record. 'S', 'usa_count', # Number of u16 sized entries in the usa including the Update Sequence Number (usn), # thus the number of fixups is the usa_count minus 1. 'Q', 'lsn', # $LogFile sequence number of the last modification of this index block 'Q', 'index_block_vcn', # VCN of this record in the full index stream. ])
- SIZEOF_INDEX_RECORD_HEADER =
Here follows the fixup array. Here follows an index node header.
INDEX_RECORD_HEADER.size
- DIR_INDEX_NODE =
BinaryStruct.new([ 'Q', 'mft_ref', # MFT file reference for file name (goofy ref). 'S', 'length', # Length of entry. 'S', 'content_len', # Length of $FILE_NAME attrib 'L', 'flags', # See IN_ below (note: these will eventually become general flags) ])
- SIZEOF_DIR_INDEX_NODE =
Here follows a $FILE_NAME attribute if content_len > 0. Last 8 bytes starting on 8 byte boundary are the VCN of the child node in $INDEX_ALLOCATION (if IN_HAS_CHILD is set).
DIR_INDEX_NODE.size
- ATTRIB_ATTRIBUTE_LIST =
ATTR_LIST_ENTRY - Attribute: Attribute list (0x20).
-
Can be either resident or non-resident.
-
Value consists of a sequence of variable length, 8-byte aligned,
ATTR_LIST_ENTRY records.
-
The attribute list attribute contains one entry for each attribute of
the file in which the list is located, except for the list attribute itself. The list is sorted: first by attribute type, second by attribute name (if present), third by instance number. The extents of one non-resident attribute (if present) immediately follow after the initial extent. They are ordered by lowest_vcn and have their instance set to zero. It is not allowed to have two attributes with all sorting keys equal.
-
Further restrictions:
-
If not resident, the vcn to lcn mapping array has to fit inside the base mft record.
-
The attribute list attribute value has a maximum size of 256kb. This is imposed by the Windows cache manager.
-
Attribute lists are only used when the attributes of mft record do not
fit inside the mft record despite all attributes (that can be made non-resident) having been made non-resident. This can happen e.g. when:
- File has a large number of hard links (lots of file name attributes present). - The mapping pairs array of some non-resident attribute becomes so large due to fragmentation that it overflows the mft record. - The security descriptor is very complex (not applicable to NTFS 3.0 volumes). - There are many named streams.
-
BinaryStruct.new([ 'L', 'attrib_type', # Type of referenced attribute 'S', 'length', # Byte size of this entry 'C', 'name_length', # Size in Unicode chars of the name of the attribute or 0 if unnamed 'C', 'name_offset', # Byte offset (from start of entry) to beginning of attribute name 'Q', 'first_vcn', # Lowest virtual cluster number of this portion of the attribute value. # This is usually 0. It is non-zero for the case where one attribute # does not fit into one mft record and thus several mft records are # allocated to hold this attribute. In the latter case, each mft # record holds one extent of the attribute and there is one attribute # list entry for each extent. # NOTE: This is DEFINITELY a signed value! The windows driver uses cmp, followed # by jg when comparing this, thus it treats it as signed. 'Q', 'mft_reference', # The reference of the mft record holding the ATTR_RECORD for this # portion of the attribute value 'S', 'attrib_id', # If lowest_vcn = 0, the instance of the attribute being referenced; otherwise 0. ])
- SIZEOF_ATTRIB_ATTRIBUTE_LIST =
ATTRIB_ATTRIBUTE_LIST.size
- ATTRIB_VOLUME_INFORMATION =
VOLUME_INFORMATION - Attribute: Volume information (0x70).
NOTE: Always resident. NOTE: Present only in FILE_Volume. NOTE: Windows 2000 uses NTFS 3.0 while Windows NT4 service pack 6a uses NTFS 1.2.
BinaryStruct.new([ 'Q', nil, # No information. 'C1', 'ver_major', # File system major version number. 'C1', 'ver_minor', # File system minor version number. 'S', 'flags', # Volume flags (see VF_ below). ])
- ATTRIB_STANDARD_INFORMATION =
STANDARD_INFORMATION - Attribute: Standard information (0x10).
NOTE: Always resident. NOTE: Present in all base file records on a volume. NOTE: There is conflicting information about the meaning of each of the time
fields but the meaning as defined below has been verified to be correct by practical experimentation on Windows NT4 SP6a and is hence assumed to be the one and only correct interpretation.
BinaryStruct.new([ 'Q', 'time_created', # Time file was created. Updated when a filename is changed(?) 'Q', 'time_altered', # Time the data attribute was last modified 'Q', 'time_mft_changed', # Time this mft record was last modified 'Q', 'time_read', # Approximate time when the file was last accessed (obviously # this is not updated on read-only volumes). In Windows this # is only updated when accessed if some time delta has passed # since the last update. Also, last access times updates can be # disabled altogether for speed 'L', 'dos_permissions', # These are the flags we know as 'attributes' (see FP_ below) # # If a volume has been upgraded from a previous NTFS version, then thes following # fields are present only if the file has been accessed since the upgrade. # Recognize the difference by comparing the length of the resident attribute # value. If it is 48, then the following fields are missing. If it is 72 then # the fields are present. Maybe just check like this: # if (resident.ValueLength < sizeof(STANDARD_INFORMATION)) { # Assume NTFS 1.2- format. # If (volume version is 3.0+) # Upgrade attribute to NTFS 3.0 format. # else # Use NTFS 1.2- format for access. # } else # Use NTFS 3.0 format for access. # Only problem is that it might be legal to set the length of the value to # arbitrarily large values thus spoiling this check. - But chkdsk probably # views that as a corruption, assuming that it behaves like this for all # attributes. # 'L', 'max_versions', # Maximum allowed versions for file. Zero if version numbering is disabled. 'L', 'ver_num', # This file's version (if any). Set to zero if maximum_versions is zero 'L', 'class_id', # Class id from bidirectional class id index (?) 'L', 'owner_id', # Owner_id of the user owning the file. Translate via $Q index # in FILE_Extend /$Quota to the quota control entry for the user # owning the file. Zero if quotas are disabled. 'L', 'security_id', # This is a key in the $SII index and the $SDS data stream in the file $Secure 'Q', 'quota_charged', # Number of bytes this file uses from the user's quota - total size of all streams - if zero then quotas are disabled 'Q', 'update_seq_num', # This is a direct index into the file $UsnJrnl - if zero then the USN journal is disabled ])
Instance Attribute Summary collapse
-
#boot_sector ⇒ Object
Members (these become members of an MiqFS instance).
-
#cache_hits ⇒ Object
Members (these become members of an MiqFS instance).
-
#drive_root ⇒ Object
Members (these become members of an MiqFS instance).
-
#index_cache ⇒ Object
Members (these become members of an MiqFS instance).
Instance Method Summary collapse
-
#fs_dirEntries(p) ⇒ Object
Returns String array of all names, sans path.
-
#fs_fileAtime(p) ⇒ Object
Returns Ruby Time object.
-
#fs_fileAtime_obj(fobj) ⇒ Object
Returns a Ruby Time object.
-
#fs_fileClose(_fobj) ⇒ Object
Unless there’s a way to explicitly destroy an object there’s nothing to do here.
-
#fs_fileCtime(p) ⇒ Object
Returns Ruby Time object.
-
#fs_fileCtime_obj(fobj) ⇒ Object
Returns a Ruby Time object.
-
#fs_fileDirectory?(p) ⇒ Boolean
Returns true if name is a directory.
-
#fs_fileExists?(p) ⇒ Boolean
Returns true if name exists, false if not.
-
#fs_fileFile?(p) ⇒ Boolean
Returns true if name is a regular file.
-
#fs_fileMtime(p) ⇒ Object
Returns Ruby Time object.
-
#fs_fileMtime_obj(fobj) ⇒ Object
Returns a Ruby Time obect.
-
#fs_fileOpen(p, _mode = "r") ⇒ Object
New FileObject instance.
-
#fs_fileRead(fobj, len) ⇒ Object
Returns a Ruby String object.
-
#fs_fileSeek(fobj, offset, whence) ⇒ Object
Seek to the requested position.
-
#fs_fileSize(p) ⇒ Object
Returns size in bytes.
-
#fs_fileSize_obj(fobj) ⇒ Object
File size the faster way.
-
#fs_freeBytes ⇒ Object
Returns free space on file system in bytes.
-
#fs_init ⇒ Object
File system interface.
-
#fs_isSymLink?(_p) ⇒ Boolean
Returns true if name is a symbolic link.
- #getVolumeInfo ⇒ Object
-
#ifs_getDir(p, miqfs = nil) ⇒ Object
Return an index of a path.
-
#ifs_getFile(p, miqfs = nil) ⇒ Object
Return DirectoryIndexNode for a given file or nil if not exist.
-
#ifs_getIndex(names, miqfs) ⇒ Object
Return DirectoryIndexNode for given directory or nil if not exist.
-
#unnormalizePath(p) ⇒ Object
Wack leading drive leter & colon.
Instance Attribute Details
#boot_sector ⇒ Object
Members (these become members of an MiqFS instance).
12 13 14 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 12 def boot_sector @boot_sector end |
#cache_hits ⇒ Object
Members (these become members of an MiqFS instance).
12 13 14 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 12 def cache_hits @cache_hits end |
#drive_root ⇒ Object
Members (these become members of an MiqFS instance).
12 13 14 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 12 def drive_root @drive_root end |
#index_cache ⇒ Object
Members (these become members of an MiqFS instance).
12 13 14 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 12 def index_cache @index_cache end |
Instance Method Details
#fs_dirEntries(p) ⇒ Object
Returns String array of all names, sans path.
75 76 77 78 79 80 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 75 def fs_dirEntries(p) # Get path index (directory). index = ifs_getDir(p) return nil if index.nil? index.globNames end |
#fs_fileAtime(p) ⇒ Object
Returns Ruby Time object. NOTE: I did find files with 0 time, but they should never appear in this context.
123 124 125 126 127 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 123 def fs_fileAtime(p) file = ifs_getFile(p) return nil if file.nil? file.afn.aTime end |
#fs_fileAtime_obj(fobj) ⇒ Object
Returns a Ruby Time object.
151 152 153 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 151 def fs_fileAtime_obj(fobj) fobj.din.afn.aTime end |
#fs_fileClose(_fobj) ⇒ Object
Unless there’s a way to explicitly destroy an object there’s nothing to do here.
192 193 194 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 192 def fs_fileClose(_fobj) fobj = nil end |
#fs_fileCtime(p) ⇒ Object
Returns Ruby Time object.
130 131 132 133 134 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 130 def fs_fileCtime(p) file = ifs_getFile(p) return nil if file.nil? file.afn.cTime end |
#fs_fileCtime_obj(fobj) ⇒ Object
Returns a Ruby Time object.
156 157 158 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 156 def fs_fileCtime_obj(fobj) fobj.din.afn.cTime end |
#fs_fileDirectory?(p) ⇒ Boolean
Returns true if name is a directory.
101 102 103 104 105 106 107 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 101 def fs_fileDirectory?(p) file = ifs_getFile(p) return false if file.nil? file.resolve(boot_sector) return true if file.isDir? false end |
#fs_fileExists?(p) ⇒ Boolean
Returns true if name exists, false if not.
83 84 85 86 87 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 83 def fs_fileExists?(p) file = ifs_getFile(p) return false if file.nil? true end |
#fs_fileFile?(p) ⇒ Boolean
Returns true if name is a regular file. NOTE: If a name isn’t a directory, it’s always a file - so far… (it could be a reparse point [a.k.a. hard link], but that’s for later).
92 93 94 95 96 97 98 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 92 def fs_fileFile?(p) file = ifs_getFile(p) return false if file.nil? file.resolve(boot_sector) return false if file.isDir? true end |
#fs_fileMtime(p) ⇒ Object
Returns Ruby Time object.
137 138 139 140 141 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 137 def fs_fileMtime(p) file = ifs_getFile(p) return nil if file.nil? file.afn.mTime end |
#fs_fileMtime_obj(fobj) ⇒ Object
Returns a Ruby Time obect.
161 162 163 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 161 def fs_fileMtime_obj(fobj) fobj.din.afn.mTime end |
#fs_fileOpen(p, _mode = "r") ⇒ Object
New FileObject instance. NOTE: FileObject must have access to NTFS members. This is kind of like a ‘skip this’ thing. NTFS methods use stuff owned by MiqFS, so this is necessary.
174 175 176 177 178 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 174 def fs_fileOpen(p, _mode = "r") fobj = FileObject.new(p, self) fobj.open fobj end |
#fs_fileRead(fobj, len) ⇒ Object
Returns a Ruby String object.
186 187 188 189 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 186 def fs_fileRead(fobj, len) return nil if fobj.data.nil? fobj.data.read(len) end |
#fs_fileSeek(fobj, offset, whence) ⇒ Object
Seek to the requested position
181 182 183 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 181 def fs_fileSeek(fobj, offset, whence) fobj.seek(offset, whence) end |
#fs_fileSize(p) ⇒ Object
Returns size in bytes.
115 116 117 118 119 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 115 def fs_fileSize(p) file = ifs_getFile(p) return nil if file.nil? file.afn.length end |
#fs_fileSize_obj(fobj) ⇒ Object
File size the faster way.
166 167 168 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 166 def fs_fileSize_obj(fobj) fobj.din.afn.length end |
#fs_freeBytes ⇒ Object
Returns free space on file system in bytes.
68 69 70 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 68 def fs_freeBytes boot_sector.freeBytes end |
#fs_init ⇒ Object
File system interface.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 43 def fs_init self.fsType = "NTFS" # Initialize Boot Sector @dobj.seek(0, IO::SEEK_SET) self.boot_sector = BootSect.new(@dobj) boot_sector.setup self.drive_root = boot_sector.rootDir # Init cache. self.index_cache = LruHash.new(DEF_CACHE_SIZE) self.cache_hits = 0 # Expose fsId & volName. @fsId, @volName = getVolumeInfo end |
#fs_isSymLink?(_p) ⇒ Boolean
Returns true if name is a symbolic link.
110 111 112 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 110 def fs_isSymLink?(_p) false end |
#getVolumeInfo ⇒ Object
60 61 62 63 64 65 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 60 def getVolumeInfo vi = boot_sector.volumeInfo volName = vi["name"] fsId = vi["objectId"] return fsId, volName end |
#ifs_getDir(p, miqfs = nil) ⇒ Object
Return an index of a path. Raise error if path doesn’t exist.
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 239 def ifs_getDir(p, miqfs = nil) $log.debug "NTFS.ifs_getDir >> p=#{p}" if $log && $log.debug? # If this is being called from a FileObject instance, then MiqFS owns contained instance members. # If this is being called from an NTFS module method, then self owns contained instance members. miqfs = self if miqfs.nil? # Wack leading drive. p = unnormalizePath(p).downcase # Get an array of directory names, kill off the first (it's always empty). names = p.split(/[\\\/]/) names.shift # Get the index for this directory index = ifs_getIndex(names, miqfs) $log.debug "NTFS.ifs_getDir << #{index.nil? ? "Directory not found " : ""}p=#{p}" if $log && $log.debug? index end |
#ifs_getFile(p, miqfs = nil) ⇒ Object
Return DirectoryIndexNode for a given file or nil if not exist.
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 199 def ifs_getFile(p, miqfs = nil) $log.debug "NTFS.ifs_getFile >> p=#{p}" if $log && $log.debug? # If this is being called from a FileObject instance, then MiqFS owns contained instance members. # If this is being called from an NTFS module method, then self owns contained instance members. miqfs = self if miqfs.nil? # Wack leading drive. p = unnormalizePath(p).downcase # Get directory & file as separate strings. dir = p.split(/[\\\/]/) fname = dir[dir.size - 1] fname = "." if fname.nil? # Special case: if fname is nil then dir is root. dir = dir.size > 2 ? dir[0...dir.size - 1].join('/') : '/' # Check for this file in the cache. cache_name = "#{dir == '/' ? '' : dir}/#{fname}" if miqfs.index_cache.key?(cache_name) miqfs.cache_hits += 1 file = miqfs.index_cache[cache_name] $log.debug "NTFS.ifs_getFile << (cached) p=#{p} din=#{file}" if $log && $log.debug? return file end # Look for file in dir, but don't error if it doesn't exist. # NOTE: if p is a directory that's ok, find it. file = nil index = ifs_getDir(dir, miqfs) unless index.nil? $log.debug "NTFS.ifs_getFile -- getting din for #{fname} in <#{index.class.name}>" if $log && $log.debug? file = index.find(fname) end $log.debug "NTFS.ifs_getFile << p=#{p} din=#{file}" if $log && $log.debug? miqfs.index_cache[cache_name] = file end |
#ifs_getIndex(names, miqfs) ⇒ Object
Return DirectoryIndexNode for given directory or nil if not exist.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 260 def ifs_getIndex(names, miqfs) return miqfs.drive_root if names.empty? # Check for this path in the cache. fname = names.join('/') if miqfs.index_cache.key?(fname) miqfs.cache_hits += 1 return miqfs.index_cache[fname] end name = names.pop index = ifs_getIndex(names, miqfs) return nil if index.nil? din = index.find(name) return nil if din.nil? index = din.resolve(miqfs.boot_sector).indexRoot return nil if index.nil? miqfs.index_cache[fname] = index end |
#unnormalizePath(p) ⇒ Object
Wack leading drive leter & colon.
284 285 286 |
# File 'lib/fs/MiqFS/modules/NTFS.rb', line 284 def unnormalizePath(p) p[1] == 58 ? p[2, p.size] : p end |