singrdk/base/Services/Fat/ClientManager/FatClientManager.sg

454 lines
16 KiB
Plaintext

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: Fat/ClientManager/FatClientManager.sg
//
// Note:
//
using Microsoft.SingSharp;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Directory;
using Microsoft.Singularity.Services.Fat.Contracts;
using System;
using System.Collections;
using System.IO;
using System.Text;
[assembly: Microsoft.Singularity.Security.ApplicationPublisherAttribute("singularity.microsoft.com")]
[assembly: Microsoft.Singularity.Security.AssertPrivilegeAttribute("$register-privilege.localhost")]
namespace Microsoft.Singularity.Services.Fat.ClientManager
{
public class ClientManager
{
internal static readonly char PathSeparator = Path.DirectorySeparatorChar;
private static ArrayList! mounts = new ArrayList();
internal class Client
{
TRef<FatClientContract.Imp:Running>! tref;
string! diskPath;
string! mountPath;
bool readOnly;
uint cacheMB;
internal Client(
[Claims] FatClientContract.Imp! imp,
string! diskPath,
string! mountPath,
bool readOnly,
uint cacheMB
)
{
this.tref = new TRef<FatClientContract.Imp:Running>(imp);
this.diskPath = diskPath;
this.mountPath = mountPath;
this.readOnly = readOnly;
this.cacheMB = cacheMB;
}
internal FatClientContract.Imp! Acquire()
{
return tref.Acquire();
}
internal void Release([Claims] FatClientContract.Imp! imp)
{
tref.Release(imp);
}
internal string! DiskPath { get { return this.diskPath; } }
internal string! MountPath { get { return this.mountPath; } }
}
internal static void
AddClient(Client newClient)
{
mounts.Add(newClient);
}
internal static Client GetClientByMountPath(string! path)
requires (path.Length > 0 &&
path[path.Length - 1] != Path.DirectorySeparatorChar);
{
for (int i = 0; i < mounts.Count; i++) {
Client! cs = (Client!)mounts[i];
if (String.Compare(cs.MountPath, path, true) == 0) {
return cs;
}
}
return null;
}
internal static Client GetClientByDiskPath(string! path)
requires (path.Length > 0 &&
path[path.Length - 1] != Path.DirectorySeparatorChar);
{
for (int i = 0; i < mounts.Count; i++) {
Client! cs = (Client!)mounts[i];
if (String.Compare(cs.DiskPath, path, true) == 0) {
return cs;
}
}
return null;
}
internal static void DeleteClient(Client! closedClient)
{
mounts.Remove(closedClient);
}
internal static bool ClientIsClosed(Client! client)
{
FatClientContract.Imp:Running! clientImp = client.Acquire();
bool closed = false;
switch receive {
case clientImp.ChannelClosed():
closed = true;
break;
case timeout:
break;
}
client.Release(clientImp);
return closed;
}
private static void
DoMount(FatControlContract.Exp:Ready! controller,
[Claims]
FatMountSettings*! in ExHeap fms)
{
string! diskPath = ToPath(fms->DiskPath);
string! mountPath = ToPath(fms->MountPath);
bool readOnly = fms->ReadOnly;
uint cacheMB = fms->CacheMB;
Client client = GetClientByDiskPath(diskPath);
if (client != null) {
if (!ClientIsClosed(client)) {
// Already using disk path
controller.SendFail(FatContractErrorCode.DiskUnavailable);
delete fms;
return;
}
else {
DeleteClient(client);
client = null;
}
}
client = GetClientByMountPath(mountPath);
if (client != null) {
if (!ClientIsClosed(client)) {
// Already using mount path
controller.SendFail(FatContractErrorCode.PathUnavailable);
delete fms;
return;
}
else {
DeleteClient(client);
client = null;
}
}
FatClientContract.Imp! clientImp;
FatClientContract.Exp! clientExp;
FatClientContract.NewChannel(out clientImp, out clientExp);
string [] args = { "fatfs" };
Process process = new Process(args);
process.SetStartupEndpoint(0, clientExp);
process.Start();
clientImp.SendMount(fms);
switch receive
{
case clientImp.Success():
AddClient(
new Client(clientImp, diskPath, mountPath,
readOnly, cacheMB)
);
controller.SendSuccess();
break;
case clientImp.ChannelClosed():
controller.SendSuccess();
delete clientImp;
break;
case clientImp.Fail(reason):
controller.SendFail(reason);
delete clientImp;
break;
}
}
private static void
DoUnmount(
FatControlContract.Exp:Ready! controller,
[Claims] char[]! in ExHeap mountPath,
bool force
)
{
string path = ToPath(Bitter.ToString2(mountPath));
delete mountPath;
Client client = GetClientByMountPath(path);
if (client == null) {
controller.SendFail(FatContractErrorCode.NotMounted);
return;
}
else if (ClientIsClosed(client)) {
controller.SendSuccess();
return;
}
FatClientContract.Imp! clientImp = client.Acquire();
clientImp.SendUnmount(force);
switch receive {
case clientImp.Success():
controller.SendSuccess();
client.Release(clientImp);
DeleteClient(client);
break;
case clientImp.Fail(reason):
controller.SendFail(reason);
client.Release(clientImp);
break;
case clientImp.ChannelClosed():
controller.SendFail(FatContractErrorCode.InternalError);
client.Release(clientImp);
DeleteClient(client);
break;
}
}
private static void
DoGetPreferredFormatSettings(
FatControlContract.Exp:Ready! controller,
[Claims] char[]! in ExHeap diskPath
)
{
string! cleanPath = ToPath(Bitter.ToString2(diskPath));
delete diskPath;
FatClientContract.Imp! clientImp;
FatClientContract.Exp! clientExp;
FatClientContract.NewChannel(out clientImp, out clientExp);
string [] args = { "fatfs" };
Process process = new Process(args);
process.SetStartupEndpoint(0, clientExp);
process.Start();
clientImp.SendGetPreferredFormatSettings(
Bitter.FromString2(cleanPath)
);
try {
switch receive
{
case clientImp.PreferredFormatSettings(settings):
controller.SendPreferredFormatSettings(settings);
break;
case clientImp.Fail(error):
controller.SendFail(error);
break;
case clientImp.ChannelClosed():
DebugStub.Break();
break;
}
}
finally {
delete clientImp;
}
}
private static void
DoFormat(
FatControlContract.Exp:Ready! controller,
[Claims] char[]! in ExHeap diskPath,
[Claims] char[]! in ExHeap volumeLabel,
[Claims] FatFormatSettings*! in ExHeap settings
)
{
string! cleanPath = ToPath(Bitter.ToString2(diskPath));
delete diskPath;
FatClientContract.Imp! clientImp;
FatClientContract.Exp! clientExp;
FatClientContract.NewChannel(out clientImp, out clientExp);
string [] args = { "fatfs" };
Process process = new Process(args);
process.SetStartupEndpoint(0, clientExp);
process.Start();
clientImp.SendFormat(Bitter.FromString2(cleanPath),
volumeLabel,
settings);
try {
switch receive
{
case clientImp.Success():
controller.SendSuccess();
break;
case clientImp.Fail(error):
controller.SendFail(error);
break;
case clientImp.ChannelClosed():
DebugStub.Break();
break;
}
}
finally {
delete clientImp;
}
}
private static void Shutdown()
{
}
private static void ServiceClients(ServiceProviderContract.Exp! spExp)
{
ESet<FatControlContract.Exp:Ready> controllers =
new ESet<FatControlContract.Exp:Ready>();
bool run = true;
while (run) {
switch receive {
case spExp.Connect(candidate):
FatControlContract.Exp controller =
candidate as FatControlContract.Exp;
if (controller != null) {
controller.SendSuccess();
controllers.Add(controller);
spExp.SendAckConnect();
}
else {
spExp.SendNackConnect(candidate);
}
break;
case spExp.ChannelClosed():
run = false;
break;
case controller.Mount(settings) in controllers:
DoMount(controller, settings);
controllers.Add(controller);
break;
case controller.Unmount(mountPath, force) in controllers:
DoUnmount(controller, mountPath, force);
controllers.Add(controller);
break;
case controller.GetPreferredFormatSettings(diskPath) in controllers:
DoGetPreferredFormatSettings(controller, diskPath);
controllers.Add(controller);
break;
case controller.Format(diskPath, volumeLabel, fms) in controllers:
DoFormat(controller, diskPath, volumeLabel, fms);
controllers.Add(controller);
break;
case controller.ChannelClosed() in controllers:
delete controller;
break;
}
}
Shutdown();
controllers.Dispose();
}
/// <remarks> Sanitize path string. This method removes
/// trailing separators and repeat sequences of
/// separators. </remarks>
private static string! ToPath(string! p)
{
int length = p.Length;
if (p.Length > 0 && p.Length - 1 == Path.DirectorySeparatorChar) {
length = p.LastIndexOf(Path.DirectorySeparatorChar);
}
StringBuilder sb = new StringBuilder(length);
bool multi = false;
for (int i = 0; i < length; i++) {
if (p[i] == Path.DirectorySeparatorChar) {
if (multi) {
continue;
}
else {
sb.Append(p[i]);
multi = true;
}
}
else {
sb.Append(p[i]);
multi = false;
}
}
return sb.ToString();
}
public static int Main()
{
ServiceProviderContract.Imp! spImp;
ServiceProviderContract.Exp! spExp;
ServiceProviderContract.NewChannel(out spImp, out spExp);
DirectoryServiceContract.Imp! dsImp =
DirectoryService.NewClientEndpoint();
dsImp.SendRegister(Bitter.FromString2(
FatControlContract.ManagerControlPath
),
spImp);
try {
switch receive {
case dsImp.AckRegister():
DebugStub.Print(
"FatClientManager registered at: {0}\n",
__arglist(
FatControlContract.ManagerControlPath
)
);
delete dsImp;
ServiceClients(spExp);
break;
case dsImp.NakRegister(rejectedImp, error):
delete rejectedImp;
delete dsImp;
DebugStub.Print(
"FatClientManager failed to register" +
"(error=\"{0}\")\n", __arglist(error.ToString())
);
break;
case dsImp.ChannelClosed():
delete dsImp;
DebugStub.Print(
"FatClientManager lost connection to namespace\n"
);
break;
}
}
finally {
delete spExp;
}
return 0;
}
}
}