ソースを参照

Added delay into smb closing logic

Toby Chui 2 年 前
コミット
b52540fd83
1 ファイル変更385 行追加380 行削除
  1. 385 380
      mod/filesystem/abstractions/smbfs/smbfs.go

+ 385 - 380
mod/filesystem/abstractions/smbfs/smbfs.go

@@ -1,380 +1,385 @@
-package smbfs
-
-import (
-	"fmt"
-	"io"
-	"io/fs"
-	"log"
-	"net"
-	"os"
-	"path/filepath"
-	"regexp"
-	"strings"
-	"time"
-
-	"github.com/hirochachacha/go-smb2"
-	"imuslab.com/arozos/mod/filesystem/arozfs"
-)
-
-/*
-	Server Message Block.go
-
-	This is a file abstraction that mount SMB folders onto ArozOS as virtual drive
-
-*/
-
-type ServerMessageBlockFileSystemAbstraction struct {
-	UUID       string
-	Hierarchy  string
-	root       string
-	ipaddr     string
-	user       string
-	pass       string
-	conn       *net.Conn
-	session    *smb2.Session
-	share      *smb2.Share
-	tickerChan chan bool
-}
-
-func NewServerMessageBlockFileSystemAbstraction(uuid string, hierarchy string, ipaddr string, rootShare string, username string, password string) (ServerMessageBlockFileSystemAbstraction, error) {
-	log.Println("[SMB-FS] Connecting to " + uuid + ":/ (" + ipaddr + ")")
-	nd := net.Dialer{Timeout: 10 * time.Second}
-	conn, err := nd.Dial("tcp", ipaddr)
-	if err != nil {
-		log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
-		return ServerMessageBlockFileSystemAbstraction{}, err
-	}
-
-	d := &smb2.Dialer{
-		Initiator: &smb2.NTLMInitiator{
-			User:     username,
-			Password: password,
-		},
-	}
-
-	s, err := d.Dial(conn)
-	if err != nil {
-		log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
-		return ServerMessageBlockFileSystemAbstraction{}, err
-	}
-
-	//Mound remote storage
-	fs, err := s.Mount(rootShare)
-	if err != nil {
-		log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
-		return ServerMessageBlockFileSystemAbstraction{}, err
-	}
-
-	done := make(chan bool)
-	fsAbstraction := ServerMessageBlockFileSystemAbstraction{
-		UUID:       uuid,
-		Hierarchy:  hierarchy,
-		root:       rootShare,
-		ipaddr:     ipaddr,
-		user:       username,
-		pass:       password,
-		conn:       &conn,
-		session:    s,
-		share:      fs,
-		tickerChan: done,
-	}
-
-	return fsAbstraction, nil
-}
-
-func (a ServerMessageBlockFileSystemAbstraction) Chmod(filename string, mode os.FileMode) error {
-	filename = filterFilepath(filename)
-	filename = toWinPath(filename)
-	return a.share.Chmod(filename, mode)
-}
-func (a ServerMessageBlockFileSystemAbstraction) Chown(filename string, uid int, gid int) error {
-	return arozfs.ErrOperationNotSupported
-}
-func (a ServerMessageBlockFileSystemAbstraction) Chtimes(filename string, atime time.Time, mtime time.Time) error {
-	filename = filterFilepath(filename)
-	filename = toWinPath(filename)
-	return a.share.Chtimes(filename, atime, mtime)
-}
-func (a ServerMessageBlockFileSystemAbstraction) Create(filename string) (arozfs.File, error) {
-	filename = filterFilepath(filename)
-	f, err := a.share.Create(filename)
-	if err != nil {
-		return nil, err
-	}
-	af := NewSmbFsFile(f)
-	return af, nil
-}
-func (a ServerMessageBlockFileSystemAbstraction) Mkdir(filename string, mode os.FileMode) error {
-	filename = filterFilepath(filename)
-	filename = toWinPath(filename)
-	return a.share.Mkdir(filename, mode)
-}
-func (a ServerMessageBlockFileSystemAbstraction) MkdirAll(filename string, mode os.FileMode) error {
-	filename = filterFilepath(filename)
-	filename = toWinPath(filename)
-	return a.share.MkdirAll(filename, mode)
-}
-func (a ServerMessageBlockFileSystemAbstraction) Name() string {
-	return ""
-}
-func (a ServerMessageBlockFileSystemAbstraction) Open(filename string) (arozfs.File, error) {
-	filename = toWinPath(filterFilepath(filename))
-	f, err := a.share.Open(filename)
-	if err != nil {
-		return nil, err
-	}
-	af := NewSmbFsFile(f)
-	return af, nil
-}
-func (a ServerMessageBlockFileSystemAbstraction) OpenFile(filename string, flag int, perm os.FileMode) (arozfs.File, error) {
-	filename = toWinPath(filterFilepath(filename))
-	f, err := a.share.OpenFile(filename, flag, perm)
-	if err != nil {
-		return nil, err
-	}
-	af := NewSmbFsFile(f)
-	return af, nil
-}
-func (a ServerMessageBlockFileSystemAbstraction) Remove(filename string) error {
-	filename = filterFilepath(filename)
-	filename = toWinPath(filename)
-	return a.share.Remove(filename)
-}
-func (a ServerMessageBlockFileSystemAbstraction) RemoveAll(filename string) error {
-	filename = filterFilepath(filename)
-	filename = toWinPath(filename)
-	return a.share.RemoveAll(filename)
-}
-func (a ServerMessageBlockFileSystemAbstraction) Rename(oldname, newname string) error {
-	oldname = toWinPath(filterFilepath(oldname))
-	newname = toWinPath(filterFilepath(newname))
-	return a.share.Rename(oldname, newname)
-}
-func (a ServerMessageBlockFileSystemAbstraction) Stat(filename string) (os.FileInfo, error) {
-	filename = toWinPath(filterFilepath(filename))
-	return a.share.Stat(filename)
-}
-func (a ServerMessageBlockFileSystemAbstraction) Close() error {
-	//Stop connection checker
-	a.tickerChan <- true
-
-	//Unmount the smb folder
-	a.share.Umount()
-	a.session.Logoff()
-	conn := *(a.conn)
-	conn.Close()
-
-	return nil
-}
-
-/*
-	Abstraction Utilities
-*/
-
-func (a ServerMessageBlockFileSystemAbstraction) VirtualPathToRealPath(subpath string, username string) (string, error) {
-	if strings.HasPrefix(subpath, a.UUID+":") {
-		//This is full virtual path. Trim the uuid and correct the subpath
-		subpath = strings.TrimPrefix(subpath, a.UUID+":")
-	}
-	subpath = filterFilepath(subpath)
-
-	if a.Hierarchy == "user" {
-		return toWinPath(filepath.ToSlash(filepath.Clean(filepath.Join("users", username, subpath)))), nil
-	} else if a.Hierarchy == "public" {
-		return toWinPath(filepath.ToSlash(filepath.Clean(subpath))), nil
-	}
-
-	return "", arozfs.ErrVpathResolveFailed
-}
-
-func (a ServerMessageBlockFileSystemAbstraction) RealPathToVirtualPath(fullpath string, username string) (string, error) {
-	fullpath = filterFilepath(fullpath)
-	fullpath = strings.TrimPrefix(fullpath, "\\")
-	vpath := a.UUID + ":/" + strings.ReplaceAll(fullpath, "\\", "/")
-	return vpath, nil
-}
-
-func (a ServerMessageBlockFileSystemAbstraction) FileExists(realpath string) bool {
-	realpath = toWinPath(filterFilepath(realpath))
-	f, err := a.share.Open(realpath)
-	if err != nil {
-		return false
-	}
-	f.Close()
-	return true
-}
-
-func (a ServerMessageBlockFileSystemAbstraction) IsDir(realpath string) bool {
-	realpath = filterFilepath(realpath)
-	realpath = toWinPath(realpath)
-	stx, err := a.share.Stat(realpath)
-	if err != nil {
-		return false
-	}
-	return stx.IsDir()
-}
-
-func (a ServerMessageBlockFileSystemAbstraction) Glob(realpathWildcard string) ([]string, error) {
-	realpathWildcard = strings.ReplaceAll(realpathWildcard, "[", "?")
-	realpathWildcard = strings.ReplaceAll(realpathWildcard, "]", "?")
-	matches, err := a.share.Glob(realpathWildcard)
-	if err != nil {
-		return []string{}, err
-	}
-	return matches, nil
-}
-
-func (a ServerMessageBlockFileSystemAbstraction) GetFileSize(realpath string) int64 {
-	realpath = toWinPath(filterFilepath(realpath))
-	stat, err := a.share.Stat(realpath)
-	if err != nil {
-		return 0
-	}
-	return stat.Size()
-}
-
-func (a ServerMessageBlockFileSystemAbstraction) GetModTime(realpath string) (int64, error) {
-	realpath = toWinPath(filterFilepath(realpath))
-	stat, err := a.share.Stat(realpath)
-	if err != nil {
-		return 0, nil
-	}
-	return stat.ModTime().Unix(), nil
-}
-
-func (a ServerMessageBlockFileSystemAbstraction) WriteFile(filename string, content []byte, mode os.FileMode) error {
-	filename = toWinPath(filterFilepath(filename))
-	return a.share.WriteFile(filename, content, mode)
-}
-func (a ServerMessageBlockFileSystemAbstraction) ReadFile(filename string) ([]byte, error) {
-	filename = toWinPath(filterFilepath(filename))
-	return a.share.ReadFile(filename)
-}
-
-func (a ServerMessageBlockFileSystemAbstraction) ReadDir(filename string) ([]fs.DirEntry, error) {
-	filename = toWinPath(filterFilepath(filename))
-	fis, err := a.share.ReadDir(filename)
-	if err != nil {
-		return []fs.DirEntry{}, err
-	}
-	dirEntires := []fs.DirEntry{}
-	for _, fi := range fis {
-		if fi.Name() == "System Volume Information" || fi.Name() == "$RECYCLE.BIN" || fi.Name() == "$MFT" {
-			//System folders. Hide it
-			continue
-		}
-		dirEntires = append(dirEntires, newDirEntryFromFileInfo(fi))
-	}
-	return dirEntires, nil
-}
-
-func (a ServerMessageBlockFileSystemAbstraction) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
-	filename = toWinPath(filterFilepath(filename))
-	f, err := a.share.OpenFile(filename, os.O_CREATE|os.O_WRONLY, mode)
-	if err != nil {
-		return err
-	}
-
-	p := make([]byte, 32768)
-	for {
-		_, err := stream.Read(p)
-		if err != nil {
-			if err == io.EOF {
-				break
-			} else {
-				return err
-			}
-		}
-		_, err = f.Write(p)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-func (a ServerMessageBlockFileSystemAbstraction) ReadStream(filename string) (io.ReadCloser, error) {
-	filename = toWinPath(filterFilepath(filename))
-	f, err := a.share.OpenFile(filename, os.O_RDONLY, 0755)
-	if err != nil {
-		return nil, err
-	}
-	return f, nil
-}
-
-//Note that walk on SMB is super slow. Avoid using this if possible.
-func (a ServerMessageBlockFileSystemAbstraction) Walk(root string, walkFn filepath.WalkFunc) error {
-	root = toWinPath(filterFilepath(root))
-	err := fs.WalkDir(a.share.DirFS(root), ".", func(path string, d fs.DirEntry, err error) error {
-		if err != nil {
-			return err
-		}
-		statInfo, err := d.Info()
-		if err != nil {
-			return err
-		}
-		walkFn(filepath.ToSlash(filepath.Join(root, path)), statInfo, err)
-		return nil
-	})
-	return err
-}
-
-func (a ServerMessageBlockFileSystemAbstraction) Heartbeat() error {
-	_, err := a.share.Stat("")
-	return err
-}
-
-/*
-
-	Optional Functions
-
-*/
-
-func (a *ServerMessageBlockFileSystemAbstraction) CapacityInfo() {
-	fsinfo, err := a.share.Statfs(".")
-	if err != nil {
-		return
-	}
-
-	fmt.Println(fsinfo)
-}
-
-/*
-
-	Helper Functions
-
-*/
-
-func toWinPath(filename string) string {
-	backslashed := strings.ReplaceAll(filename, "/", "\\")
-	return strings.TrimPrefix(backslashed, "\\")
-
-}
-
-func filterFilepath(rawpath string) string {
-	rawpath = filepath.ToSlash(filepath.Clean(rawpath))
-	rawpath = strings.TrimSpace(rawpath)
-
-	if strings.HasPrefix(rawpath, "./") {
-		return rawpath[1:]
-	} else if rawpath == "." || rawpath == "" {
-		return "/"
-	}
-
-	return rawpath
-}
-
-func wildCardToRegexp(pattern string) string {
-	var result strings.Builder
-	for i, literal := range strings.Split(pattern, "*") {
-		// Replace * with .*
-		if i > 0 {
-			result.WriteString(".*")
-		}
-
-		// Quote any regular expression meta characters in the
-		// literal text.
-		result.WriteString(regexp.QuoteMeta(literal))
-	}
-	return result.String()
-}
+package smbfs
+
+import (
+	"fmt"
+	"io"
+	"io/fs"
+	"log"
+	"net"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+	"time"
+
+	"github.com/hirochachacha/go-smb2"
+	"imuslab.com/arozos/mod/filesystem/arozfs"
+)
+
+/*
+	Server Message Block.go
+
+	This is a file abstraction that mount SMB folders onto ArozOS as virtual drive
+
+*/
+
+type ServerMessageBlockFileSystemAbstraction struct {
+	UUID       string
+	Hierarchy  string
+	root       string
+	ipaddr     string
+	user       string
+	pass       string
+	conn       *net.Conn
+	session    *smb2.Session
+	share      *smb2.Share
+	tickerChan chan bool
+}
+
+func NewServerMessageBlockFileSystemAbstraction(uuid string, hierarchy string, ipaddr string, rootShare string, username string, password string) (ServerMessageBlockFileSystemAbstraction, error) {
+	log.Println("[SMB-FS] Connecting to " + uuid + ":/ (" + ipaddr + ")")
+	nd := net.Dialer{Timeout: 10 * time.Second}
+	conn, err := nd.Dial("tcp", ipaddr)
+	if err != nil {
+		log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
+		return ServerMessageBlockFileSystemAbstraction{}, err
+	}
+
+	d := &smb2.Dialer{
+		Initiator: &smb2.NTLMInitiator{
+			User:     username,
+			Password: password,
+		},
+	}
+
+	s, err := d.Dial(conn)
+	if err != nil {
+		log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
+		return ServerMessageBlockFileSystemAbstraction{}, err
+	}
+
+	//Mound remote storage
+	fs, err := s.Mount(rootShare)
+	if err != nil {
+		log.Println("[SMB-FS] Unable to connect to remote: ", err.Error())
+		return ServerMessageBlockFileSystemAbstraction{}, err
+	}
+
+	done := make(chan bool)
+	fsAbstraction := ServerMessageBlockFileSystemAbstraction{
+		UUID:       uuid,
+		Hierarchy:  hierarchy,
+		root:       rootShare,
+		ipaddr:     ipaddr,
+		user:       username,
+		pass:       password,
+		conn:       &conn,
+		session:    s,
+		share:      fs,
+		tickerChan: done,
+	}
+
+	return fsAbstraction, nil
+}
+
+func (a ServerMessageBlockFileSystemAbstraction) Chmod(filename string, mode os.FileMode) error {
+	filename = filterFilepath(filename)
+	filename = toWinPath(filename)
+	return a.share.Chmod(filename, mode)
+}
+func (a ServerMessageBlockFileSystemAbstraction) Chown(filename string, uid int, gid int) error {
+	return arozfs.ErrOperationNotSupported
+}
+func (a ServerMessageBlockFileSystemAbstraction) Chtimes(filename string, atime time.Time, mtime time.Time) error {
+	filename = filterFilepath(filename)
+	filename = toWinPath(filename)
+	return a.share.Chtimes(filename, atime, mtime)
+}
+func (a ServerMessageBlockFileSystemAbstraction) Create(filename string) (arozfs.File, error) {
+	filename = filterFilepath(filename)
+	f, err := a.share.Create(filename)
+	if err != nil {
+		return nil, err
+	}
+	af := NewSmbFsFile(f)
+	return af, nil
+}
+func (a ServerMessageBlockFileSystemAbstraction) Mkdir(filename string, mode os.FileMode) error {
+	filename = filterFilepath(filename)
+	filename = toWinPath(filename)
+	return a.share.Mkdir(filename, mode)
+}
+func (a ServerMessageBlockFileSystemAbstraction) MkdirAll(filename string, mode os.FileMode) error {
+	filename = filterFilepath(filename)
+	filename = toWinPath(filename)
+	return a.share.MkdirAll(filename, mode)
+}
+func (a ServerMessageBlockFileSystemAbstraction) Name() string {
+	return ""
+}
+func (a ServerMessageBlockFileSystemAbstraction) Open(filename string) (arozfs.File, error) {
+	filename = toWinPath(filterFilepath(filename))
+	f, err := a.share.Open(filename)
+	if err != nil {
+		return nil, err
+	}
+	af := NewSmbFsFile(f)
+	return af, nil
+}
+func (a ServerMessageBlockFileSystemAbstraction) OpenFile(filename string, flag int, perm os.FileMode) (arozfs.File, error) {
+	filename = toWinPath(filterFilepath(filename))
+	f, err := a.share.OpenFile(filename, flag, perm)
+	if err != nil {
+		return nil, err
+	}
+	af := NewSmbFsFile(f)
+	return af, nil
+}
+func (a ServerMessageBlockFileSystemAbstraction) Remove(filename string) error {
+	filename = filterFilepath(filename)
+	filename = toWinPath(filename)
+	return a.share.Remove(filename)
+}
+func (a ServerMessageBlockFileSystemAbstraction) RemoveAll(filename string) error {
+	filename = filterFilepath(filename)
+	filename = toWinPath(filename)
+	return a.share.RemoveAll(filename)
+}
+func (a ServerMessageBlockFileSystemAbstraction) Rename(oldname, newname string) error {
+	oldname = toWinPath(filterFilepath(oldname))
+	newname = toWinPath(filterFilepath(newname))
+	return a.share.Rename(oldname, newname)
+}
+func (a ServerMessageBlockFileSystemAbstraction) Stat(filename string) (os.FileInfo, error) {
+	filename = toWinPath(filterFilepath(filename))
+	return a.share.Stat(filename)
+}
+func (a ServerMessageBlockFileSystemAbstraction) Close() error {
+	//Stop connection checker
+	go func() {
+		a.tickerChan <- true
+	}()
+
+	//Unmount the smb folder
+	time.Sleep(300 * time.Millisecond)
+	a.share.Umount()
+	time.Sleep(300 * time.Millisecond)
+	a.session.Logoff()
+	time.Sleep(300 * time.Millisecond)
+	conn := *(a.conn)
+	conn.Close()
+
+	return nil
+}
+
+/*
+	Abstraction Utilities
+*/
+
+func (a ServerMessageBlockFileSystemAbstraction) VirtualPathToRealPath(subpath string, username string) (string, error) {
+	if strings.HasPrefix(subpath, a.UUID+":") {
+		//This is full virtual path. Trim the uuid and correct the subpath
+		subpath = strings.TrimPrefix(subpath, a.UUID+":")
+	}
+	subpath = filterFilepath(subpath)
+
+	if a.Hierarchy == "user" {
+		return toWinPath(filepath.ToSlash(filepath.Clean(filepath.Join("users", username, subpath)))), nil
+	} else if a.Hierarchy == "public" {
+		return toWinPath(filepath.ToSlash(filepath.Clean(subpath))), nil
+	}
+
+	return "", arozfs.ErrVpathResolveFailed
+}
+
+func (a ServerMessageBlockFileSystemAbstraction) RealPathToVirtualPath(fullpath string, username string) (string, error) {
+	fullpath = filterFilepath(fullpath)
+	fullpath = strings.TrimPrefix(fullpath, "\\")
+	vpath := a.UUID + ":/" + strings.ReplaceAll(fullpath, "\\", "/")
+	return vpath, nil
+}
+
+func (a ServerMessageBlockFileSystemAbstraction) FileExists(realpath string) bool {
+	realpath = toWinPath(filterFilepath(realpath))
+	f, err := a.share.Open(realpath)
+	if err != nil {
+		return false
+	}
+	f.Close()
+	return true
+}
+
+func (a ServerMessageBlockFileSystemAbstraction) IsDir(realpath string) bool {
+	realpath = filterFilepath(realpath)
+	realpath = toWinPath(realpath)
+	stx, err := a.share.Stat(realpath)
+	if err != nil {
+		return false
+	}
+	return stx.IsDir()
+}
+
+func (a ServerMessageBlockFileSystemAbstraction) Glob(realpathWildcard string) ([]string, error) {
+	realpathWildcard = strings.ReplaceAll(realpathWildcard, "[", "?")
+	realpathWildcard = strings.ReplaceAll(realpathWildcard, "]", "?")
+	matches, err := a.share.Glob(realpathWildcard)
+	if err != nil {
+		return []string{}, err
+	}
+	return matches, nil
+}
+
+func (a ServerMessageBlockFileSystemAbstraction) GetFileSize(realpath string) int64 {
+	realpath = toWinPath(filterFilepath(realpath))
+	stat, err := a.share.Stat(realpath)
+	if err != nil {
+		return 0
+	}
+	return stat.Size()
+}
+
+func (a ServerMessageBlockFileSystemAbstraction) GetModTime(realpath string) (int64, error) {
+	realpath = toWinPath(filterFilepath(realpath))
+	stat, err := a.share.Stat(realpath)
+	if err != nil {
+		return 0, nil
+	}
+	return stat.ModTime().Unix(), nil
+}
+
+func (a ServerMessageBlockFileSystemAbstraction) WriteFile(filename string, content []byte, mode os.FileMode) error {
+	filename = toWinPath(filterFilepath(filename))
+	return a.share.WriteFile(filename, content, mode)
+}
+func (a ServerMessageBlockFileSystemAbstraction) ReadFile(filename string) ([]byte, error) {
+	filename = toWinPath(filterFilepath(filename))
+	return a.share.ReadFile(filename)
+}
+
+func (a ServerMessageBlockFileSystemAbstraction) ReadDir(filename string) ([]fs.DirEntry, error) {
+	filename = toWinPath(filterFilepath(filename))
+	fis, err := a.share.ReadDir(filename)
+	if err != nil {
+		return []fs.DirEntry{}, err
+	}
+	dirEntires := []fs.DirEntry{}
+	for _, fi := range fis {
+		if fi.Name() == "System Volume Information" || fi.Name() == "$RECYCLE.BIN" || fi.Name() == "$MFT" {
+			//System folders. Hide it
+			continue
+		}
+		dirEntires = append(dirEntires, newDirEntryFromFileInfo(fi))
+	}
+	return dirEntires, nil
+}
+
+func (a ServerMessageBlockFileSystemAbstraction) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
+	filename = toWinPath(filterFilepath(filename))
+	f, err := a.share.OpenFile(filename, os.O_CREATE|os.O_WRONLY, mode)
+	if err != nil {
+		return err
+	}
+
+	p := make([]byte, 32768)
+	for {
+		_, err := stream.Read(p)
+		if err != nil {
+			if err == io.EOF {
+				break
+			} else {
+				return err
+			}
+		}
+		_, err = f.Write(p)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+func (a ServerMessageBlockFileSystemAbstraction) ReadStream(filename string) (io.ReadCloser, error) {
+	filename = toWinPath(filterFilepath(filename))
+	f, err := a.share.OpenFile(filename, os.O_RDONLY, 0755)
+	if err != nil {
+		return nil, err
+	}
+	return f, nil
+}
+
+//Note that walk on SMB is super slow. Avoid using this if possible.
+func (a ServerMessageBlockFileSystemAbstraction) Walk(root string, walkFn filepath.WalkFunc) error {
+	root = toWinPath(filterFilepath(root))
+	err := fs.WalkDir(a.share.DirFS(root), ".", func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			return err
+		}
+		statInfo, err := d.Info()
+		if err != nil {
+			return err
+		}
+		walkFn(filepath.ToSlash(filepath.Join(root, path)), statInfo, err)
+		return nil
+	})
+	return err
+}
+
+func (a ServerMessageBlockFileSystemAbstraction) Heartbeat() error {
+	_, err := a.share.Stat("")
+	return err
+}
+
+/*
+
+	Optional Functions
+
+*/
+
+func (a *ServerMessageBlockFileSystemAbstraction) CapacityInfo() {
+	fsinfo, err := a.share.Statfs(".")
+	if err != nil {
+		return
+	}
+
+	fmt.Println(fsinfo)
+}
+
+/*
+
+	Helper Functions
+
+*/
+
+func toWinPath(filename string) string {
+	backslashed := strings.ReplaceAll(filename, "/", "\\")
+	return strings.TrimPrefix(backslashed, "\\")
+
+}
+
+func filterFilepath(rawpath string) string {
+	rawpath = filepath.ToSlash(filepath.Clean(rawpath))
+	rawpath = strings.TrimSpace(rawpath)
+
+	if strings.HasPrefix(rawpath, "./") {
+		return rawpath[1:]
+	} else if rawpath == "." || rawpath == "" {
+		return "/"
+	}
+
+	return rawpath
+}
+
+func wildCardToRegexp(pattern string) string {
+	var result strings.Builder
+	for i, literal := range strings.Split(pattern, "*") {
+		// Replace * with .*
+		if i > 0 {
+			result.WriteString(".*")
+		}
+
+		// Quote any regular expression meta characters in the
+		// literal text.
+		result.WriteString(regexp.QuoteMeta(literal))
+	}
+	return result.String()
+}