/////////////////////////////////////////////////////////////////////////////// // // 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! 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(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 controllers = new ESet(); 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(); } /// Sanitize path string. This method removes /// trailing separators and repeat sequences of /// separators. 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; } } }