/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: WebAppFSUtils.sg // // Note: File system operations for general use by webapps // using System; using System.Collections; using System.Diagnostics; using Microsoft.SingSharp; using Microsoft.SingSharp.Runtime; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; using Microsoft.Singularity.WebApps; using Microsoft.Singularity.WebApps.Contracts; using System.Text; using System.Web; using FileSystem.Utils; namespace Microsoft.Singularity.WebApps { public class WebAppFSUtils { private const string NoSuchPathPreamble = "No such path

No such path as \""; private const string NoSuchPathPostamble = "\"

"; private const string PathIsFilePreamble = "Path is file

Path \""; private const string PathIsFilePostamble = "\" is a file

"; private const string DirListingPreamble = "Directory Listing

Directory Listing

"; private const string DirListingPostamble = ""; private const string NSListingPreamble = "Directory Listing

Directory Listing

"; private const string NSListingPostamble = ""; private const string ErrorPreamble = "Error"; private const string ErrorPostamble = ""; public static void ServeFSPath(string! path, IHttpRequest! request, DirectoryServiceContract.Imp:Ready! nsImp) { ulong startCycles, endCycles; startCycles = Processor.CycleCount; // First check if this is a file FileContract.Imp:Ready fileChan = FileUtils.OpenFile(path, nsImp); endCycles = Processor.CycleCount; if ((endCycles - startCycles) > (ulong)((ulong)Processor.CyclesPerSecond / (ulong)5000)) { //DebugStub.WriteLine("Slow file open"); } if (fileChan != null) { startCycles = Processor.CycleCount; ServeFile(fileChan, request); delete fileChan; endCycles = Processor.CycleCount; if ((endCycles - startCycles) > (ulong)((ulong)Processor.CyclesPerSecond / (ulong)5000)) { //DebugStub.WriteLine("Slow file read"); } return; } // Fallback: can this path be enumerated via the namespace? if (IsValidNSPath(path, nsImp)) { ServeNSListing(path, request, nsImp); } else { // OK, we give up request.SendStatus(404, "Not Found"); request.SendHeader("Content-type", "text/html"); request.SendBodyData(Encoding.ASCII.GetBytes(NoSuchPathPreamble)); request.SendBodyData(Encoding.ASCII.GetBytes(path)); request.SendBodyData(Encoding.ASCII.GetBytes(NoSuchPathPostamble)); request.Done(); } } public static void ServeFile(FileContract.Imp:Ready! fileImp, IHttpRequest! request) { request.SendStatus(200, "OK"); TransmitFileContents(fileImp, request); request.Done(); } public static void TransmitFileContents(FileContract.Imp:Ready! fileImp, IHttpRequest! request) { long readSize = 1024 * 64; long readOffset = 0; bool keepReading = true; do { byte[] in ExHeap buf = new[ExHeap] byte[readSize]; fileImp.SendRead(buf, 0, readOffset, readSize); switch receive { case fileImp.AckRead( _buf, long bytesRead, int error) : if ((error != 0) || (_buf == null)) { if (_buf != null) { delete _buf; } keepReading = false; } else { byte[]! in ExHeap original = _buf; if (bytesRead > 0) { if (bytesRead < readSize) { // We must be at the end of the file; the FS only // filled in part of our supplied block. // Discard the remainder of the block. byte[]! in ExHeap remainder = Bitter.SplitOff(ref original, (int)bytesRead); delete remainder; } request.SendBodyData(original); readOffset += bytesRead; } else { // By coincidence, our previous read took us to // exactly the end of the file. We should // optimize this away somehow delete original; } if (bytesRead < readSize) { // We're at the end keepReading = false; } } break; case fileImp.ChannelClosed() : keepReading = false; break; } } while(keepReading); } public static void ServeNSListing(string! path, IHttpRequest! request, DirectoryServiceContract.Imp:Ready! nsImp) { ArrayList! listing; ErrorCode errorOut; long length; NodeType nodeType; bool ok; int rc = GetNSListing(path, nsImp, out listing); if (rc != 0) { request.SendStatus(500, "Error code " + rc + " while generating listing"); request.SendHeader("Content-type", "text/html"); request.SendBodyData(Encoding.ASCII.GetBytes(ErrorPreamble)); request.SendBodyData(Encoding.ASCII.GetBytes("Error code " + rc + " while generating listing")); request.SendBodyData(Encoding.ASCII.GetBytes(ErrorPostamble)); request.Done(); } else { request.SendStatus(200, "OK"); request.SendHeader("Content-type", "text/html"); request.SendBodyData(Encoding.ASCII.GetBytes(NSListingPreamble)); request.SendBodyData(Encoding.ASCII.GetBytes(" Listing for \"" + path + "\":

")); foreach(string! subPath in listing) { string name; if (path == "/") name = subPath; else name = path + "/" +subPath; ok = SdsUtils.GetAttributes(name, nsImp, out length, out nodeType, out errorOut); if (!ok) { // No hyperlink request.SendBodyData(Encoding.ASCII.GetBytes(subPath + "(ERROR)
")); } else { if (nodeType == NodeType.File) { // Hyperlink + file info request.SendBodyData(Encoding.ASCII.GetBytes( GetNodeTypeString(nodeType) + "" + subPath + " " + " (" + length + ")
")); } else { // if (nodeType == NodeType.Directory) { // Hyperlink + directory info request.SendBodyData(Encoding.ASCII.GetBytes( GetNodeTypeString(nodeType) + "[" + subPath + "]" + "
")); } } } request.SendBodyData(Encoding.ASCII.GetBytes("

" + NSListingPostamble)); request.Done(); } } private static string GetNodeTypeString(NodeType nodeType) { const string startVal = "("; const string endVal = ")"; string retval = startVal; switch (nodeType) { case NodeType.File: retval += "N"; break; case NodeType.Directory: retval += "D"; break; case NodeType.SymLink: retval += "L"; break; case NodeType.ServiceProvider: retval += "S"; break; case NodeType.IoMemory: retval += "I"; break; case NodeType.BadNode: retval += "BAD"; break; } if (retval.Equals(startVal)) { return String.Empty; } else { return retval + endVal; } } private static bool IsValidNSPath(string! path, DirectoryServiceContract.Imp:Ready! nsImp) { NodeType nodeType; long size; ErrorCode error; return FileUtils.GetAttributes(path, nsImp, out size, out nodeType, out error); } // Returns 0 on success; if return val != 0, outparam is null private static int GetNSListing(string! path, DirectoryServiceContract.Imp:Ready! nsImp, out ArrayList! listing) { listing = new ArrayList(); ErrorCode errorOut; DirectoryServiceContract.Imp! dirImp; DirectoryServiceContract.Exp! dirExp; DirectoryServiceContract.NewChannel(out dirImp, out dirExp); bool ok = SdsUtils.Bind(path, nsImp, dirExp, out errorOut); if (!ok) { delete dirImp; return -1; //unable to bind to requested dir } dirImp.RecvSuccess(); EnumerationRecords[] in ExHeap responses = SdsUtils.EnumerateDirectory (dirImp, out errorOut); delete dirImp; if (null == responses) { return -1; } else { for (int i=0; i < responses.Length; i++){ string name; expose (responses[i]) { name = Bitter.ToString2(responses[i].Path); listing.Add(name); } Console.WriteLine(name); } delete responses; return 0; } } } }