Implement FileSystem almost as MemoryFileSystemProvider.

0 votes
asked May 15 by dmitry.petrishin (400 points)

Hello, I'm trying to implement my own FileSystem. I bet you know :))

So I faced with few problems, do I understand correctly to implement something like Memory file system provider I need to create private variable with NodeBase type in CustomProvider class and store everything there? Or it's stored somewhere in base class? And, for example, for getting of children I need to get the data from base?

Sorry for retarding :)
Thanks.

Applies to: Rebex SFTP

1 Answer

+1 vote
answered May 15 by renestein (2,310 points)
selected May 16 by dmitry.petrishin
 
Best answer

Hi Dmitry,
I am afraid that one FileNode/DirectoryNode for file system is never enough. BTW: As you probably know, NodeBase is abstract class.
Quick and dirty simple memory provider. I hope that code speaks for itself.
Obvious warnings apply. Code has no production quality (e.g. provider is not thread safe) .

using System.Collections.Generic;
using System.IO;
using System.Linq;
using Rebex.IO.FileSystem;

namespace MemoryFs
{

    public class SimpleMemoryProvider
    : ReadWriteFileSystemProvider
{
    private readonly Dictionary<NodePath, NodeBase> _pathToNodeDictionary;
    private readonly Dictionary<NodeBase, FsNodeData> _storage;

    public SimpleMemoryProvider() : this(null)
    {
    }

    public SimpleMemoryProvider(FileSystemProviderSettings settings) : base(settings)
    {
        _pathToNodeDictionary = new Dictionary<NodePath, NodeBase>();
        _storage = new Dictionary<NodeBase, FsNodeData>();
    }

    public void AddRoot()
    {
        init();
    }

    private Dictionary<NodeBase, FsNodeData> Storage
    {
        get
        {
            return _storage;
        }
    }

    private Dictionary<NodePath, NodeBase> PathToNodeDictionary
    {
        get
        {
            return _pathToNodeDictionary;
        }
    }


    protected override NodeAttributes GetAttributes(NodeBase node)
    {
        if (!node.Exists())
        {
            return node.Attributes;
        }

        return Storage[node].Attributes;
    }

    protected override NodeTimeInfo GetTimeInfo(NodeBase node)
    {
        return Storage[node].TimeInfo;
    }

    protected override NodeBase GetChild(string name, DirectoryNode parent)
    {
        return Storage[parent].Children.FirstOrDefault(child => child.Name == name);
    }

    protected override IEnumerable<NodeBase> GetChildren(DirectoryNode parent, NodeType nodeType)
    {
        if (!parent.Exists())
        {
            return Enumerable.Empty<NodeBase>();
        }

        var children = Storage[parent].Children;
        return children;
    }

    protected override bool Exists(NodePath path, NodeType nodeType)
    {
        NodeBase node;
        PathToNodeDictionary.TryGetValue(path, out node);

        return node != null && node.NodeType == nodeType;
    }

    protected override long GetLength(NodeBase node)
    {
        if (!node.Exists())
        {
            return 0L;
        }

        return Storage[node].Length;
    }

    protected override NodeContent GetContent(NodeBase node, NodeContentParameters contentParameters)
    {
        if (!node.Exists())
        {
            return NodeContent.CreateEmptyContent();
        }

        return NodeContent.CreateDelayedWriteContent(Storage[node].Content.GetStream());
    }

    protected override DirectoryNode CreateDirectory(DirectoryNode parent, DirectoryNode child)
    {
        return createCommon(parent, child) as DirectoryNode;
    }

    protected override FileNode CreateFile(DirectoryNode parent, FileNode child)
    {
        return createCommon(parent, child) as FileNode;
    }

    protected override NodeBase Delete(NodeBase node)
    {
        if (!node.Exists())
        {
            return node;
        }

        Storage.Remove(node);
        PathToNodeDictionary.Remove(node.Path);
        Storage[node.Parent].Children.Remove(node);
        return node;
    }

    protected override NodeBase Rename(NodeBase node, string newName)
    {
        var isFile = node.NodeType == NodeType.File;
        var newNode = isFile
            ? (NodeBase) new FileNode(newName,
                node.Parent)
            : new DirectoryNode(newName, node.Parent);
        Delete(node);
        return newNode;
    }

    protected override NodeBase SaveContent(NodeBase node, NodeContent content)
    {
        if (!node.Exists())
        {
            return node;
        }

        Storage[node].Content = content;
        return node;
    }

    protected override NodeBase SetAttributes(NodeBase node, NodeAttributes attributes)
    {
        Storage[node].Attributes = attributes;
        return node;
    }

    protected override NodeBase SetTimeInfo(NodeBase node, NodeTimeInfo newTimeInfo)
    {
        Storage[node].TimeInfo = newTimeInfo;
        return node;
    }

    private void init()
    {
        if (_storage.Count == 0)
        {
            _storage.Add(Root, new FsNodeData());
            _pathToNodeDictionary.Add(Root.Path, Root);
        }
    }

    private NodeBase createCommon(DirectoryNode parent, NodeBase child)
    {
        Storage.Add(child, new FsNodeData());
        Storage[parent].Children.Add(child);
        PathToNodeDictionary.Add(child.Path, child);

        return child;
    }
}

internal class FsNodeData
{
    public FsNodeData()
    {
        Content = NodeContent.CreateEmptyContent();
        TimeInfo = new NodeTimeInfo();
        Children = new List<NodeBase>();
        Attributes = new NodeAttributes(FileAttributes.Offline);
    }

