///////////////////////////////////////////////////////////////////////////////
//
// 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 pathNo such path as \"";
private const string NoSuchPathPostamble = "\"
";
private const string PathIsFilePreamble =
"Path is filePath \"";
private const string PathIsFilePostamble = "\" is a file
";
private const string DirListingPreamble =
"Directory ListingDirectory Listing
";
private const string DirListingPostamble = "";
private const string NSListingPreamble =
"Directory ListingDirectory 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;
}
}
}
}