Module: Ext3

Included in:
FileObject
Defined in:
lib/fs/MiqFS/modules/Ext3.rb,
lib/fs/ext3/inode.rb,
lib/fs/ext3/directory.rb,
lib/fs/ext3/file_data.rb,
lib/fs/ext3/superblock.rb,
lib/fs/ext3/alloc_bitmap.rb,
lib/fs/ext3/ex_attrib_name.rb,
lib/fs/ext3/directory_entry.rb,
lib/fs/ext3/hash_tree_entry.rb,
lib/fs/ext3/posix_acl_entry.rb,
lib/fs/ext3/ex_attrib_header.rb,
lib/fs/ext3/hash_tree_header.rb,
lib/fs/ext3/posix_acl_header.rb,
lib/fs/ext3/block_pointers_path.rb,
lib/fs/ext3/group_descriptor_entry.rb,
lib/fs/ext3/group_descriptor_table.rb

Overview

Ext3 file system interface to MiqFS.

Defined Under Namespace

Classes: AllocBitmap, BlockPointersPath, Directory, DirectoryEntry, ExAttribHeader, ExAttribName, FileData, FileObject, GroupDescriptorEntry, GroupDescriptorTable, HashTreeEntry, HashTreeHeader, Inode, PosixAclEntry, PosixAclHeader, Superblock

Constant Summary collapse

INODE =

//////////////////////////////////////////////////////////////////////////// // Data definitions.

BinaryStruct.new([
  'S',  'file_mode',    # File mode (type and permission), see PF_ DF_ & FM_ below.
  'S',  'uid_lo',       # Lower 16-bits of user id.
  'L',  'size_lo',      # Lower 32-bits of size in bytes.
  'L',  'atime',        # Last access time.
  'L',  'ctime',        # Last change time.
  'L',  'mtime',        # Last modification time.
  'L',  'dtime',        # Time deleted.
  'S',  'gid_lo',       # Lower 16-bits of group id.
  'S',  'link_count',   # Link count.
  'L',  'sector_count', # Sector count.
  'L',  'flags',        # Inode flags, see IF_ below.
  'L',  'unused1',      # Unused.
  'a48',  'blk_ptrs',   # 12 direct block pointers.
  'L',  'ind_ptr',      # 1 single indirect block pointer.
  'L',  'dbl_ind_ptr',  # 1 double indirect block pointer.
  'L',  'tpl_ind_ptr',  # 1 triple indirect block pointer.
  'L',  'gen_num',      # Generation number (NFS).
  'L',  'ext_attrib',   # Extended attribute block (ACL).
  'L',  'size_hi',      # Upper 32-bits of size in bytes or directory ACL.
  'L',  'frag_blk',     # Block address of fragment.
  'C',  'frag_idx',     # Fragment index in block.
  'C',  'frag_siz',     # Fragment size.
  'S',  'unused2',      # Unused.
  'S',  'uid_hi',       # Upper 16-bits of user id.
  'S',  'gid_hi',       # Upper 16-bits of group id.
  'L',  'unused3',      # Unused.
])
SYM_LNK_OFFSET =

Offset of block pointers for those files whose content is a symbolic link of less than 60 chars.

40
SYM_LNK_SIZE =
60
SUPERBLOCK =

//////////////////////////////////////////////////////////////////////////// // Data definitions. Linux 2.6.2 from Fedora Core 6.

