454 lines
16 KiB
Plaintext
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;
|
||
|
}
|
||
|
}
|
||
|
}
|