/usr/share/gocode/src/github.com/mitchellh/go-fs/fat/directory_cluster.go is in golang-github-mitchellh-go-fs-dev 0.0~git20150611.0.a34c1b9-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 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 115 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 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 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 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 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 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 | package fat
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/mitchellh/go-fs"
"math"
"time"
"unicode/utf16"
)
type DirectoryAttr uint8
const (
AttrReadOnly DirectoryAttr = 0x01
AttrHidden = 0x02
AttrSystem = 0x04
AttrVolumeId = 0x08
AttrDirectory = 0x10
AttrArchive = 0x20
AttrLongName = AttrReadOnly | AttrHidden | AttrSystem | AttrVolumeId
)
// The size in bytes of a single directory entry.
const DirectoryEntrySize = 32
// Mask applied to the ord of the last long entry.
const LastLongEntryMask = 0x40
// DirectoryCluster represents a cluster on the disk that contains
// entries/contents.
type DirectoryCluster struct {
entries []*DirectoryClusterEntry
fat16Root bool
startCluster uint32
}
// DirectoryClusterEntry is a single 32-byte entry that is part of the
// chain of entries in a directory cluster.
type DirectoryClusterEntry struct {
name string
ext string
attr DirectoryAttr
createTime time.Time
accessTime time.Time
writeTime time.Time
cluster uint32
fileSize uint32
deleted bool
longOrd uint8
longName string
longChecksum uint8
}
func DecodeDirectoryCluster(startCluster uint32, device fs.BlockDevice, fat *FAT) (*DirectoryCluster, error) {
bs := fat.bs
chain := fat.Chain(startCluster)
data := make([]byte, uint32(len(chain))*bs.BytesPerCluster())
for i, clusterNumber := range chain {
dataOffset := uint32(i) * bs.BytesPerCluster()
devOffset := int64(bs.ClusterOffset(int(clusterNumber)))
chainData := data[dataOffset : dataOffset+bs.BytesPerCluster()]
if _, err := device.ReadAt(chainData, devOffset); err != nil {
return nil, err
}
}
result, err := decodeDirectoryCluster(data, bs)
if err != nil {
return nil, err
}
result.startCluster = startCluster
return result, nil
}
// DecodeFAT16RootDirectory decodes the FAT16 root directory structure
// from the device.
func DecodeFAT16RootDirectoryCluster(device fs.BlockDevice, bs *BootSectorCommon) (*DirectoryCluster, error) {
data := make([]byte, DirectoryEntrySize*bs.RootEntryCount)
if _, err := device.ReadAt(data, int64(bs.RootDirOffset())); err != nil {
return nil, err
}
result, err := decodeDirectoryCluster(data, bs)
if err != nil {
return nil, err
}
result.fat16Root = true
return result, nil
}
func decodeDirectoryCluster(data []byte, bs *BootSectorCommon) (*DirectoryCluster, error) {
entries := make([]*DirectoryClusterEntry, 0, bs.RootEntryCount)
for i := uint16(0); i < uint16(len(data)/DirectoryEntrySize); i++ {
offset := i * DirectoryEntrySize
entryData := data[offset : offset+DirectoryEntrySize]
if entryData[0] == 0 {
break
}
entry, err := DecodeDirectoryClusterEntry(entryData)
if err != nil {
return nil, err
}
entries = append(entries, entry)
}
result := &DirectoryCluster{
entries: entries,
}
return result, nil
}
func NewDirectoryCluster(start uint32, parent uint32, t time.Time) *DirectoryCluster {
cluster := new(DirectoryCluster)
cluster.startCluster = start
// Create the "." and ".." entries
cluster.entries = []*DirectoryClusterEntry{
&DirectoryClusterEntry{
accessTime: t,
attr: AttrDirectory,
cluster: start,
createTime: t,
name: ".",
writeTime: t,
},
&DirectoryClusterEntry{
accessTime: t,
attr: AttrDirectory,
cluster: parent,
createTime: t,
name: "..",
writeTime: t,
},
}
return cluster
}
// NewFat16RootDirectory creates a new DirectoryCluster that is meant only
// to be the root directory of a FAT12/FAT16 filesystem.
func NewFat16RootDirectoryCluster(bs *BootSectorCommon, label string) (*DirectoryCluster, error) {
if bs.RootEntryCount == 0 {
return nil, errors.New("root entry count is 0 in boot sector")
}
result := &DirectoryCluster{
entries: make([]*DirectoryClusterEntry, 1, bs.RootEntryCount),
}
// Create the volume ID entry
result.entries[0] = &DirectoryClusterEntry{
attr: AttrVolumeId,
name: label,
cluster: 0,
}
return result, nil
}
// Bytes returns the on-disk byte data for this directory structure.
func (d *DirectoryCluster) Bytes() []byte {
result := make([]byte, cap(d.entries)*DirectoryEntrySize)
for i, entry := range d.entries {
offset := i * DirectoryEntrySize
entryBytes := entry.Bytes()
copy(result[offset:offset+DirectoryEntrySize], entryBytes)
}
return result
}
// WriteToDevice writes the cluster to the device.
func (d *DirectoryCluster) WriteToDevice(device fs.BlockDevice, fat *FAT) error {
if d.fat16Root {
// Write the cluster to the FAT16 root directory location
offset := int64(fat.bs.RootDirOffset())
if _, err := device.WriteAt(d.Bytes(), offset); err != nil {
return err
}
} else {
chain := &ClusterChain{
device: device,
fat: fat,
startCluster: d.startCluster,
}
if _, err := chain.Write(d.Bytes()); err != nil {
return err
}
}
return nil
}
// Bytes returns the on-disk byte data for this directory entry.
func (d *DirectoryClusterEntry) Bytes() []byte {
var result [DirectoryEntrySize]byte
if d.longName != "" {
runes := bytes.Runes([]byte(d.longName))
// The name must be zero-terminated then padded with 0xFF
// up to 13 characters
if len(runes) < 13 {
runes = append(runes, 0)
for len(runes) < 13 {
runes = append(runes, 0xFFFF)
}
}
// LDIR_Ord
result[0] = d.longOrd
// LDIR_Name1
for i := 0; i < int(math.Min(float64(len(runes)), 5)); i++ {
offset := 1 + (i * 2)
data := result[offset : offset+2]
binary.LittleEndian.PutUint16(data, uint16(runes[i]))
}
// LDIR_Attr
result[11] = byte(AttrLongName)
// LDIR_Type
result[12] = 0
// LDIR_Chksum
result[13] = d.longChecksum
// LDIR_Name2
for i := 0; i < 6; i++ {
offset := 14 + (i * 2)
data := result[offset : offset+2]
binary.LittleEndian.PutUint16(data, uint16(runes[i+5]))
}
// LDIR_FstClusLO
result[26] = 0
result[27] = 0
// LDIR_Name3
for i := 0; i < 2; i++ {
offset := 28 + (i * 2)
data := result[offset : offset+2]
binary.LittleEndian.PutUint16(data, uint16(runes[i+11]))
}
} else {
// DIR_Name
var simpleName string
if d.name == "." || d.name == ".." {
simpleName = d.name
} else {
simpleName = fmt.Sprintf("%s.%s", d.name, d.ext)
}
copy(result[0:11], shortNameEntryValue(simpleName))
// DIR_Attr
result[11] = byte(d.attr)
// DIR_CrtTime
crtDate, crtTime, crtTenths := encodeDOSTime(d.createTime)
result[13] = crtTenths
binary.LittleEndian.PutUint16(result[14:16], crtTime)
binary.LittleEndian.PutUint16(result[16:18], crtDate)
// DIR_LstAccDate
accDate, _, _ := encodeDOSTime(d.accessTime)
binary.LittleEndian.PutUint16(result[18:20], accDate)
// DIR_FstClusHI
binary.LittleEndian.PutUint16(result[20:22], uint16(d.cluster>>16))
// DIR_WrtTime and DIR_WrtDate
wrtDate, wrtTime, _ := encodeDOSTime(d.writeTime)
binary.LittleEndian.PutUint16(result[22:24], wrtTime)
binary.LittleEndian.PutUint16(result[24:26], wrtDate)
// DIR_FstClusLO
binary.LittleEndian.PutUint16(result[26:28], uint16(d.cluster&0xFFFF))
// DIR_FileSize
binary.LittleEndian.PutUint32(result[28:32], d.fileSize)
}
return result[:]
}
// IsLong returns true if this is a long entry.
func (d *DirectoryClusterEntry) IsLong() bool {
return (d.attr & AttrLongName) == AttrLongName
}
func (d *DirectoryClusterEntry) IsVolumeId() bool {
return (d.attr & AttrVolumeId) == AttrVolumeId
}
// DecodeDirectoryClusterEntry decodes a single directory entry in the
// Directory structure.
func DecodeDirectoryClusterEntry(data []byte) (*DirectoryClusterEntry, error) {
var result DirectoryClusterEntry
// Do the attributes so we can determine if we're dealing with long names
result.attr = DirectoryAttr(data[11])
if (result.attr & AttrLongName) == AttrLongName {
result.longOrd = data[0]
chars := make([]uint16, 13)
for i := 0; i < 5; i++ {
offset := 1 + (i * 2)
chars[i] = binary.LittleEndian.Uint16(data[offset : offset+2])
}
for i := 0; i < 6; i++ {
offset := 14 + (i * 2)
chars[i+5] = binary.LittleEndian.Uint16(data[offset : offset+2])
}
for i := 0; i < 2; i++ {
offset := 28 + (i * 2)
chars[i+11] = binary.LittleEndian.Uint16(data[offset : offset+2])
}
result.longName = string(utf16.Decode(chars))
result.longChecksum = data[13]
} else {
result.deleted = data[0] == 0xE5
// Basic attributes
if data[0] == 0x05 {
data[0] = 0xE5
}
result.name = string(data[0:8])
result.ext = string(data[8:11])
// Creation time
createTimeTenths := data[13]
createTimeWord := binary.LittleEndian.Uint16(data[14:16])
createDateWord := binary.LittleEndian.Uint16(data[16:18])
result.createTime = decodeDOSTime(createDateWord, createTimeWord, createTimeTenths)
// Access time
accessDateWord := binary.LittleEndian.Uint16(data[18:20])
result.accessTime = decodeDOSTime(accessDateWord, 0, 0)
// Write time
writeTimeWord := binary.LittleEndian.Uint16(data[22:24])
writeDateWord := binary.LittleEndian.Uint16(data[24:26])
result.writeTime = decodeDOSTime(writeDateWord, writeTimeWord, 0)
// Cluster
result.cluster = uint32(binary.LittleEndian.Uint16(data[20:22]))
result.cluster <<= 4
result.cluster |= uint32(binary.LittleEndian.Uint16(data[26:28]))
// File size
result.fileSize = binary.LittleEndian.Uint32(data[28:32])
}
return &result, nil
}
// NewLongDirectoryClusterEntry returns the series of directory cluster
// entries that need to be written for a long directory entry. This list
// of entries does NOT contain the short name entry.
func NewLongDirectoryClusterEntry(name string, shortName string) ([]*DirectoryClusterEntry, error) {
// Split up the shortName properly
checksum := checksumShortName(shortNameEntryValue(shortName))
// Calcualte the number of entries we'll actually need to store
// the long name.
numLongEntries := len(name) / 13
if len(name)%13 != 0 {
numLongEntries++
}
entries := make([]*DirectoryClusterEntry, numLongEntries)
for i := 0; i < numLongEntries; i++ {
entries[i] = new(DirectoryClusterEntry)
entry := entries[i]
entry.attr = AttrLongName
entry.longOrd = uint8(numLongEntries - i)
if i == 0 {
entry.longOrd |= LastLongEntryMask
}
// Calculate the offsets of the string for this entry
j := (numLongEntries - i - 1) * 13
k := j + 13
if k > len(name) {
k = len(name)
}
entry.longChecksum = checksum
entry.longName = name[j:k]
}
return entries, nil
}
func decodeDOSTime(date, dosTime uint16, tenths uint8) time.Time {
return time.Date(
1980+int(date>>9),
time.Month((date>>5)&0x0F),
int(date&0x1F),
int(dosTime>>11),
int((dosTime>>5)&0x3F),
int((dosTime&0x1F)*2),
int(tenths)*10*int(time.Millisecond),
time.Local)
}
func encodeDOSTime(t time.Time) (uint16, uint16, uint8) {
var date uint16 = uint16((t.Year() - 1980) << 9)
date |= uint16(t.Month()) << 5
date += uint16(t.Day() & 0xFF)
var time uint16 = uint16(t.Hour() << 11)
time |= uint16(t.Minute() << 5)
time += uint16(t.Second() / 2)
var tenths uint8
// TODO(mitchellh): Do tenths
return date, time, tenths
}
|