BinaryStruct.new([
  'L',  'num_inodes',         # Number of inodes in file system.
  'L',  'num_blocks',         # Number of blocks in file system.
  'L',  'reserved_blocks',    # Number of reserved blocks to prevent file system from filling up.
  'L',  'unallocated_blocks', # Number of unallocated blocks.
  'L',  'unallocated_inodes', # Number of unallocated inodes.
  'L',  'block_group_zero',   # Block where block group 0 starts.
  'L',  'block_size',         # Block size (saved as num bits to shift 1024 left).
  'L',  'fragment_size',      # Fragment size (saved as num bits to shift 1024 left).
  'L',  'blocks_in_group',    # Number of blocks in each block group.
  'L',  'fragments_in_group', # Number of fragments in each block group.
  'L',  'inodes_in_group',    # Number of inodes in each block group.
  'L',  'last_mount_time',    # Time FS was last mounted.
  'L',  'last_write_time',    # Time FS was last written to.
  'S',  'mount_count',        # Current mount count.
  'S',  'max_mount_count',    # Maximum mount count.
  'S',  'signature',          # Always 0xef53
  'S',  'fs_state',           # File System State: see FSS_ below.
  'S',  'err_method',         # Error Handling Method: see EHM_ below.
  'S',  'ver_minor',          # Minor version number.
  'L',  'last_check_time',    # Last consistency check time.
  'L',  'forced_check_int',   # Forced check interval.
  'L',  'creator_os',         # Creator OS: see CO_ below.
  'L',  'ver_major',          # Major version: see MV_ below.
  'S',  'uid_res_blocks',     # UID that can use reserved blocks.
  'S',  'gid_res_blocks',     # GID that can uss reserved blocks.
  # Begin dynamic version fields
  'L',  'first_inode',        # First non-reserved inode in file system.
  'S',  'inode_size',         # Size of each inode.
  'S',  'block_group',        # Block group that this superblock is part of (if backup copy).
  'L',  'compat_flags',       # Compatible feature flags (see CFF_ below).
  'L',  'incompat_flags',     # Incompatible feature flags (see ICF_ below).
  'L',  'ro_flags',           # Read Only feature flags (see ROF_ below).
  'a16',  'fs_id',            # File system ID (UUID or GUID).
  'a16',  'vol_name',         # Volume name.
  'a64',  'last_mnt_path',    # Path where last mounted.
  'L',  'algo_use_bmp',       # Algorithm usage bitmap.
  # Performance hints
  'C',  'file_prealloc_blks', # Blocks to preallocate for files.
  'C',  'dir_prealloc_blks',  # Blocks to preallocate for directories.
  'S',  'unused1',            # Unused.
  # Journal support
  'a16',  'jrnl_id',          # Joural ID (UUID or GUID).
  'L',  'jrnl_inode',         # Journal inode.
  'L',  'jrnl_device',        # Journal device.
  'L',  'orphan_head',        # Head of orphan inode list.
  'a16',  'hash_seed',        # HTREE hash seed. This is actually L4 (__u32 s_hash_seed[4])
  'C',  'hash_ver',           # Default hash version.
  'C',  'unused2',
  'S',  'unused3',
  'L',  'mount_opts',         # Default mount options.
  'L',  'first_meta_blk_grp', # First metablock block group.
  'a360', 'reserved'          # Unused.
])
SUPERBLOCK_SIG =
0xef53
SUPERBLOCK_OFFSET =
1024
SUPERBLOCK_SIZE =
1024
GDE_SIZE =
32
INODE_SIZE =

Default inode size.

128
SUPERBLOCK_VALIDATE =

Simpler structure for just validating the presence of a superblock

BinaryStruct.new([
  'x56', nil,
  'S',  'signature',          # Always 0xef53
  'S',  'fs_state',           # File System State: see FSS_ below.
  'S',  'err_method',         # Error Handling Method: see EHM_ below.
])
SUPERBLOCK_VALIDATE_SIZE =
SUPERBLOCK_VALIDATE.size
DEF_CACHE_SIZE =

Default directory cache size.

50
EX_ATTRIB_NAME =

//////////////////////////////////////////////////////////////////////////// // Data definitions.

[
  'C',  'name_len',       # Length of name
  'C',  'type',           # Type of attribute, see AT_ below.
  'S',  'offset_to_val',  # Offset to value.
  'L',  'blk_loc',        # Block location of value (not used).
  'L',  'size_of_val',    # Size in bytes of value.
  'L',  'hash_of_val',    # Hash of value.
]
DIR_ENTRY_ORIGINAL =

//////////////////////////////////////////////////////////////////////////// // Data definitions.

BinaryStruct.new([
  'L',  'inode_val',  # Inode address of metadata.
  'S',  'entry_len',  # Length of entry.
  'S',  'name_len',   # Length of name.
])
SIZEOF_DIR_ENTRY_ORIGINAL =

Here follows the name in ASCII.

DIR_ENTRY_ORIGINAL.size
DIR_ENTRY_NEW =
BinaryStruct.new([
  'L',  'inode_val',  # Inode address of metadata.
  'S',  'entry_len',  # Length of entry.
  'C',  'name_len',   # Length of name.
  'C',  'file_type',  # Type of file (see FT_ below).
])
SIZEOF_DIR_ENTRY_NEW =