    public NodeAttributes Attributes
    {
        get;
        set;
    }

    public NodeTimeInfo TimeInfo
    {
        get;
        set;
    }

    public List<NodeBase> Children
    {
        get;
        set;
    }

    public long Length
    {
        get
        {
            return Content.GetStream().Length;
        }
    }

    public NodeContent Content { get; set; }
}

}

 //Usage:
    var provider  = new SimpleMemoryProvider();
    provider.AddRoot();
commented May 16 by dmitry.petrishin (400 points)
Also, if I use GetContentSurrogate event handler as you described a week early, such as:

//You can replace implementation of the GetContent method.
notifier.GetContentSurrogate += (sender, args) =>
{
    //analyze args.Node, args.ContentParameters and decide how to handle request
    if (args.Node.IsFile && openForWriteRequest(args))
    {
        var streamForWrite = new MemoryStream(); //or open your custom special server stream

        //Create the delayed write content. Delayed = I will save new content of the file later - when the upload of the file finished.
        args.ResultContent = NodeContent.CreateDelayedWriteContent(streamForWrite);
    }
};

The stream in SetContent method, or event handler also is empty (length is 0).
commented May 16 by renestein (2,310 points)
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Rebex.IO.FileSystem;

namespace MemoryFs
{

    public class SimpleMemoryProvider
    : ReadWriteFileSystemProvider
{
    private readonly Dictionary<NodePath, NodeBase> _pathToNodeDictionary;
    private readonly Dictionary<NodeBase, FsNodeData> _storage;

    public SimpleMemoryProvider() : this(null)
    {
    }

    public SimpleMemoryProvider(FileSystemProviderSettings settings) : base(settings)
    {
        _pathToNodeDictionary = new Dictionary<NodePath, NodeBase>();
        _storage = new Dictionary<NodeBase, FsNodeData>();
    }

    public void AddRoot()
    {
        init();
    }

    private Dictionary<NodeBase, FsNodeData> Storage
    {
        get
        {
            return _storage;
        }
    }

    private Dictionary<NodePath, NodeBase> PathToNodeDictionary
    {
        get
        {
            return _pathToNodeDictionary;
        }
    }


    protected override NodeAttributes GetAttributes(NodeBase node)
    {
        if (!node.Exists())
        {
            return node.Attributes;
        }

        return Storage[node].Attributes;
    }

    protected override NodeTimeInfo GetTimeInfo(NodeBase node)
    {
        return Storage[node].TimeInfo;
    }

    protected override NodeBase GetChild(string name, DirectoryNode parent)
    {
        return Storage[parent].Children.FirstOrDefault(child => child.Name == name);
    }

    protected override IEnumerable<NodeBase> GetChildren(DirectoryNode parent, NodeType nodeType)
    {
        if (!parent.Exists())
        {
            return Enumerable.Empty<NodeBase>();
        }

        var children = Storage[parent].Children;
        return children;
    }

    protected override bool Exists(NodePath path, NodeType nodeType)
    {
        NodeBase node;
        PathToNodeDictionary.TryGetValue(path, out node);

        return node != null && node.NodeType == nodeType;
    }

    protected override long GetLength(NodeBase node)
    {
        if (!node.Exists())
        {
            return 0L;
        }

        return Storage[node].Length;
    }

    protected override NodeContent GetContent(NodeBase node, NodeContentParameters contentParameters)
    {
        if (!node.Exists())
        {
            return NodeContent.CreateDelayedWriteContent(new MemoryStream());
        }

        return NodeContent.CreateDelayedWriteContent(Storage[node].Content.GetStream());
    }

    protected override DirectoryNode CreateDirectory(DirectoryNode parent, DirectoryNode child)
    {
        return createCommon(parent, child) as DirectoryNode;
    }

    protected override FileNode CreateFile(DirectoryNode parent, FileNode child)
    {
        return createCommon(parent, child) as FileNode;
    }

    protected override NodeBase Delete(NodeBase node)
    {
        if (!node.Exists())
        {
            return node;
        }

        Storage.Remove(node);
        PathToNodeDictionary.Remove(node.Path);
        Storage[node.Parent].Children.Remove(node);
        return node;
    }

    protected override NodeBase Rename(NodeBase node, string newName)
    {
        var isFile = node.NodeType == NodeType.File;
        var newNode = isFile
            ? (NodeBase) new FileNode(newName,
                node.Parent)
            : new DirectoryNode(newName, node.Parent);
        Delete(node);
        return newNode;
    }

    protected override NodeBase SaveContent(NodeBase node, NodeContent content)
    {
        if (!node.Exists())
        {
            return node;
        }

        var myCurrentStream = new MemoryStream();
        content.GetStream().CopyTo(myCurrentStream);
        Storage[node].Content = NodeContent.CreateDelayedWriteContent(myCurrentStream);
        return node;
    }

    protected override NodeBase SetAttributes(NodeBase node, NodeAttributes attributes)
    {
        Storage[node].Attributes = attributes;
        return node;
    }

    protected override NodeBase SetTimeInfo(NodeBase node, NodeTimeInfo newTimeInfo)
    {
        Storage[node].TimeInfo = newTimeInfo;
        return node;
    }

    private void init()
    {
        if (_storage.Count == 0)
        {
            _storage.Add(Root, new FsNodeData());
            _pathToNodeDictionary.Add(Root.Path, Root);
        }
    }

    private NodeBase createCommon(DirectoryNode parent, NodeBase child)
    {
        Storage.Add(child, new FsNodeData());
        Storage[parent].Children.Add(child);
        PathToNodeDictionary.Add(child.Path, child);

        return child;
    }
}

internal class FsNodeData
{
    public FsNodeData()
    {
        Content = NodeContent.CreateDelayedWriteContent(new MemoryStream());
        TimeInfo = new NodeTimeInfo();
        Children = new List<NodeBase>();
        Attributes = new NodeAttributes(FileAttributes.Offline);
    }

