///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: FsObject.sg
//
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Directory;
namespace Microsoft.Singularity.Services.Fat.Fs
{
/// Base class for file system objects.
/// Provides reference counting functionality and support for
/// common directory and file operations, such as updating the
/// last write time.
///
internal abstract class FsObject
{
private Directory parent;
private int shortEntryOffset;
private int refcnt;
public string cachedName = null; // Debugging info
public string cachedPathName = null; // Debugging info
///
/// Create an FsObject with a reference count of 1.
///
[ Microsoft.Contracts.NotDelayed ]
internal FsObject(Directory! parent, int shortEntryOffset)
requires shortEntryOffset > 0;
{
this.parent = parent;
this.shortEntryOffset = shortEntryOffset;
this.refcnt = 1;
}
internal FsObject()
{
this.refcnt = 0;
this.shortEntryOffset = -1;
this.parent = null;
}
[ Microsoft.Contracts.Pure ]
internal bool HasParent { get { return parent != null; } }
[ Microsoft.Contracts.Pure ]
internal bool HasNoReferences { get { return this.refcnt == 0; } }
[ Microsoft.Contracts.Pure ]
internal bool HasOneReference { get { return this.refcnt == 1; } }
[ Microsoft.Contracts.Pure ]
internal int ShortEntryOffset
{
get { return this.shortEntryOffset; }
}
abstract internal int FirstCluster
{
get;
}
private void PopulateCachedName()
requires this.cachedName == null;
ensures this.cachedName != null;
{
if (parent != null) {
char[]! in ExHeap v = parent.GetName(this.shortEntryOffset);
this.cachedName = Bitter.ToString2(v);
delete v;
}
else {
this.cachedName = "/";
}
}
internal string Name
{
get {
if (cachedName == null) {
PopulateCachedName();
}
return cachedName;
}
}
internal bool GetShortDirectoryEntry(out DirectoryEntry de)
{
if (this.parent != null) {
this.parent.GetShortDirectoryEntry(this.shortEntryOffset,
out de);
return true;
}
de = new DirectoryEntry();
return false;
}
private void PopulateCachedPathName()
requires this.cachedPathName == null;
ensures this.cachedPathName != null;
{
if (parent != null) {
if (this.parent.FullPathName == "/") {
this.cachedPathName = this.parent.FullPathName + this.Name;
}
else {
this.cachedPathName = this.parent.FullPathName + "/" + this.Name;
}
}
else {
this.cachedPathName = this.Name;
}
}
internal string FullPathName
{
get {
if (cachedPathName == null) {
PopulateCachedPathName();
}
return cachedPathName;
}
}
internal void UpdateLastWriteTime()
{
if (parent != null && !FatVolume.IsReadOnly) {
parent.UpdateLastWriteTime(this.shortEntryOffset);
}
}
internal void UpdateLastAccessTime()
{
if (parent != null && !FatVolume.IsReadOnly) {
parent.UpdateLastAccessTime(this.shortEntryOffset);
}
}
/// Update the file size field in the
/// directory entry associated with a file object. This
/// method also updates the last access and last update
/// fields.
internal void UpdateFileSize(uint fileBytes)
{
if (parent != null && !FatVolume.IsReadOnly) {
parent.UpdateFileSize(this.shortEntryOffset, fileBytes);
}
}
/// Update the first cluster field in the
/// directory entry associated with a file object. This
/// method also updates the last access and last update
/// fields. It is called when a zero length file has been
/// created and has no first cluster, and is subsequently
/// grown
internal void UpdateFirstCluster(uint firstCluster)
{
if (parent != null && !FatVolume.IsReadOnly) {
parent.UpdateFirstCluster(this.shortEntryOffset, firstCluster);
}
}
internal void SetMutableAttributes(byte newAttributes)
requires (newAttributes & ~DirectoryEntry.AttributeMutable) == 0;
{
if (parent != null && !FatVolume.IsReadOnly) {
parent.SetMutableAttributes(this.shortEntryOffset,
newAttributes);
}
}
internal byte GetMutableAttributes()
{
if (parent != null) {
return parent.GetMutableAttributes();
}
return 0;
}
internal void
GetAttributes(ref FileAttributesRecord fileAttributes)
{
if (parent == null) {
fileAttributes.Type = NodeType.Directory;
fileAttributes.FileSize = 0;
}
else {
parent.GetAttributes(this.shortEntryOffset,
ref fileAttributes);
}
}
/// Increment reference count by 1
internal void AddRef()
{
this.refcnt++;
}
/// Decrement reference count by 1
internal void Release()
{
this.refcnt--;
}
internal void CloseInstance()
{
if (parent != null) {
parent.CloseOpenFsObject(this);
}
}
}
}