Here follows the name in ASCII.

DIR_ENTRY_NEW.size
HASH_TREE_ENTRY_FIRST =

//////////////////////////////////////////////////////////////////////////// // Data definitions.

[
  'S',  'max_descriptors',  # Maximum number of node descriptors.
  'S',  'cur_descriptors',  # Current number of node descriptors.
  'L',  'first_node',       # Block address of first node.
]
HASH_TREE_ENTRY_NEXT =
[
  'L',  'min_hash',   # Minimum hash value in node.
  'L',  'next_node',  # Block address of next node.
]
POSIX_ACL_ENTRY =

//////////////////////////////////////////////////////////////////////////// // Data definitions.

[
  'S',  'type',         # Entry type, see ET_ below.
  'S',  'permissions',  # Permissions, see EP_ below.
  'L',  'u_g_id',       # User / Group id (not defined for some types).
]
EX_ATTRIB_HEADER =

//////////////////////////////////////////////////////////////////////////// // Data definitions.

[
  'L',  'signature',  # Always 0xea020000
  'L',  'ref_count',
  'L',  'num_blks',
  'L',  'hash',
]
HASH_TREE_HEADER =

//////////////////////////////////////////////////////////////////////////// // Data definitions.

[
  'L',  'unused1',    # Unused.
  'C',  'hash_ver',   # Hash version.
  'C',  'length',     # Length of this structure.
  'C',  'leaf_level', # Levels of leaves.
  'C',  'unused2',    # Unused.
]
POSIX_ACL_HEADER =

//////////////////////////////////////////////////////////////////////////// // Data definitions.

[
  'L',  'version',  # Always 1, or 1 is all that's supported.
]
GDE =

//////////////////////////////////////////////////////////////////////////// // Data definitions.

BinaryStruct.new([
  'L',  'blk_bmp',        # Starting block address of block bitmap.
  'L',  'inode_bmp',      # Starting block address of inode bitmap.
  'L',  'inode_table',    # Starting block address of inode table.
  'S',  'unalloc_blks',   # Number of unallocated blocks in group.
  'S',  'unalloc_inodes', # Number of unallocated inodes in group.
  'S',  'num_dirs',       # Number of directories in group.
  'a14',  'unused1',      # Unused.
])
SIZEOF_GDE =
GDE.size

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#cache_hitsObject

Members (these become members of an MiqFS instance).



15
16
17
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 15

def cache_hits
  @cache_hits
end

#dir_cacheObject

Members (these become members of an MiqFS instance).



15
16
17
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 15

def dir_cache
  @dir_cache
end

#entry_cacheObject

Members (these become members of an MiqFS instance).



15
16
17
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 15

def entry_cache
  @entry_cache
end

#rootDirObject

Members (these become members of an MiqFS instance).



15
16
17
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 15

def rootDir
  @rootDir
end

#superblockObject

Members (these become members of an MiqFS instance).



15
16
17
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 15

def superblock
  @superblock
end

Instance Method Details

#fs_dirEntries(p) ⇒ Object

Returns String array of all names, sans path.



70
71
72
73
74
75
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 70

def fs_dirEntries(p)
  # Get path directory.
  dir = ifs_getDir(p)
  return nil if dir.nil?
  dir.globNames
end

#fs_dirMkdir(_p) ⇒ Object

Make a directory. Parent must exist.



78
79
80
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 78

def fs_dirMkdir(_p)
  raise "Write functionality is not yet supported on Ext3."
end

#fs_dirRmdir(_p) ⇒ Object

Remove a directory.



83
84
85
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 83

def fs_dirRmdir(_p)
  raise "Write functionality is not yet supported on Ext3."
end

#fs_fileAtime(p) ⇒ Object

Returns Ruby Time object.



125
126
127
128
129
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 125

def fs_fileAtime(p)
  de = ifs_getFile(p)
  return nil if de.nil?
  @superblock.getInode(de.inode).aTime
end

#fs_fileAtime_obj(fobj) ⇒ Object

Returns a Ruby Time object.



158
159
160
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 158

def fs_fileAtime_obj(fobj)
  fobj.inode.aTime
end

#fs_fileClose(_fobj) ⇒ Object

