Module: Fat32
- Included in:
- FileObject
- Defined in:
- lib/fs/fat32/boot_sect.rb,
lib/fs/fat32/directory.rb,
lib/fs/fat32/file_data.rb,
lib/fs/MiqFS/modules/Fat32.rb,
lib/fs/fat32/directory_entry.rb
Overview
TODO: reserved1 is the infamous magic number. Somehow it works to preserve case on Windows XP. Nobody seems to know how. Here it is always set to 0 (which yields uppercase names on XP).
Defined Under Namespace
Classes: BootSect, Directory, DirectoryEntry, FileData, FileObject
Constant Summary collapse
- BOOT_SECT =
BinaryStruct.new([ 'a3', 'jmp_boot', # Jump to boot loader. 'a8', 'oem_name', # OEM Name in ASCII. 'S', 'bytes_per_sec', # Bytes per sector: 512, 1024, 2048 or 4096. 'C', 'sec_per_clus', # Sectors per cluster, size must be < 32K. 'S', 'res_sec', # Reserved sectors. 'C', 'num_fats', # Typically 2, but can be 1. 'S', 'max_root', # Max files in root dir - 0 FOR FAT32. 'S', 'num_sec16', # 16-bit number of sectors in file system (0 if 32-bits needed). 'C', 'media_type', # Ususally F8, but can be F0 for removeable. 'S', 'fat_size16', # 16-bit number of sectors in FAT, 0 FOR FAT32. 'S', 'sec_per_track', # Sectors per track. 'S', 'num_heads', # Number of heads. 'L', 'pre_sec', # Sectors before the start of the partition. 'L', 'num_sec32', # 32-bit number of sectors in the file system (0 if 16-bit num used). 'L', 'fat_size32', # 32-bit number of sectors in FAT. 'S', 'fat_usage', # Describes how FATs are used: See FU_ below. 'S', 'version', # Major & minor version numbers. 'L', 'root_clus', # Cluster location of root directory. 'S', 'fsinfo_sec', # Sector location of FSINFO structure . 'S', 'boot_bkup', # Sector location of boot sector backup. 'a12', 'reserved1', # Reserved. 'C', 'drive_num', # INT13 drive number. 'C', 'unused1', # Unused. 'C', 'ex_sig', # If 0x29, then the next three values are valid. 'L', 'serial_num', # Volume serial number. 'a11', 'label', # Volume label. 'a8', 'fs_label', # File system type label, not required. # NOTE: MS always uses "FAT32 ". For probe, seek to 66 & verify 0x29, # then seek to 82 & read 8, compare with "FAT32 ". 'a420', nil, # Unused. 'S', 'signature', # 0xaa55 ])
- SIZEOF_BOOT_SECT =
BOOT_SECT.size
- DOS_SIGNATURE =
0xaa55
- FSINFO =
BinaryStruct.new([ 'a4', 'sig1', # Signature - 0x41615252 (RRaA). 'a480', nil, # Unused. 'a4', 'sig2', # Signature - 0x61417272 (rrAa). 'L', 'free_clus', # Number of free clusters. 'L', 'next_free', # Next free cluster. 'a12', nil, # Unused. 'L', 'sig3', # Signature - 0xaa550000. ])
- SIZEOF_FSINFO =
FSINFO.size
- FSINFO_SIG1 =
"RRaA"
- FSINFO_SIG2 =
"rrAa"
- FSINFO_SIG3 =
0xaa550000
- DEF_CACHE_SIZE =
Default directory cache size.
50
- DIR_ENT_SFN =
BinaryStruct.new([ 'a11', 'name', # If name[0] = 0, unallocated; if name[0] = 0xe5, deleted. DOES NOT INCLUDE DOT. 'C', 'attributes', # See FA_ below. If 0x0f then LFN entry. 'C', 'reserved1', # Reserved. 'C', 'ctime_tos', # Created time, tenths of second. 'S', 'ctime_hms', # Created time, hours, minutes & seconds. 'S', 'ctime_day', # Created day. 'S', 'atime_day', # Accessed day. 'S', 'first_clus_hi', # Hi 16-bits of first cluster address. 'S', 'mtime_hms', # Modified time, hours, minutes & seconds. 'S', 'mtime_day', # Modified day. 'S', 'first_clus_lo', # Lo 16-bits of first cluster address. 'L', 'file_size', # Size of file (0 for directories). ])
- DIR_ENT_LFN =
BinaryStruct.new([ 'C', 'seq_num', # Sequence number, bit 6 marks end, 0xe5 if deleted. 'a10', 'name', # UNICODE chars 1-5 of name. 'C', 'attributes', # Always 0x0f. 'C', 'reserved1', # Reserved. 'C', 'checksum', # Checksum of SFN entry, all LFN entries must match. 'a12', 'name2', # UNICODE chars 6-11 of name. 'S', 'reserved2', # Reserved. 'a4', 'name3' # UNICODE chars 12-13 of name. ])
- CHARS_PER_LFN =
13
- LFN_NAME_MAXLEN =
260
- DIR_ENT_SIZE =
32
- ATTRIB_OFFSET =
11
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).
-
#dir_cache ⇒ Object
Members (these become members of an MiqFS instance).
-
#drive_root ⇒ Object
Members (these become members of an MiqFS instance).
-
#rootDirEnt ⇒ Object
Returns the value of attribute rootDirEnt.
Instance Method Summary collapse
-
#fs_dirEntries(p) ⇒ Object
Returns String array of all names, sans path.
-
#fs_dirMkdir(p) ⇒ Object
Make a directory.
-
#fs_dirRmdir(p) ⇒ Object
Remove a directory.
-
#fs_fileAtime(p) ⇒ Object
Returns Ruby Time object.
-
#fs_fileAtime_obj(fobj) ⇒ Object
Returns a Ruby Time object.
-
#fs_fileClose(fobj) ⇒ Object
Write changes & destroy.
-
#fs_fileCtime(p) ⇒ Object
Returns Ruby Time object.
-
#fs_fileCtime_obj(fobj) ⇒ Object
Returns a Ruby Time object.
-
#fs_fileDelete(p) ⇒ Object
Delete file.
-
#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
Returns current file position.
-
#fs_fileSize(p) ⇒ Object
Returns size in bytes.
-
#fs_fileSize_obj(fobj) ⇒ Object
In these, fobj is a FileObject.
- #fs_fileWrite(fobj, buf, len) ⇒ Object
-
#fs_freeBytes ⇒ Object
Returns free space on file system in bytes.
-
#fs_init ⇒ Object
File system interface.
-
#fs_isSymLink?(_p) ⇒ Boolean
FAT file systems don’t do symbolic links.
-
#ifs_getDir(p, miqfs = nil) ⇒ Object
Return a Directory object for a path.
-
#ifs_getFile(p, miqfs = nil) ⇒ Object
Return a DirectoryEntry for a given file or nil if not exist.
-
#ifs_putFile(p, miqfs = nil) ⇒ Object
Create a directory entry.
-
#unnormalizePath(p) ⇒ Object
Wack leading drive leter & colon.
Instance Attribute Details
#boot_sector ⇒ Object
Members (these become members of an MiqFS instance).
15 16 17 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 15 def boot_sector @boot_sector end |
#cache_hits ⇒ Object
Members (these become members of an MiqFS instance).
15 16 17 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 15 def cache_hits @cache_hits end |
#dir_cache ⇒ Object
Members (these become members of an MiqFS instance).
15 16 17 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 15 def dir_cache @dir_cache end |
#drive_root ⇒ Object
Members (these become members of an MiqFS instance).
15 16 17 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 15 def drive_root @drive_root end |
#rootDirEnt ⇒ Object
Returns the value of attribute rootDirEnt.
16 17 18 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 16 def rootDirEnt @rootDirEnt end |
Instance Method Details
#fs_dirEntries(p) ⇒ Object
Returns String array of all names, sans path.
89 90 91 92 93 94 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 89 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.
97 98 99 100 101 102 103 104 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 97 def fs_dirMkdir(p) de = ifs_getFile(p) raise "Name already exists: #{p}" unless de.nil? parent, name = File.split(p) parent = ifs_getDir(parent) raise "Parent directory must exist: #{p}" if parent.nil? parent.mkdir(name) end |
#fs_dirRmdir(p) ⇒ Object
Remove a directory.
107 108 109 110 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 107 def fs_dirRmdir(p) raise "Directory [#{p}] is not empty" if ifs_getDir(p).globNames.size > 2 fs_fileDelete(p) end |
#fs_fileAtime(p) ⇒ Object
Returns Ruby Time object.
161 162 163 164 165 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 161 def fs_fileAtime(p) de = ifs_getFile(p) return nil if de.nil? de.aTime end |
#fs_fileAtime_obj(fobj) ⇒ Object
Returns a Ruby Time object.
188 189 190 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 188 def fs_fileAtime_obj(fobj) fobj.de.aTime end |
#fs_fileClose(fobj) ⇒ Object
Write changes & destroy.
227 228 229 230 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 227 def fs_fileClose(fobj) fobj.data.close fobj = nil end |
#fs_fileCtime(p) ⇒ Object
Returns Ruby Time object.
168 169 170 171 172 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 168 def fs_fileCtime(p) de = ifs_getFile(p) return nil if de.nil? de.cTime end |
#fs_fileCtime_obj(fobj) ⇒ Object
Returns a Ruby Time object.
193 194 195 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 193 def fs_fileCtime_obj(fobj) fobj.de.cTime end |
#fs_fileDelete(p) ⇒ Object
Delete file.
154 155 156 157 158 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 154 def fs_fileDelete(p) de = ifs_getFile(p) return if de.nil? de.delete(@boot_sector) end |
#fs_fileDirectory?(p) ⇒ Boolean
Returns true if name is a directory.
135 136 137 138 139 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 135 def fs_fileDirectory?(p) de = ifs_getFile(p) return false if de.nil? de.isDir? end |
#fs_fileExists?(p) ⇒ Boolean
Returns true if name exists, false if not.
117 118 119 120 121 122 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 117 def fs_fileExists?(p) return true if p == "/" || p == "\\" de = ifs_getFile(p) return false if de.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… (FAT32 supports .lnk files at this level, so this should also).
127 128 129 130 131 132 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 127 def fs_fileFile?(p) de = ifs_getFile(p) return false if de.nil? return false if de.isDir? true end |
#fs_fileMtime(p) ⇒ Object
Returns Ruby Time object.
175 176 177 178 179 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 175 def fs_fileMtime(p) de = ifs_getFile(p) return nil if de.nil? de.mTime end |
#fs_fileMtime_obj(fobj) ⇒ Object
Returns a Ruby Time obect.
198 199 200 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 198 def fs_fileMtime_obj(fobj) fobj.de.mTime end |
#fs_fileOpen(p, mode = "r") ⇒ Object
New FileObject instance. NOTE: FileObject must have access to Fat32 members. This is kind of like a ‘skip this’ thing. Fat32 methods use stuff owned by MiqFS, so this is necessary.
206 207 208 209 210 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 206 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.
218 219 220 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 218 def fs_fileRead(fobj, len) fobj.data.read(len) end |
#fs_fileSeek(fobj, offset, whence) ⇒ Object
Returns current file position.
213 214 215 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 213 def fs_fileSeek(fobj, offset, whence) fobj.data.seek(offset, whence) end |
#fs_fileSize(p) ⇒ Object
Returns size in bytes.
147 148 149 150 151 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 147 def fs_fileSize(p) de = ifs_getFile(p) return nil if de.nil? de.length end |
#fs_fileSize_obj(fobj) ⇒ Object
In these, fobj is a FileObject.
183 184 185 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 183 def fs_fileSize_obj(fobj) fobj.de.length end |
#fs_fileWrite(fobj, buf, len) ⇒ Object
222 223 224 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 222 def fs_fileWrite(fobj, buf, len) fobj.data.write(buf, len) end |
#fs_freeBytes ⇒ Object
Returns free space on file system in bytes.
80 81 82 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 80 def fs_freeBytes @boot_sector.freeClusters * @boot_sector.bytesPerCluster end |
#fs_init ⇒ Object
File system interface.
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 56 def fs_init # puts "Fat32::fs_init(#{@dobj.dInfo.fileName})" self.fsType = "FAT32" # Initialize bs & read root dir. @dobj.seek(0, IO::SEEK_SET) self.boot_sector = BootSect.new(@dobj) self.drive_root = Directory.new(@boot_sector) self.dir_cache = LruHash.new(DEF_CACHE_SIZE) self.cache_hits = 0 # Volume info: Note that fsId is a long in this case (not a UUID) # and volName could be modified by a file in the root with the label attrib set. self.fsId = boot_sector.fsId self.volName = boot_sector.volName # Spoof root dir ent - Fat32 has no "." in root. self.rootDirEnt = DirectoryEntry.new rootDirEnt.setAttribute(DirectoryEntry::FA_DIRECTORY) rootDirEnt.firstCluster = 0 rootDirEnt.zeroTime end |
#fs_isSymLink?(_p) ⇒ Boolean
FAT file systems don’t do symbolic links.
142 143 144 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 142 def fs_isSymLink?(_p) false end |
#ifs_getDir(p, miqfs = nil) ⇒ Object
Return a Directory object for a path. Raise error if path doesn’t exist.
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 279 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 NTFS module method, then self owns contained instance members. miqfs = self if miqfs.nil? # Wack leading drive. p = unnormalizePath(p) # Check for this path in the cache. if miqfs.dir_cache.key?(p) miqfs.cache_hits += 1 return Directory.new(miqfs.boot_sector, miqfs.dir_cache[p]) end # Return root if lone separator. return Directory.new(miqfs.boot_sector) if p == "/" || p == "\\" # Get an array of directory names, kill off the first (it's always empty). names = p.split(/[\\\/]/); names.shift # Find first cluster of target dir. cluster = miqfs.boot_sector.rootCluster loop do break if names.empty? dir = Directory.new(miqfs.boot_sector, cluster) de = dir.findEntry(names.shift, Directory::FE_DIR) raise "Can't find directory: \'#{p}\'" if de.nil? cluster = de.firstCluster end # Save cluster in the cache & return a Directory. miqfs.dir_cache[p] = cluster Directory.new(miqfs.boot_sector, cluster) end |
#ifs_getFile(p, miqfs = nil) ⇒ Object
Return a DirectoryEntry for a given file or nil if not exist.
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 235 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 Fat32 module method, then self owns contained instance members. miqfs = self if miqfs.nil? # If root dir return spoof dir ent. return rootDirEnt if p == "/" || p == "\\" # Preprocess path. p = unnormalizePath(p) dir, fil = File.split(p) # Look for file in dir, but don't barf if it doesn't exist. # NOTE: if p is a directory that's ok, find it. begin dirObj = ifs_getDir(dir, miqfs) return nil if dirObj.nil? # Do NOT explicitly pass FE_FILE. dirEnt = dirObj.findEntry(fil) return nil if dirEnt.nil? rescue RuntimeError return nil end dirEnt end |
#ifs_putFile(p, miqfs = nil) ⇒ Object
Create a directory entry.
262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 262 def ifs_putFile(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 Fat32 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. dir = ifs_getDir(dir, miqfs) return nil if dir.nil? dir.createFile(fil) end |
#unnormalizePath(p) ⇒ Object
Wack leading drive leter & colon.
315 316 317 |
# File 'lib/fs/MiqFS/modules/Fat32.rb', line 315 def unnormalizePath(p) p[1] == 58 ? p[2, p.size] : p end |