Possible to build a "virtual" file system?

+1 vote
asked Nov 17, 2015 by Uwe Keim (760 points)

Currently evaluating the File Server component to provide my own SFTP server, I dug through the API documentation and feel a bit lost.

Would the following be possible with your component?:

Create a file system that is completely independent of the actual file system on disk

e.g. I'm thinking about having handlers (or interface methods) for all kind of events like:

  • Get directory list
  • Get (download) files
  • Change directory
  • Upload files
  • etc.

My scenario is that I have a "file system" stored inside database tables and want users to access it via your server component.

Is this possible in some way?

Applies to: File Server, Rebex SFTP

2 Answers

0 votes
answered Aug 11 by Lukas Pokorny (85,590 points)
 
Best answer

We added a powerful and simple-to-use custom file system API to Rebex File Server 2017 R4.

For additional information and sample code, check out the Virtual file systems page.

The File Server package also includes a sample app that shows how to implement custom file system providers.

+1 vote
answered Nov 17, 2015 by Lukas Pokorny (85,590 points)
edited Aug 11 by Lukas Pokorny

Update: Rebex File Server 2017 R4 adds virtual file systems support. See the latest answer for details.


Although virtual file systems are not supported yet, Rebex File Server internals were designed with this scenario in mind and we plan to add a simple-to-use high-level API for this soon.

However, if you can't wait, you can try the Rebex File Server Beta with proof-of-concept IFilesystem interface and the related objects:

interface IFilesystem
{
    IHandle OpenDirectory(string path, string mask);
    IHandle OpenFile(string path, FileOpenMode openMode, FileAccessMode accessMode);
    void Close(IHandle handle);
    ItemInfo ReadNextItem(IHandle handle);
    int Read(IHandle handle, byte[] buffer, int offset, int count);
    void Write(IHandle handle, byte[] buffer, int offset, int count);
    long Seek(IHandle handle, FileSeekOrigin origin, long offset);
    void CreateDirectory(string path);
    void DeleteDirectory(string path);
    void DeleteFile(string path);
    ItemInfo GetItemInfo(string path);
    void SetItemInfo(IHandle handle, ItemInfo info);
    void SetItemInfo(string path, ItemInfo info);
    void Rename(string sourcePath, string targetPath);
    FilesystemInfo GetFilesystemInfo();
    string Normalize(string absolutePath);
    string ResolvePath(string path, string relativePath);
}

This is a bit more low-level that we would like, but on the other hand it makes it possible to implement a virtual file system that is completely independent of the actual file system on disk. I will mail you a download link.

To get started, try the following code. It implements a FileServerEx class that inherits from FileServer, but wraps the original file system object in a custom class that can be extended to do something else. For example, the OpenFile method can be modified to return a custom IHandle instead of calling _inner.OpenFile, and other methods that accept IHandle can be enhanced to handle both custom and built-in IHandles. Depending on your scenario, you will probably want to ignore the built-in IFilesystem implementations and write a complete custom filesystem from scratch.

   public class FileServerExt : FileServer
   {
          private class FilesystemWrapper : IFilesystem
          {
                 private readonly IFilesystem _inner;
                 public FilesystemWrapper(IFilesystem inner)
                 {
                       _inner = inner;
                 }

                 void IFilesystem.Close(IHandle handle)
                 {
                       _inner.Close(handle);
                 }

                 void IFilesystem.CreateDirectory(string path)
                 {
                       _inner.CreateDirectory(path);
                 }

                 void IFilesystem.DeleteDirectory(string path)
                 {
                       _inner.DeleteDirectory(path);
                 }

                 void IFilesystem.DeleteFile(string path)
                 {
                       _inner.DeleteFile(path);
                 }

                 FilesystemInfo IFilesystem.GetFilesystemInfo()
                 {
                       return _inner.GetFilesystemInfo();
                 }

                 ItemInfo IFilesystem.GetItemInfo(string path)
                 {
                       return _inner.GetItemInfo(path);
                 }

                 string IFilesystem.Normalize(string absolutePath)
                 {
                       return _inner.Normalize(absolutePath);
                 }

                 IHandle IFilesystem.OpenDirectory(string path, string mask)
                 {
                       return _inner.OpenDirectory(path, mask);
                 }

                 IHandle IFilesystem.OpenFile(string path, FileOpenMode openMode, FileAccessMode accessMode)
                 {
                       return _inner.OpenFile(path, openMode, accessMode);
                 }

                 int IFilesystem.Read(IHandle handle, byte[] buffer, int offset, int count)
                 {
                       return _inner.Read(handle, buffer, offset, count);
                 }

                 ItemInfo IFilesystem.ReadNextItem(IHandle handle)
                 {
                       return _inner.ReadNextItem(handle);
                 }

                 void IFilesystem.Rename(string sourcePath, string targetPath)
                 {
                       _inner.Rename(sourcePath, targetPath);
                 }

                 string IFilesystem.ResolvePath(string path, string relativePath)
                 {
                       return _inner.ResolvePath(path, relativePath);
                 }

                 long IFilesystem.Seek(IHandle handle, FileSeekOrigin origin, long offset)
                 {
                       return _inner.Seek(handle, origin, offset);
                 }

                 void IFilesystem.SetItemInfo(string path, ItemInfo info)
                 {
                       _inner.SetItemInfo(path, info);
                 }

                 void IFilesystem.SetItemInfo(IHandle handle, ItemInfo info)
                 {
                       _inner.SetItemInfo(handle, info);
                 }

                 void IFilesystem.Write(IHandle handle, byte[] buffer, int offset, int count)
                 {
                       _inner.Write(handle, buffer, offset, count);
                 }
          }

          protected override IFilesystem GetFilesystem(ServerUser user)
          {
                 IFilesystem inner = base.GetFilesystem(user);
                 IFilesystem outer = new FilesystemWrapper(inner);
                 return outer;
          }

   }

Please note that when using the FileServerExt class intead of FileServer class, user’s virtualRootPath still applies, making it possible for different users to access different parts of the custom filesystem.

Other users that want to test beta version, write us to support@rebex.net.

commented Dec 11, 2015 by Uwe Keim (760 points)
Is it possible inside my IFilesystem-derived class to somehow access information about the current connection?

E.g. from which IP address the request comes from, etc. I.e. something like the "Request" object in an HTTP web application.
commented Dec 11, 2015 by Lukas Pokorny (85,590 points)
This is not possible at the moment, but it surely looks like a must-have feature. I think the `FileServer` object's `GetFilesystem` virtual method should get a "Context" object instead of `ServerUser` that would at least contain the user and request info. I will look into this next week.

Alternatively, it should be possible to create a custom ServerUser object that inherits from FileServerUser and provides additional information about the current connection. If you created insteances of these objects in FileServer's Authentication event (see http://www.rebex.net/file-server/features/authentication.aspx#custom-authentication for details), you could copy the information about the current connection from the event arguments.
commented Jun 15, 2016 by s4ber7 (140 points)
I don't see a FilesystemException in the current beta binaries. Is there a new way to respond with "Not Supported"?
commented Jun 15, 2016 by Tomas Knopp (58,580 points)
edited Aug 3, 2016 by Lukas Pokorny
Thanks for pointing it out s4ber7! We actually forgot to make the FilesystemException public in the old beta. I have prepared a new beta that makes the FilesystemException public so that you can handle it properly in your code.
commented Aug 11 by Lukas Pokorny (85,590 points)
Rebex File Server 2017 R4 adds virtual file systems support. See the latest answer for details: http://forum.rebex.net/5789/possible-to-build-a-virtual-file-system?show=7281#a7281
...