Class: Fat32::Directory
- Inherits:
-
Object
- Object
- Fat32::Directory
- Defined in:
- lib/fs/fat32/directory.rb
Constant Summary collapse
- MAX_ENT_SIZE =
Maximum LFN entry span in bytes (LFN entries can span clusters).
640
- FE_DIR =
Find entry flags.
0
- FE_FILE =
1
- FE_EITHER =
2
- GF_W98 =
Get free entry behaviors. Windows 98 returns the first deleted or unallocated entry. Windows XP returns the first unallocated entry. Advantage W98: less allocation, advantage WXP: deleted entries are not overwritten.
0
- GF_WXP =
1
Instance Method Summary collapse
-
#countFreeEntries(behavior, buf) ⇒ Object
Return the number of contiguous free entries starting at buf according to behavior.
- #createFile(name) ⇒ Object
-
#findEntry(name, flags = FE_EITHER) ⇒ Object
Return a DirectoryEntry for a specific file (or subdirectory).
- #getClusterStatus(offset) ⇒ Object
- #getDirData ⇒ Object
-
#getFirstFreeEntry(num_entries = 1, behavior = GF_W98) ⇒ Object
Get free entry or entries in directory data.
-
#globNames ⇒ Object
Return all names in directory as a sorted string array.
-
#initialize(bs, cluster = nil) ⇒ Directory
constructor
Initialization.
- #isFree(allocStatus, behavior) ⇒ Object
-
#mkClusterStatus(num, dirty) ⇒ Object
TODO: - Loose this idea.
- #mkdir(name) ⇒ Object
Constructor Details
#initialize(bs, cluster = nil) ⇒ Directory
Initialization
35 36 37 38 39 40 41 42 43 44 |
# File 'lib/fs/fat32/directory.rb', line 35 def initialize(bs, cluster = nil) raise "Nil boot sector" if bs.nil? cluster = bs.rootCluster if cluster.nil? @bs = bs # Allocate one cluster if cluster is zero. cluster = @bs.allocClusters(0) if cluster == 0 @cluster = cluster @data, @all_clusters = getDirData end |
Instance Method Details
#countFreeEntries(behavior, buf) ⇒ Object
Return the number of contiguous free entries starting at buf according to behavior.
174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/fs/fat32/directory.rb', line 174 def countFreeEntries(behavior, buf) num_free = 0 0.step(buf.size - 1, DIR_ENT_SIZE) do |offset| if isFree(buf[offset], behavior) num_free += 1 else return num_free end end num_free end |
#createFile(name) ⇒ Object
143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/fs/fat32/directory.rb', line 143 def createFile(name) de = DirectoryEntry.new; de.name = name until findEntry(de.shortName).nil? raise "Duplicate file name: #{de.shortName}" unless de.shortName.include?("~") de.incShortName end de.parentOffset, de.parentCluster = getFirstFreeEntry(de.numEnts) de.writeEntry(@bs) @data, @all_clusters = getDirData de end |
#findEntry(name, flags = FE_EITHER) ⇒ Object
Return a DirectoryEntry for a specific file (or subdirectory).
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/fs/fat32/directory.rb', line 72 def findEntry(name, flags = FE_EITHER) de = nil # found directory entry. skip_next = found = false offset = 0 # Look for appropriate records. 0.step(@data.length - 1, DIR_ENT_SIZE) do|offset| # Check allocation status (ignore if deleted, done if not allocated). alloc_flags = @data[offset] next if alloc_flags == DirectoryEntry::AF_DELETED break if alloc_flags == DirectoryEntry::AF_NOT_ALLOCATED # Skip LFN entries unless it's the first (last iteration already chewed them all up). attrib = @data[offset + ATTRIB_OFFSET] if attrib == DirectoryEntry::FA_LFN && (alloc_flags & DirectoryEntry::AF_LFN_LAST != DirectoryEntry::AF_LFN_LAST) # Also skip the next entry (it's the base entry for the last dir ent). skip_next = true; next end if skip_next skip_next = false; next end # If a specific type of record was requested, look for only that type. # NOTE: You know, it's possible to look ahead and see what the base entry is. if flags != FE_EITHER && attrib != DirectoryEntry::FA_LFN next if flags == FE_DIR && (attrib & DirectoryEntry::FA_DIRECTORY == 0) next if flags == FE_FILE && (attrib & DirectoryEntry::FA_DIRECTORY != 0) end # Potential match... get a DirectoryEntry & stop if found. de = DirectoryEntry.new(@data[offset, MAX_ENT_SIZE]) # TODO: - what if the name ends with a dot & there's another dot in the name? if de.name.downcase == name.downcase || de.shortName.downcase == name.downcase found = true break end end return nil unless found parentLoc = offset.divmod(@bs.bytesPerCluster) de.parentCluster = @all_clusters[parentLoc[0]].number de.parentOffset = parentLoc[1] de end |
#getClusterStatus(offset) ⇒ Object
217 218 219 220 |
# File 'lib/fs/fat32/directory.rb', line 217 def getClusterStatus(offset) idx = offset.divmod(@bs.bytesPerCluster)[0] @all_clusters[idx] end |
#getDirData ⇒ Object
197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/fs/fat32/directory.rb', line 197 def getDirData allClusters = [] clus = @cluster allClusters << mkClusterStatus(clus, 0) buf = @bs.getCluster(clus); data = nil while (data = @bs.getNextCluster(clus)) != nil clus = data[0]; buf += data[1] allClusters << mkClusterStatus(clus, 0) end return buf, allClusters end |
#getFirstFreeEntry(num_entries = 1, behavior = GF_W98) ⇒ Object
Get free entry or entries in directory data. If not exist, allocate cluster.
159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/fs/fat32/directory.rb', line 159 def getFirstFreeEntry(num_entries = 1, behavior = GF_W98) 0.step(@data.size - 1, DIR_ENT_SIZE) do |offset| next if @data[offset] != DirectoryEntry::AF_NOT_ALLOCATED && @data[offset] != DirectoryEntry::AF_DELETED num = countFreeEntries(behavior, @data[offset..-1]) return offset.divmod(@bs.bytesPerCluster)[1], getClusterStatus(offset).number if num >= num_entries end # Must allocate another cluster. cluster = @bs.allocClusters(@cluster) @data += MemoryBuffer.create(@bs.bytesPerCluster) @all_clusters << mkClusterStatus(cluster, 0) return 0, cluster end |
#globNames ⇒ Object
Return all names in directory as a sorted string array.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/fs/fat32/directory.rb', line 50 def globNames names = [] cluster = @cluster mf = StringIO.new(@bs.getCluster(cluster)) loop do (@bs.bytesPerCluster / DIR_ENT_SIZE - 1).times do de = DirectoryEntry.new(mf.read) break if de.name == '' names << de.name.downcase if de.name != DirectoryEntry::AF_DELETED && de.name[0] != DirectoryEntry::AF_DELETED mf = StringIO.new(de.unused) break if mf.size == 0 end data = @bs.getNextCluster(cluster) break if data.nil? cluster = data[0] mf = StringIO.new(data[1]) end names.sort! end |
#isFree(allocStatus, behavior) ⇒ Object
186 187 188 189 190 191 192 193 194 195 |
# File 'lib/fs/fat32/directory.rb', line 186 def isFree(allocStatus, behavior) if behavior == GF_W98 return true if allocStatus == DirectoryEntry::AF_NOT_ALLOCATED || allocStatus == DirectoryEntry::AF_DELETED elsif behavior == GF_WXP return true if allocStatus == DirectoryEntry::AF_NOT_ALLOCATED else raise "Fat32Directory#isFree: Unknown behavior: #{behavior}" end false end |
#mkClusterStatus(num, dirty) ⇒ Object
TODO: - Loose this idea.
210 211 212 213 214 215 |
# File 'lib/fs/fat32/directory.rb', line 210 def mkClusterStatus(num, dirty) status = OpenStruct.new status.number = num status.dirty = dirty status end |
#mkdir(name) ⇒ Object
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/fs/fat32/directory.rb', line 116 def mkdir(name) dir = createFile(name) data = FileData.new(dir, @bs) dir.setAttribute(DirectoryEntry::FA_ARCHIVE, false) dir.setAttribute(DirectoryEntry::FA_DIRECTORY) dir.writeEntry(@bs) # Write dot and double dot directories. dot = DirectoryEntry.new; dotdot = DirectoryEntry.new dot.name = "."; dotdot.name = ".." dot.setAttribute(DirectoryEntry::FA_ARCHIVE, false) dot.setAttribute(DirectoryEntry::FA_DIRECTORY) dotdot.setAttribute(DirectoryEntry::FA_ARCHIVE, false) dotdot.setAttribute(DirectoryEntry::FA_DIRECTORY) buf = dot.raw + dotdot.raw data.write(buf) dir.firstCluster = data.firstCluster dir.writeEntry(@bs) # Update firsCluster in . and .. (if .. is root then it's 0, not 2). dot.firstCluster = dir.firstCluster dotdot.firstCluster = dir.parentCluster == 2 ? 0 : dir.parentCluster buf = dot.raw + dotdot.raw data.rewind data.write(buf) end |