///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: DirectoryEntry.sg
//
// Note:
//
// Based on page 23 of "Microsoft Extensible Firmware
// Initiative FAT32 File System Specification", Version 1.03,
// December 6, 2000, Microsoft Corporation.
using System.Runtime.InteropServices;
using System;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Channels;
namespace Microsoft.Singularity.Services.Fat.Fs
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal pointerfree struct DirectoryEntry
{
///////////////////////////////////////////////////////////////////////
// Constants
internal const uint Length = 32;
internal const byte Name00FreeEntry = 0xe5;
internal const byte Name00LastFreeEntry = 0x00;
internal const byte Name00ReallyE5 = 0x05; // Kanji mapping
internal const byte AsciiSpace = 0x20;
internal const byte AsciiPeriod = 0x2e;
internal const byte AttributeReadOnly = 0x01;
internal const byte AttributeHidden = 0x02;
internal const byte AttributeSystem = 0x04;
internal const byte AttributeVolumeId = 0x08;
internal const byte AttributeDirectory = 0x10;
internal const byte AttributeArchive = 0x20;
internal const byte AttributeLongName = (byte)(AttributeReadOnly |
AttributeHidden |
AttributeSystem |
AttributeVolumeId);
internal const byte AttributeLongNameMask = (byte)(AttributeLongName |
AttributeDirectory |
AttributeArchive);
// Mutable attributes on a file or directory
internal const byte AttributeMutable = (byte)(AttributeReadOnly |
AttributeHidden |
AttributeSystem);
private const int DayShift = 0;
private const int DayWidth = 5;
private const int MonthShift = 5;
private const int MonthWidth = 4;
private const int YearShift = 9;
private const int YearWidth = 7;
private const int BaseYear = 1980;
private const int TwoSecondShift = 0;
private const int TwoSecondWidth = 5;
private const int MinuteShift = 5;
private const int MinuteWidth = 6;
private const int HourShift = 11;
private const int HourWidth = 5;
internal const int ShortNameMaxLength = 12; // 8.3
internal const int ShortNameEntryLength = 11; // 8 + 3;
internal const int ShortNameMaxBaseName = 8;
internal const int ShortNameMaxExtension = 3;
///////////////////////////////////////////////////////////////////////
// Members
private byte name00; // 0..10
private byte name01;
private byte name02;
private byte name03;
private byte name04;
private byte name05;
private byte name06;
private byte name07;
private byte name08;
private byte name09;
private byte name10;
private byte attribute; // 11
private byte NtReserved; // 12
private byte CreationTimeTenths; // 13
private ushort creationTime; // 14..15
private ushort creationDate; // 16..17
private ushort lastAccessDate; // 18..19
private ushort firstClusterHi; // 20..21
private ushort lastWriteTime; // 22..23
private ushort lastWriteDate; // 24..25
private ushort firstClusterLo; // 26..27
private uint fileSize; // 28..31
///////////////////////////////////////////////////////////////////////
// Methods
internal bool IsFreeEntry
{
get { return name00 == Name00FreeEntry; }
}
internal bool IsFinalFreeEntry
{
get { return name00 == Name00LastFreeEntry; }
}
internal bool IsLongEntry
{
get {
return ((attribute == AttributeLongName) &&
!IsFinalFreeEntry && !IsFreeEntry);
}
}
internal bool IsShortEntry
{
get {
return ((attribute != AttributeLongName) &&
((attribute & AttributeVolumeId) == 0) &&
!IsFreeEntry && !IsFinalFreeEntry);
}
}
internal bool IsFile
{
get {
return ((attribute & (AttributeDirectory | AttributeVolumeId))
== 0);
}
}
internal bool IsDirectory
{
get {
return ((attribute & (AttributeDirectory | AttributeVolumeId))
== AttributeDirectory);
}
}
internal bool IsVolumeId
{
get {
return ((attribute & (AttributeDirectory | AttributeVolumeId))
== AttributeVolumeId);
}
}
internal bool IsParentPointer
{
get {
return (IsDirectory &&
this.name00 == AsciiPeriod &&
this.name01 == AsciiPeriod &&
this.name02 == AsciiSpace &&
this.name03 == AsciiSpace &&
this.name04 == AsciiSpace &&
this.name05 == AsciiSpace &&
this.name06 == AsciiSpace &&
this.name07 == AsciiSpace &&
this.name08 == AsciiSpace &&
this.name09 == AsciiSpace &&
this.name10 == AsciiSpace);
}
}
internal bool IsSelfPointer
{
get {
return (IsDirectory &&
this.name00 == AsciiPeriod &&
this.name01 == AsciiSpace &&
this.name02 == AsciiSpace &&
this.name03 == AsciiSpace &&
this.name04 == AsciiSpace &&
this.name05 == AsciiSpace &&
this.name06 == AsciiSpace &&
this.name07 == AsciiSpace &&
this.name08 == AsciiSpace &&
this.name09 == AsciiSpace &&
this.name10 == AsciiSpace);
}
}
ushort CreationTime
{
get { return ByteOrder.LittleEndianToHost(creationTime); }
set { creationTime = ByteOrder.HostToLittleEndian(value); }
}
ushort CreationDate
{
get { return ByteOrder.LittleEndianToHost(creationDate); }
set { creationDate = ByteOrder.HostToLittleEndian(value); }
}
ushort LastAccessDate
{
get { return ByteOrder.LittleEndianToHost(lastAccessDate); }
set { lastAccessDate = ByteOrder.HostToLittleEndian(value); }
}
internal uint FirstCluster
{
get {
return (((uint)ByteOrder.LittleEndianToHost(firstClusterHi)) << 16) + ByteOrder.LittleEndianToHost(firstClusterLo);
}
set {
firstClusterHi = ByteOrder.HostToLittleEndian((ushort)(value >> 16));
firstClusterLo = ByteOrder.HostToLittleEndian((ushort)(value & 0xffff));
}
}
ushort LastWriteTime
{
get { return ByteOrder.LittleEndianToHost(lastWriteTime); }
set { lastWriteTime = ByteOrder.HostToLittleEndian(value); }
}
ushort LastWriteDate
{
get { return ByteOrder.LittleEndianToHost(lastWriteDate); }
set { lastWriteDate = ByteOrder.HostToLittleEndian(value); }
}
uint FileSize
{
internal get { return ByteOrder.LittleEndianToHost(fileSize); }
set { fileSize = ByteOrder.HostToLittleEndian(value); }
}
ushort MakeDate(DateTime when)
requires when.Year >= BaseYear;
{
// We could be in 1979 because of timezone, but we're not obliged
// to support such legacy and we're certainly not writing files
// with this datestamp...
return (ushort) (((when.Year - BaseYear) << YearShift) |
(when.Month << MonthShift) |
(when.Day << DayShift));
}
ushort MakeTime(DateTime when)
{
return (ushort) ((when.Hour << HourShift) |
(when.Minute << MinuteShift) |
((when.Second / 2) << TwoSecondShift));
}
byte MakeTenths(DateTime when)
{
// The spec calls this value tenths.
// It's plainly hundredths from the definition.
int tenths = (when.Second % 2) * 100 + when.Millisecond / 10;
return (byte)tenths;
}
internal void Invalidate(bool toBeLastEntry)
{
this.name00 = toBeLastEntry ? Name00LastFreeEntry : Name00FreeEntry;
this.name01 = this.name02 = this.name03 = this.name04 = 0;
this.name05 = this.name06 = this.name07 = this.name08 = 0;
this.name09 = this.name10 = 0;
this.attribute = 0;
this.CreationTimeTenths = 0;
this.creationTime = 0;
this.creationDate = 0;
this.lastAccessDate = 0;
this.lastWriteTime = 0;
this.lastWriteDate = 0;
this.firstClusterLo = 0;
this.firstClusterHi = 0;
this.fileSize = 0;
}
private void Initialize(char[]! in ExHeap shortNameEntry,
byte attributes,
uint cluster)
requires shortNameEntry.Length == ShortNameEntryLength;
{
this.CopyShortNameEntry(shortNameEntry);
this.SetInitialMetaData(attributes, cluster);
}
private void SetInitialMetaData(byte attributes,
uint cluster)
{
DateTime now = DateTime.Now;
ushort time = MakeTime(now);
ushort date = MakeDate(now);
this.attribute = attributes;
this.NtReserved = 0;
this.FirstCluster = cluster;
this.CreationDate = date;
this.CreationTime = time;
this.CreationTimeTenths = MakeTenths(now);
this.LastAccessDate = date;
this.LastWriteTime = time;
this.LastWriteDate = date;
this.FileSize = 0;
}
/// Properties for mutable attribute bits
/// (ReadOnly, System, Hidden)
internal byte MutableAttributes
{
get {
return (byte)(this.attribute & AttributeMutable);
}
set {
int tmp = (this.attribute & AttributeMutable) | value;
this.attribute = (byte)tmp;
}
}
internal void InitializeAsDirectory(char[]! in ExHeap validName,
uint cluster)
{
Initialize(validName, AttributeDirectory, cluster);
}
internal void InitializeAsFile(char[]! in ExHeap validName,
uint cluster)
{
Initialize(validName, AttributeArchive, cluster);
}
internal void InitializeAsVolumeId(char[]! in ExHeap validName)
{
Initialize(validName, AttributeVolumeId, 0);
}
internal void InitializeAsParentPointer(uint parentCluster)
{
this.name00 = this.name01 = AsciiPeriod;
this.name02 = this.name03 = this.name04 = AsciiSpace;
this.name05 = this.name06 = this.name07 = AsciiSpace;
this.name08 = this.name09 = this.name10 = AsciiSpace;
this.attribute = AttributeDirectory;
this.NtReserved = 0;
this.FirstCluster = parentCluster;
DateTime now = DateTime.Now;
ushort time = MakeTime(now);
ushort date = MakeDate(now);
this.CreationDate = date;
this.CreationTime = time;
this.CreationTimeTenths = MakeTenths(now);
this.LastAccessDate = date;
this.LastWriteTime = time;
this.LastWriteDate = date;
this.FileSize = 0;
}
internal void InitializeAsSelfPointer(uint firstCluster)
{
this.name00 = AsciiPeriod;
this.name01 = this.name02 = this.name03 = this.name04 = AsciiSpace;
this.name05 = this.name06 = this.name07 = this.name08 = AsciiSpace;
this.name09 = this.name10 = AsciiSpace;
this.attribute = AttributeDirectory;
this.NtReserved = 0;
this.FirstCluster = firstCluster;
DateTime now = DateTime.Now;
ushort time = MakeTime(now);
ushort date = MakeDate(now);
this.CreationDate = date;
this.CreationTime = time;
this.CreationTimeTenths = MakeTenths(now);
this.LastAccessDate = date;
this.LastWriteTime = time;
this.LastWriteDate = date;
this.FileSize = 0;
}
internal bool UpdateAccessTime()
{
ushort now = MakeDate(DateTime.Now);
if (this.LastAccessDate != now) {
this.LastAccessDate = now;
return true;
}
return false;
}
internal bool UpdateFileSize(uint newFileBytes)
{
bool updated = this.UpdateWriteTime();
if (this.FileSize != newFileBytes) {
this.FileSize = newFileBytes;
updated = true;
}
return updated;
}
internal bool UpdateWriteTime()
{
DateTime now = DateTime.Now;
ushort date = MakeDate(now);
ushort time = MakeTime(now);
if (this.LastWriteTime != time ||
this.LastWriteDate != date) {
this.LastWriteTime = time;
this.LastWriteDate = date;
this.LastAccessDate = date;
return true;
}
return false;
}
internal static bool ValidShortNameCharacter(char c)
{
// Page 24 of FAT spec.
if (c > 127) {
return true;
}
if ((c < 0x20) || (c == 0x22) || (c >= 0x2a && c <= 0x2c) ||
(c == 0x2e) || (c == 0x2f) || (c >= 0x3a && c <= 0x3f) ||
(c >= 0x5b && c <= 0x5d)) {
return false;
}
return true;
}
internal static bool ValidShortName(char[]! in ExHeap name)
{
if (name.Length == 0 || name.Length > ShortNameMaxLength) {
return false;
}
else if (name[0] == '.') {
// Dot in position zero reserved for Directory names ".", ".."
return false;
}
int dotLocation = -1;
for (int i = 0; i < name.Length; i++) {
char c = Char.ToUpper(name[i]);
if (ValidShortNameCharacter(c)) {
continue;
}
else if (c == '.') {
if (dotLocation > 0) {
// Two dots are not permitted in name.
return false;
}
dotLocation = i;
}
}
if (dotLocation > 0) {
if (name.Length - dotLocation > ShortNameMaxExtension + 1) {
// File extension is too long.
return false;
}
else if (dotLocation > ShortNameMaxBaseName) {
// File base name is too long.
return false;
}
}
else if (name.Length > ShortNameMaxBaseName) {
// File base name is too long without a dot.
return false;
}
return true;
}
internal static bool ValidPackedName(char[]! in ExHeap shortName)
{
if (shortName.Length != ShortNameEntryLength) {
return false;
}
if (shortName[ShortNameMaxBaseName] == '.' ||
shortName[ShortNameMaxBaseName - 1] == '.' ||
shortName[0] == '.') {
return false;
}
for (int i = 0; i < shortName.Length; i++) {
char c = Char.ToUpper(shortName[i]);
if (!ValidShortNameCharacter(c)) {
return false;
}
}
return true;
}
/// Returns the length of the provided name
/// excluding any trailing spaces.
private static int UnpaddedLength(char[]! in ExHeap shortName)
{
int l = shortName.Length;
while (l > 0) {
if (shortName[l - 1] != ' ') {
return l;
}
l--;
}
return 0;
}
internal void CopyShortNameEntry(char[]! in ExHeap shortNameEntry)
requires shortNameEntry.Length == ShortNameEntryLength;
{
name00 = (byte)Char.ToUpper(shortNameEntry[0]);
name01 = (byte)Char.ToUpper(shortNameEntry[1]);
name02 = (byte)Char.ToUpper(shortNameEntry[2]);
name03 = (byte)Char.ToUpper(shortNameEntry[3]);
name04 = (byte)Char.ToUpper(shortNameEntry[4]);
name05 = (byte)Char.ToUpper(shortNameEntry[5]);
name06 = (byte)Char.ToUpper(shortNameEntry[6]);
name07 = (byte)Char.ToUpper(shortNameEntry[7]);
name08 = (byte)Char.ToUpper(shortNameEntry[8]);
name09 = (byte)Char.ToUpper(shortNameEntry[9]);
name10 = (byte)Char.ToUpper(shortNameEntry[10]);
}
/// Packs short name into directory entry.
/// Name to be packed. The name may be
/// padded with trailing spaces.
internal void PackShortName(char[]! in ExHeap validShortName)
{
int upLength = UnpaddedLength(validShortName);
int dot = upLength;
for (int i = upLength - 1; i >= 0; i--) {
if (validShortName[i] == '.') {
dot = i;
break;
}
}
// Valid name assertions (weak)
assert dot != 0;
assert dot <= ShortNameMaxBaseName;
assert upLength - dot - 1 <= ShortNameMaxExtension;
// Mark all characters as space to signify empty.
name00 = name01 = name02 = name03 = name04 = name05 = (byte)' ';
name06 = name07 = name08 = name09 = name10 = (byte)' ';
switch (dot) {
case 8:
name07 = (byte)Char.ToUpper(validShortName[7]);
goto case 7;
case 7:
name06 = (byte)Char.ToUpper(validShortName[6]);
goto case 6;
case 6:
name05 = (byte)Char.ToUpper(validShortName[5]);
goto case 5;
case 5:
name04 = (byte)Char.ToUpper(validShortName[4]);
goto case 4;
case 4:
name03 = (byte)Char.ToUpper(validShortName[3]);
goto case 3;
case 3:
name02 = (byte)Char.ToUpper(validShortName[2]);
goto case 2;
case 2:
name01 = (byte)Char.ToUpper(validShortName[1]);
goto case 1;
case 1:
name00 = (byte)Char.ToUpper(validShortName[0]);
break;
}
switch (upLength - dot - 1) {
case 3:
name10 = (byte)Char.ToUpper(validShortName[dot + 3]);
goto case 2;
case 2:
name09 = (byte)Char.ToUpper(validShortName[dot + 2]);
goto case 1;
case 1:
name08 = (byte)Char.ToUpper(validShortName[dot + 1]);
goto case 0;
case 0: break;
case -1: break;
default:
DebugStub.Break();
break;
}
int bl = GetBaseLength();
int tl = GetTailLength();
assert ((bl + tl + 1 == upLength) || // Dot present.
(dot == upLength && dot == bl)); // No dot.
}
private int GetTailLength()
{
if (name10 != AsciiSpace) return 3;
if (name09 != AsciiSpace) return 2;
if (name08 != AsciiSpace) return 1;
return 0;
}
private int GetBaseLength()
{
if (name07 != AsciiSpace) return 8;
if (name06 != AsciiSpace) return 7;
if (name05 != AsciiSpace) return 6;
if (name04 != AsciiSpace) return 5;
if (name03 != AsciiSpace) return 4;
if (name02 != AsciiSpace) return 3;
if (name01 != AsciiSpace) return 2;
if (name00 != AsciiSpace) return 1;
return 0;
}
public int GetPathLength()
{
int head = GetBaseLength();
int tail = GetTailLength();
return (tail == 0) ? head : head + tail + 1;
}
private void CopyBase(int length, char []! in ExHeap s)
requires s.Length >= length;
requires length > 0;
{
switch (length) {
case 8: s[7] = (char)name07; goto case 7;
case 7: s[6] = (char)name06; goto case 6;
case 6: s[5] = (char)name05; goto case 5;
case 5: s[4] = (char)name04; goto case 4;
case 4: s[3] = (char)name03; goto case 3;
case 3: s[2] = (char)name02; goto case 2;
case 2: s[1] = (char)name01; break;
case 1: break;
}
if (name00 == Name00ReallyE5) {
s[0] = (char)((byte)0xe5);
}
else {
s[0] = (char)name00;
}
}
private void CopyTail(int offset, int length, char[]! in ExHeap s)
requires offset + length == s.Length;
{
switch (length) {
case 3: s[offset + 2] = (char)name10; goto case 2;
case 2: s[offset + 1] = (char)name09; goto case 1;
case 1: s[offset + 0] = (char)name08; break;
case 0: break;
}
}
internal char[]! in ExHeap GetPath()
{
int baseLength = GetBaseLength();
int tailLength = GetTailLength();
if (tailLength == 0) {
char[] in ExHeap path = new [ExHeap] char [baseLength];
CopyBase(baseLength, path);
return path;
}
else {
char[] in ExHeap path =
new [ExHeap] char [baseLength + tailLength + 1];
CopyBase(baseLength, path);
path[baseLength] = (char)AsciiPeriod;
CopyTail(baseLength + 1, tailLength, path);
return path;
}
}
private byte ChecksumStep(byte sum, byte newValue)
{
int s = (int)sum;
int t = ((s & 1) << 7) + (s >> 1);
return (byte) ((int)t + (int)newValue);
}
internal byte Checksum
{
get {
byte sum = 0;
sum = ChecksumStep(sum, name00);
sum = ChecksumStep(sum, name01);
sum = ChecksumStep(sum, name02);
sum = ChecksumStep(sum, name03);
sum = ChecksumStep(sum, name04);
sum = ChecksumStep(sum, name05);
sum = ChecksumStep(sum, name06);
sum = ChecksumStep(sum, name07);
sum = ChecksumStep(sum, name08);
sum = ChecksumStep(sum, name09);
sum = ChecksumStep(sum, name10);
return sum;
}
}
internal int GetDirHashCode()
{
int sum = 0;
int length = GetBaseLength();
sum = length * 9601;
switch (GetBaseLength()) {
case 8: sum += (int)name07; goto case 7;
case 7: sum += (int)name06; goto case 6;
case 6: sum += (int)name05; goto case 5;
case 5: sum += (int)name04; goto case 4;
case 4: sum += (int)name03; goto case 3;
case 3: sum += (int)name02; goto case 2;
case 2: sum += (int)name01; goto case 1;
case 1:
if (name00 == Name00ReallyE5) {
sum += 0xe5;
}
else {
sum += (int)name00;
}
break;
case 0: break;
}
length = GetTailLength();
sum = sum + length * 10313;
switch (GetTailLength()) {
case 3: sum += (int)name10; goto case 2;
case 2: sum += (int)name09; goto case 1;
case 1: sum += (int)name08; break;;
case 0: break;
}
return sum & DHashtable.MaxKeyValue;
}
internal static int GetDirHashCode(char []! in ExHeap shortName)
{
if (shortName.Length == ShortNameEntryLength &&
shortName[ShortNameMaxBaseName] != '.' &&
shortName[ShortNameMaxBaseName - 1] != '.') {
// Name looks like it is already packed - it's 11 characters
// long and not 8.2 nor 7.3. There are no other valid
// short names that are 11 characters long.
DirectoryEntry de = new DirectoryEntry();
de.name00 = (byte)Char.ToUpper(shortName[0]);
de.name01 = (byte)Char.ToUpper(shortName[1]);
de.name02 = (byte)Char.ToUpper(shortName[2]);
de.name03 = (byte)Char.ToUpper(shortName[3]);
de.name04 = (byte)Char.ToUpper(shortName[4]);
de.name05 = (byte)Char.ToUpper(shortName[5]);
de.name06 = (byte)Char.ToUpper(shortName[6]);
de.name07 = (byte)Char.ToUpper(shortName[7]);
de.name08 = (byte)Char.ToUpper(shortName[8]);
de.name09 = (byte)Char.ToUpper(shortName[9]);
de.name10 = (byte)Char.ToUpper(shortName[10]);
return de.GetDirHashCode();
}
else {
DirectoryEntry de = new DirectoryEntry();
de.PackShortName(shortName);
return de.GetDirHashCode();
}
}
internal bool HasName(char []! in ExHeap name)
{
if (name.Length == ShortNameEntryLength &&
name[ShortNameMaxBaseName] != '.' &&
name[ShortNameMaxBaseName - 1] != '.') {
// Name looks like it is already packed - it's 11 characters
// long and not 8.2 nor 7.3. There are no other valid
// short names that are 11 characters long.
return (this.name00 == (byte)Char.ToUpper(name[0]) &&
this.name01 == (byte)Char.ToUpper(name[1]) &&
this.name02 == (byte)Char.ToUpper(name[2]) &&
this.name03 == (byte)Char.ToUpper(name[3]) &&
this.name04 == (byte)Char.ToUpper(name[4]) &&
this.name05 == (byte)Char.ToUpper(name[5]) &&
this.name06 == (byte)Char.ToUpper(name[6]) &&
this.name07 == (byte)Char.ToUpper(name[7]) &&
this.name08 == (byte)Char.ToUpper(name[8]) &&
this.name09 == (byte)Char.ToUpper(name[9]) &&
this.name10 == (byte)Char.ToUpper(name[10]));
}
else {
DirectoryEntry de = new DirectoryEntry();
de.PackShortName(name);
return (this.name00 == de.name00 &&
this.name01 == de.name01 &&
this.name02 == de.name02 &&
this.name03 == de.name03 &&
this.name04 == de.name04 &&
this.name05 == de.name05 &&
this.name06 == de.name06 &&
this.name07 == de.name07 &&
this.name08 == de.name08 &&
this.name09 == de.name09 &&
this.name10 == de.name10);
}
}
}
}