Write changes & destroy.



198
199
200
201
202
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 198

def fs_fileClose(_fobj)
  # TODO: unrem when write is supported.
  # fobj.data.close
  fobj = nil
end

#fs_fileCtime(p) ⇒ Object

Returns Ruby Time object.



132
133
134
135
136
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 132

def fs_fileCtime(p)
  de = ifs_getFile(p)
  return nil if de.nil?
  @superblock.getInode(de.inode).cTime
end

#fs_fileCtime_obj(fobj) ⇒ Object

Returns a Ruby Time object.



163
164
165
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 163

def fs_fileCtime_obj(fobj)
  fobj.inode.cTime
end

#fs_fileDelete(_p) ⇒ Object

Delete file.



120
121
122
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 120

def fs_fileDelete(_p)
  raise "Write functionality is not yet supported on Ext3."
end

#fs_fileDirectory?(p) ⇒ Boolean

Returns true if name is a directory.

Returns:

  • (Boolean)


106
107
108
109
110
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 106

def fs_fileDirectory?(p)
  de = ifs_getFile(p)
  return false if de.nil?
  de.fileType == DirectoryEntry::FT_DIRECTORY
end

#fs_fileExists?(p) ⇒ Boolean

Returns true if name exists, false if not.

Returns:

  • (Boolean)


92
93
94
95
96
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 92

def fs_fileExists?(p)
  de = ifs_getFile(p)
  return false if de.nil?
  true
end

#fs_fileFile?(p) ⇒ Boolean

Returns true if name is a regular file.

Returns:

  • (Boolean)


99
100
101
102
103
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 99

def fs_fileFile?(p)
  de = ifs_getFile(p)
  return false if de.nil?
  de.fileType == DirectoryEntry::FT_FILE
end

#fs_fileMtime(p) ⇒ Object

Returns Ruby Time object.



139
140
141
142
143
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 139

def fs_fileMtime(p)
  de = ifs_getFile(p)
  return nil if de.nil?
  @superblock.getInode(de.inode).mTime
end

#fs_fileMtime_obj(fobj) ⇒ Object

Returns a Ruby Time obect.



168
169
170
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 168

def fs_fileMtime_obj(fobj)
  fobj.inode.mTime
end

#fs_fileOpen(p, mode = "r") ⇒ Object

New FileObject instance. NOTE: FileObject must have access to Ext3 members. This is kind of like a ‘skip this’ thing. Ext3 methods use stuff owned by MiqFS, so this is necessary.



176
177
178
179
180
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 176

def fs_fileOpen(p, mode = "r")
  fobj = FileObject.new(p, self)
  fobj.open(mode)
  fobj
end

#fs_fileRead(fobj, len) ⇒ Object

Returns a Ruby String object.



188
189
190
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 188

def fs_fileRead(fobj, len)
  fobj.data.read(len)
end

#fs_fileSeek(fobj, offset, whence) ⇒ Object

Returns current file position.



183
184
185
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 183

def fs_fileSeek(fobj, offset, whence)
  fobj.data.seek(offset, whence)
end

#fs_fileSize(p) ⇒ Object

Returns size in bytes.



113
114
115
116
117
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 113

def fs_fileSize(p)
  de = ifs_getFile(p)
  return nil if de.nil?
  @superblock.getInode(de.inode).length
end

#fs_fileSize_obj(fobj) ⇒ Object

In these, fobj is a FileObject.



153
154
155
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 153

def fs_fileSize_obj(fobj)
  fobj.inode.length
end

#fs_fileWrite(_fobj, _buf, _len) ⇒ Object



192
193
194
195
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 192

def fs_fileWrite(_fobj, _buf, _len)
  raise "Write functionality is not yet supported on Ext3."
  # fobj.data.write(buf, len)
end

#fs_freeBytesObject

Returns free space on file system in bytes.



61
62
63
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 61

def fs_freeBytes
  @superblock.freeBytes
end

#fs_initObject

File system interface.



45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 45

def fs_init
  # puts "Ext3::fs_init(#{@dobj.dInfo.fileName})"
  self.fsType = "Ext3"

  # Initialize bs & read root dir.
  @dobj.seek(0, IO::SEEK_SET)
  self.superblock  = Superblock.new(@dobj)
  @fsId            = superblock.fsId.to_s
  @volName         = superblock.volName
  self.rootDir     = Directory.new(superblock)
  self.entry_cache = LruHash.new(DEF_CACHE_SIZE)
  self.dir_cache   = LruHash.new(DEF_CACHE_SIZE)
  self.cache_hits  = 0