    public NodeAttributes Attributes
    {
        get;
        set;
    }

    public NodeTimeInfo TimeInfo
    {
        get;
        set;
    }

    public List<NodeBase> Children
    {
        get;
        set;
    }

    public long Length
    {
        get
        {
            return Content.GetStream().Length;
        }
    }

    public NodeContent Content { get; set; }
}
}
commented May 16 by renestein (2,310 points)
Yes, you are right, CreateEmptyContent is our internal method, sorry for confusing.
Please try the version above, it works for me.
commented May 16 by dmitry.petrishin (400 points)
Omg, the reason was to not use Stream.Null. ..

Thanks for answer a lot!
commented May 16 by renestein (2,310 points)
dmitry, you are welcome, please use this more stable and safe version.

using System.Collections.Generic;
using System.IO;
using System.Linq;
using Rebex.IO.FileSystem;

namespace MemoryFs
{

    public class SimpleMemoryProvider
    : ReadWriteFileSystemProvider
    {
        private readonly Dictionary<NodePath, NodeBase> _pathToNodeDictionary;
        private readonly Dictionary<NodeBase, FsNodeData> _storage;

        public SimpleMemoryProvider() : this(null)
        {
        }

        public SimpleMemoryProvider(FileSystemProviderSettings settings) : base(settings)
        {
            _pathToNodeDictionary = new Dictionary<NodePath, NodeBase>();
            _storage = new Dictionary<NodeBase, FsNodeData>();
        }

        public void AddRoot()
        {
            init();
        }

        private Dictionary<NodeBase, FsNodeData> Storage
        {
            get
            {
                return _storage;
            }
        }

        private Dictionary<NodePath, NodeBase> PathToNodeDictionary
        {
            get
            {
                return _pathToNodeDictionary;
            }
        }


        protected override NodeAttributes GetAttributes(NodeBase node)
        {
            if (!node.Exists())
            {
                return node.Attributes;
            }

            return Storage[node].Attributes;
        }

        protected override NodeTimeInfo GetTimeInfo(NodeBase node)
        {
            return Storage[node].TimeInfo;
        }

        protected override NodeBase GetChild(string name, DirectoryNode parent)
        {
            return Storage[parent].Children.FirstOrDefault(child => child.Name == name);
        }

        protected override IEnumerable<NodeBase> GetChildren(DirectoryNode parent, NodeType nodeType)
        {
            if (!parent.Exists())
            {
                return Enumerable.Empty<NodeBase>();
            }

            var children = Storage[parent].Children;
            return children;
        }

        protected override bool Exists(NodePath path, NodeType nodeType)
        {
            NodeBase node;
            PathToNodeDictionary.TryGetValue(path, out node);

            return node != null && node.NodeType == nodeType;
        }

        protected override long GetLength(NodeBase node)
        {
            if (!node.Exists())
            {
                return 0L;
            }

            return Storage[node].Length;
        }

        protected override NodeContent GetContent(NodeBase node, NodeContentParameters contentParameters)
        {
            if (!node.Exists())
            {
                //error
                return NodeContent.CreateDelayedWriteContent(new MemoryStream());
            }

            var retStream = new MemoryStream();
            Storage[node].Content.CopyTo(retStream);
            retStream.Position = 0;
            Storage[node].Content.Position = 0;
            return contentParameters.AccessType == NodeContentAccess.Read
                ? NodeContent.CreateReadOnlyContent(retStream)
                : NodeContent.CreateDelayedWriteContent(retStream);
        }

        protected override DirectoryNode CreateDirectory(DirectoryNode parent, DirectoryNode child)
        {
            return createCommon(parent, child) as DirectoryNode;
        }

        protected override FileNode CreateFile(DirectoryNode parent, FileNode child)
        {
            return createCommon(parent, child) as FileNode;
        }

        protected override NodeBase Delete(NodeBase node)
        {
            if (!node.Exists())
            {
                return node;
            }

            Storage.Remove(node);
            PathToNodeDictionary.Remove(node.Path);
            Storage[node.Parent].Children.Remove(node);
            return node;
        }

        protected override NodeBase Rename(NodeBase node, string newName)
        {
            var isFile = node.NodeType == NodeType.File;
            var newNode = isFile
                ? (NodeBase)new FileNode(newName,
                    node.Parent)
                : new DirectoryNode(newName, node.Parent);
            Delete(node);
            return newNode;
        }

        protected override NodeBase SaveContent(NodeBase node, NodeContent content)
        {
            if (!node.Exists())
            {
                return node;
            }

            var myCurrentStream = new MemoryStream();
            content.GetStream().CopyTo(myCurrentStream);
            myCurrentStream.Position = 0;
            Storage[node].Content = myCurrentStream;
            return node;
        }

        protected override NodeBase SetAttributes(NodeBase node, NodeAttributes attributes)
        {
            Storage[node].Attributes = attributes;
            return node;
        }

        protected override NodeBase SetTimeInfo(NodeBase node, NodeTimeInfo newTimeInfo)
        {
            Storage[node].TimeInfo = newTimeInfo;
            return node;
        }

        private void init()
        {
            if (_storage.Count == 0)
            {
                _storage.Add(Root, new FsNodeData());
                _pathToNodeDictionary.Add(Root.Path, Root);
            }
        }

        private NodeBase createCommon(DirectoryNode parent, NodeBase child)
        {
            Storage.Add(child, new FsNodeData());
            Storage[parent].Children.Add(child);
            PathToNodeDictionary.Add(child.Path, child);

            return child;
        }
    }

    internal class FsNodeData
    {
        public FsNodeData()
        {
            Content = new MemoryStream();
            TimeInfo = new NodeTimeInfo();
            Children = new List<NodeBase>();
            Attributes = new NodeAttributes(FileAttributes.Offline);
        }

        public NodeAttributes Attributes
        {
            get;
            set;
        }

        public NodeTimeInfo TimeInfo
        {
            get;
            set;
        }

        public List<NodeBase> Children
        {
            get;
            set;
        }

        public long Length
        {
            get
            {
                return Content.Length;
            }
        }

        public MemoryStream Content { get; set; }
    }
}

//Usage:
...