end

#fs_isSymLink?(p) ⇒ Boolean

Return true if p is a path to a symbolic link.

Returns:

  • (Boolean)


146
147
148
149
150
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 146

def fs_isSymLink?(p)
  de = ifs_getFile(p)
  return false if de.nil?
  de.isSymLink?
end

#ifs_getDir(p, miqfs = nil) ⇒ Object

Return a Directory object for a path. Raise error if path doesn’t exist.



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 259

def ifs_getDir(p, miqfs = nil)
  # If this is being called from a FileObject instance, then MiqFS owns contained instance members.
  # If this is being called from an Ext3 module method, then self owns contained instance members.
  miqfs = self if miqfs.nil?

  # Wack leading drive.
  p = unnormalizePath(p)

  # Get an array of directory names, kill off the first (it's always empty).
  names = p.split(/[\\\/]/)
  names.shift

  dir = ifs_getDirR(names, miqfs)
  $log.info("Directory '#{p}' not found") if dir.nil?
  dir
end

#ifs_getDirR(names, miqfs) ⇒ Object

Return Directory recursively for given directory or nil if not exist.



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 277

def ifs_getDirR(names, miqfs)
  return miqfs.rootDir if names.empty?

  # Check for this path in the cache.
  fname = names.join('/')
  if miqfs.dir_cache.key?(fname)
    miqfs.cache_hits += 1
    return miqfs.dir_cache[fname]
  end

  name = names.pop
  pdir = ifs_getDirR(names, miqfs)
  return nil if pdir.nil?

  de = pdir.findEntry(name, DirectoryEntry::FT_DIRECTORY)
  return nil if de.nil?
  miqfs.entry_cache[fname] = de

  dir = Directory.new(miqfs.superblock, de.inode)
  return nil if dir.nil?

  miqfs.dir_cache[fname] = dir
end

#ifs_getFile(p, miqfs = nil) ⇒ Object

Return a DirectoryEntry for a given file or nil if not exist.



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/Ext3.rb', line 207

def ifs_getFile(p, miqfs = nil)
  # If this is being called from a FileObject instance, then MiqFS owns contained instance members.
  # If this is being called from an Ext3 module method, then self owns contained instance members.
  miqfs = self if miqfs.nil?

  # Preprocess path.
  p = unnormalizePath(p)
  dir, fname = File.split(p)
  # Fix for FB#835: if fil == root then fil needs to be "."
  fname = "." if fname == "/" || fname == "\\"

  # Check for this file in the cache.
  cache_name = "#{dir == '/' ? '' : dir}/#{fname}"
  if miqfs.entry_cache.key?(cache_name)
    miqfs.cache_hits += 1
    return miqfs.entry_cache[cache_name]
  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.
  begin
    dirObj = ifs_getDir(dir, miqfs)
    dirEnt = dirObj.nil? ? nil : dirObj.findEntry(fname)
  rescue RuntimeError
    dirEnt = nil
  end

  miqfs.entry_cache[cache_name] = dirEnt
end

#ifs_putFile(p, miqfs = nil) ⇒ Object

Create a directory entry.



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 238

def ifs_putFile(p, miqfs = nil)
  raise "Write functionality is not yet supported on Ext3."

  # Commented out for now to avoid unreachable code
  #
  # # If this is being called from a FileObject instance, then MiqFS owns contained instance members.
  # # If this is being called from an Ext3 module method, then self owns contained instance members.
  # miqfs = self if miqfs.nil?

  # # Preprocess path.
  # p = unnormalizePath(p)
  # dir, fil = File.split(p)

  # # Parent directory must exist.
  # dirObj = ifs_getDir(dir, miqfs)
  # return nil if dir.nil?
  # dirObj.createFile(fil)
end

#unnormalizePath(p) ⇒ Object

Wack leading drive leter & colon.



302
303
304
# File 'lib/fs/MiqFS/modules/Ext3.rb', line 302

def unnormalizePath(p)
  p[1] == 58 ? p[2, p.size] : p
end