├── Imagery └── Snarf_by_stanlydan.png ├── .gitmodules ├── Application ├── App.config ├── Nfs │ ├── NFSException.cs │ ├── FileSystem │ │ ├── FileSystemInfo.cs │ │ ├── Enums.cs │ │ ├── FileHandle.cs │ │ ├── NfsIO.cs │ │ ├── NfsFileAttributes.cs │ │ └── NfsDirectory.cs │ ├── NfsListenerManager.cs │ ├── NfsHandlerManager.cs │ ├── NfsPath.cs │ ├── Enums.cs │ ├── HandleManager.cs │ ├── MountManager.cs │ ├── NfsPacket.cs │ ├── NfsTime.cs │ ├── BaseHandler.cs │ ├── NfsHandler.cs │ ├── MountHandler.cs │ └── PortmapHandler.cs ├── Program.cs ├── Extensions.cs ├── Udp │ ├── DatagramPacket.cs │ ├── UdpPacket.cs │ ├── UdpHandler.cs │ └── UdpListener.cs ├── Properties │ └── AssemblyInfo.cs ├── Snarf.sln └── Snarf.csproj ├── COPYRIGHTS.md ├── LICENSE ├── README.md └── .gitignore /Imagery/Snarf_by_stanlydan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellscape/Snarf/HEAD/Imagery/Snarf_by_stanlydan.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "External/Shellscape.Common"] 2 | path = External/Shellscape.Common 3 | url = git@github.com:shellscape/Shellscape.Common.git 4 | -------------------------------------------------------------------------------- /Application/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Application/Nfs/NFSException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Snarf.Nfs { 4 | public class NFSException : Exception { 5 | 6 | internal NFSException(uint xId, uint errorNumber) { 7 | XID = xId; 8 | ErrorNumber = errorNumber; 9 | } 10 | 11 | public uint XID { get; private set; } 12 | public uint ErrorNumber { get; private set; } 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /COPYRIGHTS.md: -------------------------------------------------------------------------------- 1 | Copyright Info 2 | ============== 3 | 4 | **Snarf_by_stanlydan.png** 5 | 6 | Derivative work based on 'Snarf' by DanielMead 7 | 8 | * Original URL - http://danielmead.deviantart.com/art/Snarf-95875488 9 | * Author's Website - http://danielmead.deviantart.com/ 10 | 11 | I attempted to obtain permission to use the original piece but could not get a definitive 'yes' or 'no' answer. Should the author request removal, I'll comply immediately. -------------------------------------------------------------------------------- /Application/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using Snarf.Udp; 8 | using Snarf.Nfs; 9 | 10 | namespace Snarf { 11 | class Program { 12 | static void Main(string[] args) { 13 | 14 | MountManager.Init(); 15 | HandleManager.Init(); 16 | 17 | var nfs = new NfsHandler(); 18 | var mount = new MountHandler(); 19 | var portmap = new PortmapHandler(); 20 | 21 | nfs.Start(); 22 | mount.Start(); 23 | portmap.Start(); 24 | 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Application/Nfs/FileSystem/FileSystemInfo.cs: -------------------------------------------------------------------------------- 1 | // this class is used to encapsulate all of the file system information. The 2 | // idea is that it be replaced by native code to get better NFS behavior. 3 | namespace Snarf.Nfs.FileSystem { 4 | 5 | /// 6 | /// Used to encapsulate all of the file system information. The idea is that it be replaced by native code to get better NFS behavior. 7 | /// 8 | public class FileSystemInfo { 9 | 10 | internal FileSystemInfo() { 11 | } 12 | 13 | internal virtual void SetFS(string path) { 14 | } 15 | 16 | internal virtual uint TransferSize { get { return 8192; } } 17 | internal virtual uint BlockSize { get { return 512; } } 18 | internal virtual uint TotalBlocks { get { return 0; } } 19 | internal virtual uint FreeBlocks { get { return 0; } } 20 | internal virtual uint AvailableBlocks { get { return 0; } } 21 | } 22 | } -------------------------------------------------------------------------------- /Application/Nfs/NfsListenerManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using Snarf.Udp; 8 | 9 | namespace Snarf.Nfs { 10 | 11 | public static class NfsListenerManager { 12 | 13 | private static List> _cache; 14 | 15 | static NfsListenerManager() { 16 | _cache = new List>(); 17 | } 18 | 19 | public static UdpListener GetListener(int port, int programId) { 20 | var tuple = _cache.Where(o => o.Item1 == port).FirstOrDefault(); 21 | UdpListener listener = null; 22 | 23 | if (tuple == null) { 24 | listener = new UdpListener(port, false); 25 | listener.Start(); 26 | 27 | _cache.Add(new Tuple(port, programId, listener)); 28 | } 29 | else { 30 | listener = tuple.Item3; 31 | } 32 | return listener; 33 | } 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Application/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Snarf { 9 | public static class Extensions { 10 | 11 | public static Boolean Is(this FileAttributes attributes, FileAttributes value) { 12 | return ((attributes & value) == value); 13 | } 14 | 15 | public static void BubbleSort(this IList list) { 16 | BubbleSort(list, Comparer.Default); 17 | } 18 | 19 | private static void BubbleSort(IList list, IComparer comparer) { 20 | bool stillGoing = true; 21 | while (stillGoing) { 22 | stillGoing = false; 23 | for (int i = 0; i < list.Count - 1; i++) { 24 | T x = list[i]; 25 | T y = list[i + 1]; 26 | if (comparer.Compare(x, y) > 0) { 27 | list[i] = y; 28 | list[i + 1] = x; 29 | stillGoing = true; 30 | } 31 | } 32 | } 33 | } 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Application/Udp/DatagramPacket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Net; 6 | 7 | namespace Snarf.Udp { 8 | public class DatagramPacket { 9 | private byte[] _bytes; 10 | private int _length; 11 | private IPEndPoint _endPoint; 12 | 13 | public DatagramPacket(byte[] bytes, int length, IPEndPoint endPoint) : this(bytes, length) { 14 | _endPoint = endPoint; 15 | } 16 | 17 | public DatagramPacket(byte[] bytes, int length) { 18 | _length = length; 19 | _bytes = new byte[_length]; 20 | Buffer.BlockCopy(bytes, 0, _bytes, 0, _length); 21 | } 22 | 23 | public byte[] Bytes { 24 | get { return _bytes; } 25 | set { 26 | _bytes = value; 27 | _length = value.Length; 28 | } 29 | } 30 | 31 | public int Length { 32 | get { return _length; } 33 | } 34 | 35 | public IPEndPoint ServerEndPoint { 36 | get { return _endPoint; } 37 | set { _endPoint = value; } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Application/Nfs/NfsHandlerManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Snarf.Nfs { 8 | public static class NfsHandlerManager { 9 | 10 | private static List _cache; 11 | 12 | static NfsHandlerManager() { 13 | _cache = new List(); 14 | } 15 | 16 | public static void RegisterHandler(BaseHandler handler) { 17 | if (_cache.Contains(handler)) { 18 | throw new Exception("Handler has already been registered"); 19 | } 20 | _cache.Add(handler); 21 | } 22 | 23 | public static int GetPort(int programId) { 24 | var handler = _cache.Where(o => o.ProgramID == programId).FirstOrDefault(); 25 | 26 | if (handler != null) { 27 | return handler.Port; 28 | } 29 | return 0; 30 | } 31 | 32 | public static Boolean IsProgramRegistered(int programId) { 33 | var handler = _cache.Where(o => o.ProgramID == programId).FirstOrDefault(); 34 | return handler != null; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2011 Andrew Powell, Shellscape Software 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Application/Nfs/NfsPath.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Snarf.Nfs { 9 | public static class NfsPath { 10 | 11 | public static String ToWin(string unixPath) { 12 | 13 | String[] parts = unixPath.Split(new char[] { Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); 14 | StringBuilder result = new StringBuilder(); 15 | 16 | foreach (var part in parts) { 17 | result.Append(part); 18 | if (part == parts[0] && !part.Contains(Path.VolumeSeparatorChar)) { 19 | result.Append(Path.VolumeSeparatorChar); 20 | } 21 | result.Append(Path.DirectorySeparatorChar); 22 | } 23 | 24 | return result.ToString(); 25 | } 26 | 27 | public static String ToUnix(string windowsPath) { 28 | 29 | String[] parts = windowsPath.Split(new char[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); 30 | StringBuilder result = new StringBuilder(); 31 | 32 | foreach (var part in parts) { 33 | result.Append(part); 34 | if (part == parts[0] && !part.Contains(Path.VolumeSeparatorChar)) { 35 | result.Append(Path.VolumeSeparatorChar); 36 | } 37 | result.Append(Path.AltDirectorySeparatorChar); 38 | } 39 | 40 | return result.ToString(); 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Snarf 2 | ===== 3 | 4 | Snarf is an NFS v2 server implementation written in C# with .NET 4.5. 5 | 6 | Snarf is based in part on the JNFS project by Steven Procter @ http://void.org/~steven/jnfs/. 7 | 8 | **Current Status** 9 | 10 | 11/18/2012 11 | 12 | - Mounts now list files and directories. 13 | - ~~Mounts can't cd into subdirectories for some reason. Still looking into this one.~~ Mounts can cd into subdirectories and ls. 14 | - File handles and mounts are now cached in file, server can be restarted without creating mount problems. 15 | - Opening files on the client works. 16 | - Saving on the client system doesn't work. 17 | 18 | 11/16/2012 19 | 20 | - First commit/push. The server accepts version 2 connections and mounts. 21 | - Mounts only list files and not directories. 22 | - Mounts cannot persist between server restarts. 23 | - Exports are not hooked up. If the client specifies a path that is valid, it is allowed. This is temporary. 24 | 25 | **But, Why?** 26 | 27 | I started writing this for my XBMC-AppleTV2 setup. After upgrading to Windows 8 on the machine my movies are connected to, SMB started to get flaky. Very flaky. I gave HaneWin NFS a shot (and a few others, FreeNFS and winnfsd) and wasn't satisfied with them. 28 | 29 | **Down the Road** 30 | 31 | - Implement the v3 spec over TCP. 32 | 33 | **Licensing** 34 | 35 | Unless otherwise stated, the code here is licensed under the MIT license. Have at it! -------------------------------------------------------------------------------- /Application/Nfs/FileSystem/Enums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Snarf.Nfs.FileSystem { 8 | 9 | public enum FileType : int { 10 | NFNON = 0, 11 | NFREG = 1, 12 | NFDIR = 2, 13 | NFBLK = 3, 14 | NFCHR = 4, 15 | NFLNK = 5 16 | } 17 | 18 | public enum FilePermissions : uint { 19 | UPDIR = 0040000, // This is a directory 20 | UPCHRS = 0020000, // This is a character special file 21 | UPBLKS = 0060000, // This is a block special file 22 | UPFILE = 0100000, // This is a regular file 23 | UPSLINK = 0120000, // This is a symbolic link file 24 | UPSOCK = 0140000, // This is a named socket 25 | UPSUID = 0004000, // Set user id on execution. 26 | UPSGID = 0002000, // Set group id on execution. 27 | UPSTICKY = 0001000, // Save swapped text even after use. 28 | UP_OREAD = 0000400, // Read permission for owner. 29 | UP_OWRITE = 0000200, // Write permission for owner. 30 | UP_OEXEC = 0000100, // Execute and search permission owner. 31 | UP_GREAD = 0000040, // Read permission for group. 32 | UP_GWRITE = 0000020, // Write permission for group. 33 | UP_GEXEC = 0000010, // Execute and search permission group. 34 | UP_WREAD = 0000004, // Read permission for world. 35 | UP_WWRITE = 0000002, // Write permission for world. 36 | UP_WEXEC = 0000001 // Execute and search permission world. 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Application/Nfs/Enums.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Snarf.Nfs { 8 | 9 | public enum RpcSignalType : int { 10 | Call = 0, 11 | Reply = 1 12 | } 13 | 14 | public enum RpcMessageResult : int { 15 | Accepted = 0, 16 | Denied = 0 17 | } 18 | 19 | public enum RpcProcedure : int { 20 | Success = 0, 21 | ProgramUnavailable = 1, 22 | ProgramMismatch = 2, 23 | ProcedureUnavail = 3, 24 | GarbageArguments = 4 25 | } 26 | 27 | public enum RpcAuthFlavor : int { 28 | NULL = 0, 29 | UNIX = 1, 30 | SHORT = 2, 31 | DES = 3 32 | }; 33 | 34 | 35 | public enum NfsProcedure : int { 36 | NULL = 0, 37 | GETATTR = 1, 38 | SETATTR = 2, 39 | ROOT = 3, 40 | LOOKUP = 4, 41 | READLINK = 5, 42 | READ = 6, 43 | WRITECACHE = 7, 44 | WRITE = 8, 45 | CREATE = 9, 46 | REMOVE = 10, 47 | RENAME = 11, 48 | LINK = 12, 49 | SYMLINK = 13, 50 | MKDIR = 14, 51 | RMDIR = 15, 52 | READDIR = 16, 53 | STATFS = 17 54 | } 55 | 56 | public enum NfsReply : int { 57 | OK = 0, 58 | ERR_PERM = 1, 59 | ERR_NOENT = 2, 60 | ERR_IO = 5, 61 | ERR_NXIO = 6, 62 | ERR_ACCES = 13, 63 | ERR_EXIST = 17, 64 | ERR_NODEV = 19, 65 | ERR_NOTDIR = 20, 66 | ERR_ISDIR = 21, 67 | ERR_FBIG = 27, 68 | ERR_NOSPC = 28, 69 | ERR_ROFS = 30, 70 | ERR_NAMETOOLONG = 63, 71 | ERR_NOTEMPTY = 66, 72 | ERR_DQUOT = 69, 73 | ERR_STALE = 70, 74 | ERR_WFLUSH = 99 75 | } 76 | 77 | public enum RpcPrototcol : int { 78 | TCP = 6, 79 | UDP = 17 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Application/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Snarf")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Shellscape Software")] 12 | [assembly: AssemblyProduct("Snarf NFS Server")] 13 | [assembly: AssemblyCopyright("Copyright © 2012 Andrew Powell, Shellscape Software")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("37d04bfb-69f6-47b9-98db-d0ad8805aaa1")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Application/Nfs/HandleManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.Serialization; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Snarf.Nfs { 10 | 11 | [DataContract(Name = "apphandles")] 12 | public class HandleManager : Shellscape.Configuration.Config { 13 | 14 | private object _lock = new object(); 15 | private List _handles = new List() { null }; 16 | 17 | [DataMember(Name="handles")] 18 | public List Handles { 19 | get { return _handles; } 20 | set { _handles = value; } 21 | } 22 | 23 | public uint GetHandle(String name) { 24 | lock (_lock) { 25 | if (_handles.Contains(name)) { 26 | uint handle = (uint)_handles.IndexOf(name); 27 | //Console.WriteLine("HandleManager.GetHandle: name: " + name + ", handle: " + handle); 28 | return handle; 29 | } 30 | var handleId = _handles.Count; 31 | _handles.Add(name); 32 | this.Save(); 33 | 34 | return (uint)handleId; 35 | } 36 | } 37 | 38 | public String GetName(uint handleId) { 39 | lock (_lock) { 40 | if (_handles.Count >= handleId) { 41 | return _handles[(int)handleId]; 42 | } 43 | throw new Exception("HandleManager.GetName: handleId not found."); 44 | } 45 | } 46 | 47 | protected override string ApplicationName { 48 | get { return Shellscape.Utilities.AssemblyMeta.AssemblyName; } 49 | } 50 | 51 | protected override void SetDefaults() { 52 | _lock = new object(); 53 | this.FileName = "app.handles"; 54 | #if DEBUG 55 | this.AppDataPath = this.StorePath = System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath); 56 | #endif 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Application/Nfs/MountManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.Serialization; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Snarf.Nfs { 10 | 11 | public class Mount { 12 | 13 | public String ClientID { get; set; } 14 | public String MountPoint { get; set; } 15 | 16 | } 17 | 18 | [DataContract(Name = "appmounts")] 19 | public class MountManager : Shellscape.Configuration.Config { 20 | 21 | private object _lock = new object(); 22 | private List _mounts = new List(); 23 | 24 | [DataMember(Name="mounts")] 25 | public List Mounts { 26 | get { return _mounts; } 27 | set { _mounts = value; } 28 | } 29 | 30 | public Boolean Add(String clientId, String mountPoint) { 31 | var mount = _mounts.Where(o => o.ClientID == clientId && o.MountPoint == mountPoint).FirstOrDefault(); 32 | if (mount != null) { 33 | return false; 34 | } 35 | _mounts.Add(new Mount() { ClientID = clientId, MountPoint = mountPoint }); 36 | this.Save(); 37 | return true; 38 | } 39 | 40 | public Boolean Remove(String clientId) { 41 | var mount = _mounts.Where(o => o.ClientID == clientId).FirstOrDefault(); 42 | if (mount != null && _mounts.Contains(mount)) { 43 | _mounts.Remove(mount); 44 | this.Save(); 45 | } 46 | else { 47 | return false; 48 | //throw new Exception("MountManager.Remove: mount(" + clientId + ") not found."); 49 | } 50 | return true; 51 | } 52 | 53 | protected override string ApplicationName { 54 | get { return Shellscape.Utilities.AssemblyMeta.AssemblyName; } 55 | } 56 | 57 | protected override void SetDefaults() { 58 | _lock = new object(); 59 | this.FileName = "app.mounts"; 60 | #if DEBUG 61 | this.AppDataPath = this.StorePath = System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath); 62 | #endif 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | x64/ 20 | *_i.c 21 | *_p.c 22 | *.ilk 23 | *.meta 24 | *.obj 25 | *.pch 26 | *.pdb 27 | *.pgc 28 | *.pgd 29 | *.rsp 30 | *.sbr 31 | *.tlb 32 | *.tli 33 | *.tlh 34 | *.tmp 35 | *.log 36 | *.vspscc 37 | *.vssscc 38 | .builds 39 | 40 | # Visual C++ cache files 41 | ipch/ 42 | *.aps 43 | *.ncb 44 | *.opensdf 45 | *.sdf 46 | 47 | # Visual Studio profiler 48 | *.psess 49 | *.vsp 50 | *.vspx 51 | 52 | # Guidance Automation Toolkit 53 | *.gpState 54 | 55 | # ReSharper is a .NET coding add-in 56 | _ReSharper* 57 | 58 | # NCrunch 59 | *.ncrunch* 60 | .*crunch*.local.xml 61 | 62 | # Installshield output folder 63 | [Ee]xpress 64 | 65 | # DocProject is a documentation generator add-in 66 | DocProject/buildhelp/ 67 | DocProject/Help/*.HxT 68 | DocProject/Help/*.HxC 69 | DocProject/Help/*.hhc 70 | DocProject/Help/*.hhk 71 | DocProject/Help/*.hhp 72 | DocProject/Help/Html2 73 | DocProject/Help/html 74 | 75 | # Click-Once directory 76 | publish 77 | 78 | # Publish Web Output 79 | *.Publish.xml 80 | 81 | # NuGet Packages Directory 82 | packages 83 | 84 | # Windows Azure Build Output 85 | csx 86 | *.build.csdef 87 | 88 | # Windows Store app package directory 89 | AppPackages/ 90 | 91 | # Others 92 | [Bb]in 93 | [Oo]bj 94 | sql 95 | TestResults 96 | [Tt]est[Rr]esult* 97 | *.Cache 98 | ClientBin 99 | [Ss]tyle[Cc]op.* 100 | ~$* 101 | *.dbmdl 102 | Generated_Code #added for RIA/Silverlight projects 103 | 104 | # Backup & report files from converting an old project file to a newer 105 | # Visual Studio version. Backup files are not needed, because we have git ;-) 106 | _UpgradeReport_Files/ 107 | Backup*/ 108 | UpgradeLog*.XML 109 | -------------------------------------------------------------------------------- /Application/Nfs/NfsPacket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | using Snarf.Udp; 8 | 9 | namespace Snarf.Nfs { 10 | 11 | public class NfsPacket : UdpPacket { 12 | 13 | public NfsPacket(DatagramPacket datagram) : base(datagram) { } 14 | 15 | public NfsPacket(int size) : base(size) { } 16 | 17 | public uint XID { get; set; } 18 | public uint RpcVersion { get; set; } 19 | public uint ProgramID { get; set; } 20 | public uint NfsVersion { get; set; } 21 | public uint ProcedureID { get; set; } 22 | 23 | public String RemoteHost { get; set; } 24 | 25 | // struct auth_unix - RFC 1057- page 12 26 | //private uint AuthStamp { get; set; } 27 | //public String AuthMachineName { get; set; } 28 | //public uint AuthClientID { get; set; } 29 | 30 | public virtual void AddReplyHeader(uint xid) { 31 | SetUInt(xid); 32 | SetUInt((uint)RpcSignalType.Reply); 33 | SetUInt((uint)RpcMessageResult.Accepted); 34 | AddNullAuthentication(); 35 | SetUInt((uint)RpcProcedure.Success); 36 | } 37 | 38 | public virtual void AddNullAuthentication() { 39 | SetUInt(0); // the type 40 | SetUInt(0); // the length 41 | } 42 | 43 | public virtual void ReadAuthentication() { 44 | uint type = GetUInt(); 45 | uint length = GetUInt(); 46 | //int startPos = this.Position; // know where we started before we got the info we need, so we can skip ahead. 47 | 48 | //if (type == (uint)RpcAuthFlavor.UNIX) { 49 | // AuthStamp = GetUInt(); 50 | // AuthMachineName = GetString(); 51 | // AuthClientID = GetUInt(); 52 | 53 | // // skip the gids portion 54 | // int advance = (int)length - (this.Position - startPos); 55 | // if (advance > 0) { 56 | // Advance((uint)advance); 57 | // } 58 | //} 59 | //else { 60 | Advance(length); 61 | //} 62 | 63 | //struct auth_unix { 64 | // unsigned int stamp; 65 | // string machinename<255>; 66 | // unsigned int uid; 67 | // unsigned int gid; 68 | // unsigned int gids<16>; 69 | //} 70 | } 71 | 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Application/Snarf.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Snarf", "Snarf.csproj", "{A74404F2-5664-4C75-BB06-0D1BB0292B60}" 5 | EndProject 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{24F6E643-0126-4DA9-B2C2-4C7B5983EE9D}" 7 | ProjectSection(SolutionItems) = preProject 8 | ..\COPYRIGHTS.md = ..\COPYRIGHTS.md 9 | ..\LICENSE = ..\LICENSE 10 | ..\README.md = ..\README.md 11 | EndProjectSection 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shellscape.Common", "..\External\Shellscape.Common\Shellscape.Common\Shellscape.Common.csproj", "{F7F1F64D-F6CD-42A9-B167-546E4EA31463}" 14 | EndProject 15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Windows API", "..\External\Shellscape.Common\Microsoft\Windows API\Windows API.csproj", "{B20A1853-BDC5-47D5-9369-3999BBD7F516}" 16 | EndProject 17 | Global 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Release|Any CPU = Release|Any CPU 21 | EndGlobalSection 22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 23 | {A74404F2-5664-4C75-BB06-0D1BB0292B60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {A74404F2-5664-4C75-BB06-0D1BB0292B60}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {A74404F2-5664-4C75-BB06-0D1BB0292B60}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {A74404F2-5664-4C75-BB06-0D1BB0292B60}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {F7F1F64D-F6CD-42A9-B167-546E4EA31463}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {F7F1F64D-F6CD-42A9-B167-546E4EA31463}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {F7F1F64D-F6CD-42A9-B167-546E4EA31463}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {F7F1F64D-F6CD-42A9-B167-546E4EA31463}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {B20A1853-BDC5-47D5-9369-3999BBD7F516}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {B20A1853-BDC5-47D5-9369-3999BBD7F516}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {B20A1853-BDC5-47D5-9369-3999BBD7F516}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {B20A1853-BDC5-47D5-9369-3999BBD7F516}.Release|Any CPU.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(SolutionProperties) = preSolution 37 | HideSolutionNode = FALSE 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /Application/Nfs/FileSystem/FileHandle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | // 8 | // The FileHandle is used by NFS to represent files. It is a 32 byte piece of data that the NFS client gets from mountd or the NFS 9 | // server. 10 | // 11 | // This implementation stores 3 things in the FileHandle: the handle of the root of this file system (the handle of the mount point), 12 | // the handle of the file, and a flag indicating whether this handle is read only. The rest of the 32 bytes are not used, but since 13 | // NFS clients think two files are the same iff the FileHandles are the same, make sure this always gives out the same FileHandle by 14 | // setting the rest of the data to 0. 15 | 16 | 17 | namespace Snarf.Nfs.FileSystem { 18 | public class FileHandle { 19 | 20 | internal uint root; // handle of the root of this mount point 21 | internal uint handle; // handle of the file 22 | internal uint @readonly; // is the mount read only? 23 | 24 | public FileHandle() { 25 | } 26 | 27 | // Initialize this FileHandle from the packet, leave the position in the packet just past the FileHandle. 28 | public FileHandle(NfsPacket source) { 29 | Read(source); 30 | } 31 | 32 | public virtual uint Root { get { return root; } } 33 | 34 | public virtual uint Handle { get { return handle; } } 35 | 36 | public virtual uint ReadOnly { get { return @readonly; } } 37 | 38 | internal virtual bool Read(NfsPacket packet) { 39 | 40 | root = packet.GetUInt(); // the first long in the packet is the handle of the root 41 | handle = packet.GetUInt(); // the next long is the handle of the file 42 | @readonly = packet.GetUInt(); // the next is a read only flag 43 | 44 | // The rest is nothing. There are 32 bytes in a FileHandle and this has read in 3 words, or 12 bytes. 45 | packet.Advance(32 - 3 * 4); 46 | 47 | return true; 48 | } 49 | 50 | public Boolean Set(uint root, uint handle, uint readOnly) { 51 | this.root = root; 52 | this.handle = handle; 53 | this.@readonly = readOnly; 54 | 55 | return true; 56 | } 57 | 58 | public Boolean Emit(ref NfsPacket packet) { 59 | packet.SetUInt(root); 60 | packet.SetUInt(handle); 61 | packet.SetUInt(@readonly); 62 | 63 | // The rest of the words of the handle should be 0. Since there are 32 bytes in a handle, 64 | // there are 8 words and the above consumed 3 of them, so there are 5 left. 65 | for (int i = 0; i < 5; i++) { 66 | packet.SetUInt(0); 67 | } 68 | 69 | return true; 70 | } 71 | 72 | //internal virtual void Print() { 73 | // Console.WriteLine("FileHandle: root: " + root + ", handle: " + handle + ", readonly: " + @readonly); 74 | //} 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Application/Nfs/NfsTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Snarf.Nfs { 4 | public class NfsTime { 5 | 6 | private uint _seconds; 7 | private uint _milliseconds; 8 | private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 9 | 10 | public NfsTime(NfsPacket packet) { 11 | Read(packet); 12 | } 13 | 14 | public NfsTime(uint seconds, uint milliseconds) { 15 | Set(seconds, milliseconds); 16 | } 17 | 18 | public NfsTime(DateTime dateTime) { 19 | Set(GetSeconds(dateTime), GetMilliseconds(dateTime)); 20 | } 21 | 22 | public NfsTime(uint time) { 23 | // convert a 64 bit uint into two 32 bit uints to pass to the regular set function 24 | uint top = time >> 16; 25 | uint bottom = time & 0xffff; 26 | 27 | bottom += (top & 0xffff) << 16; 28 | top = top >> 16; 29 | 30 | Set(top, bottom); 31 | } 32 | 33 | internal virtual bool Read(NfsPacket packet) { 34 | _seconds = packet.GetUInt(); 35 | _milliseconds = packet.GetUInt(); 36 | return true; 37 | } 38 | 39 | internal virtual bool Emit(ref NfsPacket packet) { 40 | packet.SetUInt(_seconds); 41 | packet.SetUInt(_milliseconds); 42 | return true; 43 | } 44 | 45 | internal virtual bool Set(uint seconds, uint milliseconds) { 46 | _seconds = seconds; 47 | _milliseconds = milliseconds; 48 | return true; 49 | } 50 | 51 | internal virtual void Print() { 52 | Console.Write("Timeval: sec=" + _seconds + " msec=" + _milliseconds + "\n"); 53 | } 54 | 55 | //public static uint GetSeconds(uint localTime) { 56 | // uint startTime = 27111902 << 32 + 54590 << 16 + 32768; 57 | // uint delta = localTime - startTime; 58 | // delta /= 1000; 59 | // return delta; 60 | //} 61 | 62 | //public static uint GetMilliSeconds(uint localTime) { 63 | // uint startTime = 27111902 << 32 + 54590 << 16 + 32768; 64 | // uint delta = localTime - startTime; 65 | // delta %= 1000; 66 | // return delta; 67 | //} 68 | 69 | //public static uint GetSeconds(uint localTime) { 70 | // return localTime / (1 << 32); 71 | //} 72 | 73 | //public static uint GetMilliSeconds(uint localTime) { 74 | // return localTime % (1 << 32); 75 | //} 76 | 77 | 78 | /// 79 | /// Returns the UNIX Timestamp value 80 | /// 81 | /// 82 | /// 83 | public static uint GetMilliseconds(DateTime dateTime) { 84 | return (uint)(dateTime.ToUniversalTime() - UnixEpoch).Milliseconds; 85 | } 86 | 87 | /// 88 | /// Returns the UNIX Timestamp value 89 | /// 90 | /// 91 | /// 92 | public static uint GetSeconds(DateTime dateTime) { 93 | return (uint)(dateTime.ToUniversalTime() - UnixEpoch).TotalSeconds; 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /Application/Nfs/BaseHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | using Snarf.Udp; 9 | 10 | namespace Snarf.Nfs { 11 | public class BaseHandler : UdpHandler { 12 | 13 | public BaseHandler(int port, int programId) : base() { 14 | 15 | this.ProgramID = programId; 16 | 17 | NfsHandlerManager.RegisterHandler(this); 18 | 19 | _listener = NfsListenerManager.GetListener(port, programId); 20 | _listener.PacketReceived += OnPacketReceived; 21 | } 22 | 23 | public int ProgramID { get; private set; } 24 | 25 | protected virtual void Process(NfsPacket packet, IPEndPoint receivedFrom) { 26 | //Console.WriteLine("NfsHandler.Process : recievedFrom: " + receivedFrom.ToString()); 27 | } 28 | 29 | protected override void OnPacketReceived(byte[] bytes, IPEndPoint receivedFrom) { 30 | var datagram = new DatagramPacket(bytes, bytes.Length, receivedFrom); 31 | var packet = new NfsPacket(datagram); 32 | var e = new UdpPacketReceivedEventArgs(packet, receivedFrom); 33 | 34 | //Console.WriteLine("\nPacket Received : EndPoint: " + _listener.Client.Client.LocalEndPoint.ToString()); 35 | 36 | uint xId = packet.XID = packet.GetUInt(); 37 | uint type = packet.GetUInt(); 38 | 39 | if (type == (int)RpcSignalType.Call) { 40 | Call(ref packet, xId); 41 | } 42 | else if (type == (int)RpcSignalType.Reply) { 43 | Reply(ref packet, xId); 44 | } 45 | 46 | //RaisePacketReceived(e); 47 | 48 | if (packet.ProgramID == this.ProgramID) { 49 | //Console.WriteLine("Found program: " + packet.ProgramID); 50 | Process(packet, receivedFrom); 51 | } 52 | } 53 | 54 | protected virtual void Call(ref NfsPacket packet, uint xId) { 55 | uint rpcVersion = packet.RpcVersion = packet.GetUInt(); 56 | uint programId = packet.ProgramID = packet.GetUInt(); 57 | uint version = packet.NfsVersion = packet.GetUInt(); 58 | uint procedure = packet.ProcedureID = packet.GetUInt(); 59 | 60 | IPHostEntry entry = Dns.GetHostEntry(packet.Source); 61 | String[] parts = entry.HostName.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries); 62 | 63 | if (parts.Length >= 0) { 64 | packet.RemoteHost = parts[0]; 65 | } 66 | 67 | //if (programId == this.ProgramID) { 68 | // Console.WriteLine("\nCall: rpcVersion = " + rpcVersion + " programId = " + programId + " version = " + version + " procedure = " + procedure); 69 | //} 70 | } 71 | 72 | protected virtual void Reply(ref NfsPacket packet, uint xId) { 73 | //Console.WriteLine("Reply: I don't handle these"); 74 | } 75 | 76 | protected void SendNull(NfsPacket sourcePacket, IPEndPoint receivedFrom) { 77 | 78 | NfsPacket packet = new NfsPacket(128); 79 | 80 | packet.AddReplyHeader(sourcePacket.XID); 81 | 82 | Send(packet, receivedFrom); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Application/Nfs/NfsHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | using Snarf.Udp; 9 | 10 | namespace Snarf.Nfs { 11 | public class NfsHandler : BaseHandler { 12 | 13 | public const int NFS_PORT = 2049; 14 | public const int NFS_VERSION = 2; 15 | public const int NFS_PROGRAMID = 100003; 16 | 17 | public const int NFS_TRUE = 1; 18 | public const int NFS_FALSE = 0; 19 | 20 | private FileSystem.NfsDirectory _directory = null; 21 | private FileSystem.NfsIO _io = null; 22 | 23 | public NfsHandler() : base(NfsHandler.NFS_PORT, NfsHandler.NFS_PROGRAMID) { 24 | // read directory support 25 | _directory = new FileSystem.NfsDirectory(new FileSystem.FileSystemInfo()); 26 | 27 | // read and write 28 | _io = new FileSystem.NfsIO(); 29 | 30 | } 31 | 32 | protected override void Process(NfsPacket packet, IPEndPoint receivedFrom) { 33 | //Console.WriteLine("NfsHandler.Process : recievedFrom: " + receivedFrom.ToString()); 34 | //Console.WriteLine("NfsHandler.Process: Procedure -> " + packet.ProcedureID + ":" + ((NfsProcedure)packet.ProcedureID).ToString()); 35 | 36 | // get rid of authentication recorde in packet, we don't use them 37 | packet.ReadAuthentication(); 38 | packet.ReadAuthentication(); 39 | 40 | if (packet.ProcedureID == (int)NfsProcedure.NULL) { 41 | base.SendNull(packet, receivedFrom); 42 | return; 43 | } 44 | 45 | NfsPacket result; 46 | uint xid = packet.XID; 47 | 48 | try { 49 | switch (packet.ProcedureID) { 50 | case (int)NfsProcedure.GETATTR: 51 | result = _directory.GetAttr(packet); 52 | break; 53 | case (int)NfsProcedure.SETATTR: 54 | result = _directory.SetAttr(xid, packet); 55 | break; 56 | case (int)NfsProcedure.LOOKUP: 57 | result = _directory.Lookup(packet); 58 | break; 59 | case (int)NfsProcedure.READ: 60 | result = _io.Read(packet); 61 | break; 62 | case (int)NfsProcedure.WRITE: 63 | result = _io.Write(xid, packet); 64 | break; 65 | case (int)NfsProcedure.CREATE: 66 | result = _directory.Create(xid, packet); 67 | break; 68 | case (int)NfsProcedure.REMOVE: 69 | result = _directory.Remove(xid, packet); 70 | break; 71 | case (int)NfsProcedure.RENAME: 72 | result = _directory.Rename(xid, packet); 73 | break; 74 | case (int)NfsProcedure.MKDIR: 75 | result = _directory.Mkdir(xid, packet); 76 | break; 77 | case (int)NfsProcedure.RMDIR: 78 | result = _directory.Rmdir(xid, packet); 79 | break; 80 | case (int)NfsProcedure.READDIR: 81 | result = _directory.ReadDirectory(packet); 82 | break; 83 | case (int)NfsProcedure.STATFS: 84 | result = _directory.StatFS(packet); 85 | break; 86 | default: 87 | Console.Error.WriteLine("Unsupported NFS procedure called (" + packet.ProcedureID + ") from " + receivedFrom.ToString() + "\n"); 88 | throw new NFSException(packet.XID, (uint)NfsReply.ERR_IO); 89 | } 90 | } 91 | catch (NFSException e) { 92 | // make a reply packet that includes the error 93 | result = new NfsPacket(64); 94 | result.AddReplyHeader(packet.XID); 95 | result.SetUInt(e.ErrorNumber); 96 | } 97 | 98 | Send(result, receivedFrom); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Application/Nfs/FileSystem/NfsIO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Snarf.Nfs.FileSystem { 5 | 6 | public class NfsIO { 7 | 8 | internal NfsIO() { } 9 | 10 | public NfsPacket Write(uint xid, NfsPacket packet) { 11 | string fileName = null; 12 | 13 | try { 14 | // read info out of packet 15 | FileHandle fh = new FileHandle(packet); 16 | uint beginoffset = packet.GetUInt(); 17 | uint offset = packet.GetUInt(); 18 | uint totalcount = packet.GetUInt(); 19 | // next comes the data which is a uint of size and the bytes. 20 | uint datalen = packet.GetUInt(); 21 | uint packetOffset = (uint)packet.Position; 22 | packet.Advance(datalen); 23 | 24 | // carry out the write operation 25 | fileName = HandleManager.Current.GetName(fh.Handle); 26 | if (fileName == null) { 27 | throw new NFSException(xid, (uint)NfsReply.ERR_STALE); 28 | } 29 | // XXX comment out print lines to improve performance 30 | // System.out.print("Write(" + fileName + ", " + offset + ", " + 31 | // datalen + ")\n"); 32 | using (StreamWriter sw = new StreamWriter(fileName)) { 33 | sw.BaseStream.Seek(offset, SeekOrigin.Begin); 34 | sw.BaseStream.Write(packet.Data, (int)packetOffset, (int)datalen); 35 | } 36 | 37 | // load in new file attributes 38 | NfsFileAttributes fa = new NfsFileAttributes(); 39 | fa.Load(fileName); 40 | 41 | // create the reply packet 42 | NfsPacket result = new NfsPacket(128); 43 | result.Reset(); 44 | result.AddReplyHeader(xid); 45 | result.SetUInt((uint)NfsReply.OK); 46 | fa.Emit(ref result); 47 | 48 | return result; 49 | } 50 | catch (IOException) { 51 | throw new NFSException(xid, (uint)NfsReply.ERR_IO); 52 | } 53 | catch (System.Security.SecurityException) { 54 | throw new NFSException(xid, (uint)NfsReply.ERR_PERM); 55 | } 56 | } 57 | 58 | public NfsPacket Read(NfsPacket packet) { 59 | try { 60 | FileHandle fh = new FileHandle(packet); 61 | uint offset = packet.GetUInt(); 62 | uint count = packet.GetUInt(); 63 | uint totalCount = packet.GetUInt(); // not used 64 | uint xId = packet.XID; 65 | int numberRead; 66 | byte[] readbuf; 67 | String filePath = HandleManager.Current.GetName(fh.Handle); 68 | 69 | if (filePath == null) { 70 | throw new NFSException(xId, (uint)NfsReply.ERR_STALE); 71 | } 72 | 73 | if (count <= 0) { 74 | Console.Error.WriteLine("\tNfsIO.Read: invalid value for count " + count); 75 | throw new NFSException(xId, (uint)NfsReply.ERR_IO); 76 | } 77 | 78 | using (StreamReader sr = new StreamReader(filePath)) { 79 | sr.BaseStream.Seek(offset, SeekOrigin.Begin); 80 | readbuf = new byte[(int)count]; 81 | numberRead = sr.BaseStream.Read(readbuf, 0, (int)count); 82 | } 83 | 84 | if (numberRead < 0) { 85 | Console.Error.WriteLine("\tNfsIO.Read: number read is " + numberRead); 86 | numberRead = 0; 87 | } 88 | 89 | NfsFileAttributes attributes = new NfsFileAttributes(filePath); 90 | NfsPacket reply = new NfsPacket(128 + numberRead); 91 | 92 | reply.AddReplyHeader(xId); 93 | reply.SetUInt((uint)NfsReply.OK); 94 | 95 | attributes.Emit(ref reply); 96 | 97 | reply.SetData(numberRead, readbuf); 98 | 99 | return reply; 100 | } 101 | catch (FileNotFoundException) { 102 | throw new NFSException(packet.XID, (uint)NfsReply.ERR_NOENT); 103 | } 104 | catch (IOException) { 105 | throw new NFSException(packet.XID, (uint)NfsReply.ERR_IO); 106 | } 107 | } 108 | } 109 | 110 | } -------------------------------------------------------------------------------- /Application/Nfs/MountHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | using Snarf.Udp; 10 | 11 | namespace Snarf.Nfs { 12 | 13 | public class MountHandler : BaseHandler { 14 | 15 | public const int MOUNT_PORT = 2049; // mountd doesn't have assigned port 16 | public const int MOUNT_VERSION = 1; 17 | public const int MOUNT_PROGRAMID = 100005; 18 | 19 | public enum MountProcedure : int { 20 | NULL = 0, 21 | MNT = 1, 22 | UMNT = 3 23 | } 24 | 25 | public MountHandler() : base(MOUNT_PORT, MOUNT_PROGRAMID) { // system will set the port 26 | 27 | } 28 | 29 | protected override void Process(NfsPacket packet, IPEndPoint receivedFrom) { 30 | //Console.WriteLine("MountHandler.Process : recievedFrom: " + receivedFrom.ToString()); 31 | //Console.WriteLine("MountHandler.Process: Procedure -> " + packet.ProcedureID + ":" + ((MountProcedure)packet.ProcedureID).ToString()); 32 | 33 | switch (packet.ProcedureID) { 34 | case (int)MountProcedure.NULL: 35 | base.SendNull(packet, receivedFrom); 36 | break; 37 | case (int)MountProcedure.MNT: 38 | Mount(packet, receivedFrom); 39 | break; 40 | case (int)MountProcedure.UMNT: 41 | Unmount(packet, receivedFrom); 42 | break; 43 | default: 44 | break; 45 | 46 | } 47 | } 48 | 49 | private void Mount(NfsPacket sourcePacket, IPEndPoint receivedFrom) { 50 | 51 | NfsReply replyCode = NfsReply.OK; 52 | NfsPacket packet = new NfsPacket(128); 53 | 54 | packet.AddReplyHeader(sourcePacket.XID); 55 | 56 | // skip past the authentication records 57 | sourcePacket.ReadAuthentication(); 58 | sourcePacket.ReadAuthentication(); 59 | 60 | // next should be a dirpath, which is a string. Replace unix style path with local style path 61 | String path = sourcePacket.GetString(); 62 | 63 | if (path == null) { 64 | replyCode = NfsReply.ERR_STALE; 65 | } 66 | else { 67 | String original = path.Clone() as String; 68 | 69 | path = NfsPath.ToWin(path); 70 | 71 | //Console.WriteLine("MountHandler.Mount : requested: " + original + ", actual: " + path); 72 | 73 | //if (!Directory.Exists(path)) { 74 | // replyCode = NfsReply.ERR_EXIST; 75 | //} 76 | } 77 | 78 | // Try to validate this mount, if there is an error make an error packet, otherwise send back the handle. 79 | 80 | if (replyCode != NfsReply.OK) { 81 | packet.SetUInt((uint)replyCode); 82 | 83 | } 84 | else if (false){ //exports.Matches(packet.Source(), path) == false) { 85 | // No permission for this mount in the exports file 86 | //result.AddLong(NFS.NFSERR_PERM); 87 | //Console.Error.WriteLine("!!! Mount request for " + path + "from " + packet.Source() + " denied.\n"); 88 | } 89 | else { 90 | // put together a file handle 91 | uint handle = HandleManager.Current.GetHandle(path); 92 | var fileHandle = new FileSystem.FileHandle(); 93 | 94 | fileHandle.Set(handle, (uint)handle, 0); 95 | 96 | packet.SetUInt((uint)replyCode); 97 | 98 | fileHandle.Emit(ref packet); 99 | } 100 | 101 | if (replyCode == NfsReply.OK) { 102 | MountManager.Current.Add(sourcePacket.RemoteHost, path); 103 | } 104 | 105 | Send(packet, receivedFrom); 106 | } 107 | 108 | private void Unmount(NfsPacket sourcePacket, IPEndPoint receivedFrom) { 109 | 110 | // skip past the authentication records 111 | sourcePacket.ReadAuthentication(); 112 | sourcePacket.ReadAuthentication(); 113 | 114 | String path = sourcePacket.GetString(); 115 | NfsPacket packet = new NfsPacket(128); 116 | 117 | path = NfsPath.ToWin(path); 118 | 119 | HandleManager.Current.GetHandle(path); 120 | 121 | packet.AddReplyHeader(sourcePacket.XID); 122 | packet.SetUInt((uint)NfsReply.OK); 123 | 124 | //Console.WriteLine("MountHandler.Unmount : requested: " + path); 125 | 126 | MountManager.Current.Remove(sourcePacket.RemoteHost); 127 | 128 | Send(packet, receivedFrom); 129 | } 130 | 131 | 132 | 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /Application/Udp/UdpPacket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | 5 | namespace Snarf.Udp { 6 | 7 | public class UdpPacket { 8 | 9 | private byte[] _data; 10 | private int _length; 11 | private int _position; 12 | 13 | // store information about where the packet came from 14 | private IPAddress _source; 15 | private int _port; 16 | 17 | public UdpPacket(DatagramPacket datagram) { 18 | _data = datagram.Bytes; 19 | _length = datagram.Length; 20 | _position = 0; 21 | _source = datagram.ServerEndPoint == null ? null : datagram.ServerEndPoint.Address; 22 | _port = datagram.ServerEndPoint == null ? 0 : datagram.ServerEndPoint.Port; 23 | 24 | //Console.Write("source: " + _source); 25 | //Console.Write(" port: " + _port); 26 | //Console.Write(" data length: " + _length + "\n"); 27 | } 28 | 29 | public UdpPacket(int size) { 30 | _length = size; 31 | _data = new byte[_length]; 32 | _position = 0; 33 | } 34 | 35 | public byte[] Data { get { return _data; } } 36 | 37 | /// 38 | /// Returns the length of the data written to the buffer. This is sometimes different than Data.Length. 39 | /// 40 | public int Length { get { return _position; } } 41 | public int Position { get { return _position; } } 42 | public IPAddress Source { get { return _source; } } 43 | public int Port { get { return _port; } } 44 | 45 | #region . Get Methods . 46 | 47 | public uint GetUInt() { 48 | 49 | uint one = _data[_position]; 50 | one <<= 24; 51 | uint two = _data[_position + 1]; 52 | two <<= 16; 53 | uint three = _data[_position + 2]; 54 | three <<= 8; 55 | uint four = _data[_position + 3]; 56 | 57 | uint result = one | two | three | four; 58 | 59 | _position += 4; 60 | 61 | return result; 62 | } 63 | 64 | public long GetLong() { 65 | 66 | long one = _data[_position]; 67 | one <<= 24; 68 | long two = _data[_position + 1]; 69 | two <<= 16; 70 | long three = _data[_position + 2]; 71 | three <<= 8; 72 | long four = _data[_position + 3]; 73 | 74 | long result = one | two | three | four; 75 | 76 | _position += 4; 77 | 78 | return result; 79 | } 80 | 81 | public char GetChar() { 82 | char value = (char)_data[_position]; 83 | 84 | ++_position; 85 | 86 | return value; 87 | } 88 | 89 | public String GetString() { 90 | uint length = GetUInt(); 91 | 92 | String value = ""; 93 | 94 | for (int charCount = 0; charCount < length; ++charCount) { 95 | value += GetChar(); 96 | } 97 | 98 | if (length % 4 != 0) { 99 | _position += (int)(4 - length % 4); 100 | } 101 | 102 | return value; 103 | } 104 | 105 | public virtual long GetData(byte[] buffer) { 106 | uint plen = GetUInt(); // how much data is in the packet 107 | if (plen + _position >= _data.Length) { 108 | Console.Error.WriteLine("GetData: packet is too small\n"); 109 | return -1; 110 | } 111 | 112 | // try to copy the data into the buffer 113 | Array.Copy(_data, _position, buffer, 0, (int)plen); 114 | 115 | Advance(plen); 116 | return plen; 117 | } 118 | 119 | #endregion 120 | 121 | #region . Set Methods . 122 | 123 | public void Set(int value) { 124 | SetUInt((uint)value); 125 | } 126 | 127 | public void SetUInt(uint value) { 128 | Need(4); 129 | 130 | uint one = value; 131 | uint two = value; 132 | two >>= 8; 133 | uint three = value; 134 | three >>= 16; 135 | uint four = value; 136 | four >>= 24; 137 | 138 | _data[_position] = (byte)four; 139 | _data[_position + 1] = (byte)three; 140 | _data[_position + 2] = (byte)two; 141 | _data[_position + 3] = (byte)one; 142 | 143 | _position += 4; 144 | } 145 | 146 | public void Set(String value) { 147 | 148 | Need(4 + value.Length + 3); // Extra in case of pad 149 | 150 | SetUInt((uint)value.Length); 151 | 152 | foreach (Char c in value) { 153 | _data[_position] = (byte)c; 154 | ++_position; 155 | } 156 | 157 | if (value.Length % 4 != 0) { 158 | for (int pad = 4 - value.Length % 4; pad != 0; --pad) { 159 | _data[_position] = 0; 160 | ++_position; 161 | } 162 | } 163 | } 164 | 165 | private void Need(int need) { 166 | while (_position + need >= _data.Length) { 167 | Byte[] newData = new Byte[_data.Length * 2]; 168 | _data.CopyTo(newData, 0); 169 | 170 | _data = newData; 171 | } 172 | } 173 | 174 | public virtual long SetData(int len, byte[] data) { 175 | SetUInt((uint)len); 176 | Array.Copy(data, 0, _data, _position, len); 177 | 178 | Advance((uint)len); 179 | return 0; 180 | } 181 | 182 | public virtual long SetData(byte[] toadd) { 183 | return SetData(toadd.Length, toadd); 184 | } 185 | 186 | #endregion 187 | 188 | // Add the standard procedure you requested was called reply header 189 | public virtual void Advance(uint length) { 190 | uint words = (length + 3) / 4; 191 | uint delta = 4 * words; 192 | _position += (int)delta; 193 | } 194 | 195 | public virtual void Reset() { 196 | _position = 0; 197 | } 198 | 199 | } 200 | } -------------------------------------------------------------------------------- /Application/Udp/UdpHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Globalization; 4 | using System.Net; 5 | using System.Text; 6 | using System.Threading; 7 | 8 | namespace Snarf.Udp { 9 | 10 | public enum UdpReceiverState { 11 | Stopped, 12 | Stopping, 13 | Starting, 14 | Started 15 | } 16 | 17 | public class UdpPacketReceivedEventArgs : EventArgs { 18 | 19 | public UdpPacketReceivedEventArgs(UdpPacket packet, IPEndPoint receivedFrom) { 20 | Packet = packet; 21 | ReceivedFrom = receivedFrom; 22 | } 23 | 24 | public UdpPacket Packet { get; private set; } 25 | public IPEndPoint ReceivedFrom { get; private set; } 26 | } 27 | 28 | public class UdpHandler : IDisposable { 29 | 30 | public event EventHandler PacketReceived = null; 31 | 32 | private Thread _thread = null; 33 | private bool _disposed = false; 34 | protected UdpListener _listener = null; 35 | private long _runState = (long)UdpReceiverState.Stopped; 36 | protected Guid _uniqueId = Guid.Empty; 37 | 38 | public UdpHandler() { } 39 | 40 | public UdpHandler(int port) { 41 | 42 | _uniqueId = Guid.NewGuid(); 43 | 44 | _listener = new UdpListener(port, false); 45 | _listener.PacketReceived += OnPacketReceived; 46 | } 47 | 48 | ~UdpHandler() { 49 | this.Dispose(false); 50 | } 51 | 52 | public int Port { get { return _listener.Port; } } 53 | 54 | public virtual void Start() { 55 | if (this._thread == null || this._thread.ThreadState == ThreadState.Stopped) { 56 | this._thread = new Thread(new ThreadStart(this.ThreadStart)); 57 | this._thread.Name = String.Format(CultureInfo.InvariantCulture, "UdpHandler:{0}", _uniqueId); 58 | } 59 | else if (this._thread.ThreadState == ThreadState.Running) { 60 | //throw new ThreadStateException("The request handling process is already running."); 61 | return; // allow 62 | } 63 | 64 | if (this._thread.ThreadState != ThreadState.Unstarted) { 65 | throw new ThreadStateException("The request handling process was not properly initialized so it could not be started."); 66 | } 67 | this._thread.Start(); 68 | 69 | long waitTime = DateTime.Now.Ticks + TimeSpan.TicksPerSecond * 10; 70 | while (_runState != (long)UdpReceiverState.Started) { 71 | Thread.Sleep(100); 72 | if (DateTime.Now.Ticks > waitTime) { 73 | throw new TimeoutException("Unable to start the request handling process."); 74 | } 75 | } 76 | } 77 | 78 | public virtual void Stop() { 79 | Interlocked.Exchange(ref this._runState, (long)UdpReceiverState.Stopping); 80 | if (_runState == (long)UdpReceiverState.Starting) { 81 | this._listener.Stop(); 82 | } 83 | long waitTime = DateTime.Now.Ticks + TimeSpan.TicksPerSecond * 10; 84 | while (_runState != (long)UdpReceiverState.Stopped) { 85 | Thread.Sleep(100); 86 | if (DateTime.Now.Ticks > waitTime) { 87 | throw new TimeoutException("Unable to stop the web server process."); 88 | } 89 | } 90 | 91 | this._thread = null; 92 | } 93 | 94 | private void ThreadStart() { 95 | Interlocked.Exchange(ref this._runState, (long)UdpReceiverState.Starting); 96 | try { 97 | if (_runState != (long)UdpReceiverState.Started) { 98 | this._listener.Start(); 99 | Interlocked.Exchange(ref this._runState, (long)UdpReceiverState.Started); 100 | } 101 | 102 | try { 103 | while (_runState == (long)UdpReceiverState.Started) { 104 | //NfsListenerContext context = this._listener.GetContext(); 105 | //this.OnPacketReceived(context); 106 | } 107 | } 108 | catch (Exception) { 109 | // This will occur when the listener gets shut down. 110 | // Just swallow it and move on. 111 | } 112 | } 113 | finally { 114 | Interlocked.Exchange(ref this._runState, (long)UdpReceiverState.Stopped); 115 | } 116 | } 117 | 118 | protected virtual void OnPacketReceived(byte[] bytes, IPEndPoint receivedFrom) { 119 | var datagram = new DatagramPacket(bytes, bytes.Length, receivedFrom); 120 | var packet = new UdpPacket(datagram); 121 | var e = new UdpPacketReceivedEventArgs(packet, receivedFrom); 122 | RaisePacketReceived(e); 123 | } 124 | 125 | protected void RaisePacketReceived(UdpPacketReceivedEventArgs e) { 126 | try { 127 | if (this.PacketReceived != null) { 128 | this.PacketReceived.BeginInvoke(this, e, null, null); 129 | } 130 | } 131 | catch { 132 | // Swallow the exception and/or log it, but you probably don't want to exit 133 | // just because an incoming request handler failed. 134 | } 135 | } 136 | 137 | public void Send(UdpPacket packet, IPEndPoint dest) { 138 | //Console.WriteLine("Sending Data: length: " + packet.Length + " -> " + dest.ToString()); 139 | 140 | _listener.Client.Send(packet.Data, packet.Length, dest); 141 | } 142 | 143 | #region . IDisposable . 144 | 145 | public virtual void Dispose() { 146 | this.Dispose(true); 147 | GC.SuppressFinalize(this); 148 | } 149 | 150 | private void Dispose(bool disposing) { 151 | if (this._disposed) { 152 | return; 153 | } 154 | if (disposing) { 155 | if (_runState != (long)UdpReceiverState.Stopped) { 156 | this.Stop(); 157 | } 158 | if (this._thread != null) { 159 | this._thread.Abort(); 160 | this._thread = null; 161 | } 162 | } 163 | this._disposed = true; 164 | } 165 | 166 | #endregion 167 | } 168 | 169 | } -------------------------------------------------------------------------------- /Application/Nfs/PortmapHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | using Snarf.Udp; 9 | 10 | namespace Snarf.Nfs { 11 | 12 | public class PortmapHandler : BaseHandler { 13 | 14 | public const int PORTMAP_PORT = 111; 15 | public const int PORTMAP_VERSION = 2; 16 | public const int PORTMAP_PROGRAMID = 100000; 17 | 18 | public const int PORTMAP_TRUE = 1; 19 | public const int PORTMAP_FALSE = 0; 20 | 21 | public enum PortmapProcedure : int { 22 | NULL = 0, 23 | SET = 1, 24 | UNSET = 2, 25 | GETPORT = 3, 26 | DUMP = 4, 27 | CALLIT = 5 28 | } 29 | 30 | public enum PortmapMessage : int { 31 | SUCCESS = 0, 32 | PROG_UNAVAIL = 1, 33 | PROG_MISMATCH = 2, 34 | PROC_UNAVAIL = 3, 35 | GARBAGE_ARGS = 4 36 | } 37 | 38 | public PortmapHandler() : base(PORTMAP_PORT, PORTMAP_PROGRAMID) { 39 | 40 | } 41 | 42 | protected override void Process(NfsPacket packet, IPEndPoint receivedFrom) { 43 | //Console.WriteLine("PortmapHandler.Process : recievedFrom: " + receivedFrom.ToString()); 44 | //Console.WriteLine("PortmapHandler.Process: Procedure -> " + packet.ProcedureID + ":" + ((PortmapProcedure)packet.ProcedureID).ToString()); 45 | 46 | switch (packet.ProcedureID) { 47 | case (int)PortmapProcedure.NULL: 48 | Null(packet, receivedFrom); 49 | break; 50 | case (int)PortmapProcedure.GETPORT: 51 | GetPort(packet, receivedFrom); 52 | break; 53 | case (int)PortmapProcedure.SET: 54 | break; 55 | case (int)PortmapProcedure.UNSET: 56 | break; 57 | case (int)PortmapProcedure.DUMP: 58 | break; 59 | case (int)PortmapProcedure.CALLIT: 60 | break; 61 | default: 62 | break; 63 | } 64 | } 65 | 66 | private void Null(NfsPacket sourcePacket, IPEndPoint receivedFrom) { 67 | // Put together an XDR reply packet 68 | NfsPacket packet = new NfsPacket(128); 69 | 70 | packet.SetUInt(sourcePacket.XID); 71 | packet.SetUInt((uint)RpcSignalType.Reply); 72 | packet.SetUInt((uint)RpcMessageResult.Accepted); 73 | 74 | // Put on a NULL authentication 75 | packet.AddNullAuthentication(); 76 | 77 | packet.SetUInt((uint)RpcProcedure.Success); 78 | 79 | Send(packet, receivedFrom); 80 | } 81 | 82 | private void GetPort(NfsPacket sourcePacket, IPEndPoint receivedFrom) { 83 | 84 | // skip past the authentication records 85 | sourcePacket.ReadAuthentication(); 86 | sourcePacket.ReadAuthentication(); 87 | 88 | // Collect the arguments to the procedure 89 | uint programId = sourcePacket.GetUInt(); 90 | uint version = sourcePacket.GetUInt(); 91 | uint protocol = sourcePacket.GetUInt(); 92 | 93 | // Put together an XDR reply packet 94 | NfsPacket packet = new NfsPacket(128); 95 | 96 | packet.SetUInt(sourcePacket.XID); 97 | packet.SetUInt((uint)RpcSignalType.Reply); 98 | packet.SetUInt((uint)RpcMessageResult.Accepted); 99 | 100 | packet.AddNullAuthentication(); 101 | 102 | if (!NfsHandlerManager.IsProgramRegistered((int)programId)) { 103 | packet.SetUInt((uint)RpcProcedure.ProgramUnavailable); 104 | } 105 | else { 106 | // TODO: add version checking. we're only doing v2 right now. 107 | // version mismatch gets the ProgMismatch value. 108 | // result.AddLong(RPCConsts.RPCProgMismatch); 109 | // result.AddLong(versmin); 110 | // result.AddLong(versmax); 111 | int port = NfsHandlerManager.GetPort((int)programId); 112 | 113 | if (port == 0) { 114 | packet.SetUInt((uint)RpcProcedure.ProgramMismatch); 115 | } 116 | else { 117 | packet.SetUInt((uint)RpcProcedure.Success); 118 | packet.SetUInt((uint)port); 119 | } 120 | } 121 | 122 | Send(packet, receivedFrom); 123 | } 124 | 125 | private void Set(NfsPacket sourcePacket, IPEndPoint receivedFrom) { 126 | // skip past the authentication records 127 | sourcePacket.ReadAuthentication(); 128 | sourcePacket.ReadAuthentication(); 129 | 130 | // Collect the arguments to the procedure 131 | uint programId = sourcePacket.GetUInt(); 132 | uint version = sourcePacket.GetUInt(); 133 | uint protocol = sourcePacket.GetUInt(); 134 | uint port = sourcePacket.GetUInt(); 135 | 136 | NfsPacket packet = new NfsPacket(128); 137 | 138 | packet.AddReplyHeader(sourcePacket.XID); 139 | packet.SetUInt(PORTMAP_TRUE); 140 | 141 | // portmapMapping toadd = new portmapMapping(prog, vers, prot); 142 | // toadd.SetPort(port); 143 | 144 | // XDRPacket result = new XDRPacket(128); 145 | // result.AddReplyHeader(xid); 146 | 147 | // // look for the chain of versions for this program 148 | // long? pl = new long?(prog); 149 | // portmapMapping chain = (portmapMapping)mappings[pl]; 150 | // if (chain == null) { 151 | // mappings.Add(pl, toadd); 152 | // result.AddLong(Portmap.PM_TRUE); 153 | // } 154 | // else { 155 | // // See if this version is already registered in the chain 156 | // while (chain != null) { 157 | // if (chain.Version() == vers && chain.Protocol() == prot) { 158 | // result.AddLong(Portmap.PM_FALSE); 159 | // break; 160 | // } 161 | // else if (chain.Next() == null) { 162 | // chain.SetNext(toadd); 163 | // result.AddLong(Portmap.PM_TRUE); 164 | // break; 165 | // } 166 | // chain = chain.Next(); 167 | // } 168 | // } 169 | 170 | Send(packet, receivedFrom); 171 | } 172 | 173 | } 174 | } -------------------------------------------------------------------------------- /Application/Snarf.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {A74404F2-5664-4C75-BB06-0D1BB0292B60} 8 | Exe 9 | Properties 10 | Snarf 11 | Snarf 12 | v4.5 13 | 512 14 | false 15 | publish\ 16 | true 17 | Disk 18 | false 19 | Foreground 20 | 7 21 | Days 22 | false 23 | false 24 | true 25 | 0 26 | 1.0.0.%2a 27 | false 28 | true 29 | 30 | 31 | x86 32 | true 33 | full 34 | false 35 | bin\Debug\ 36 | DEBUG;TRACE 37 | prompt 38 | 4 39 | 40 | 41 | AnyCPU 42 | pdbonly 43 | true 44 | bin\Release\ 45 | TRACE 46 | prompt 47 | 4 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Code 67 | 68 | 69 | 70 | 71 | 72 | Code 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | False 98 | Microsoft .NET Framework 4.5 %28x86 and x64%29 99 | true 100 | 101 | 102 | False 103 | .NET Framework 3.5 SP1 Client Profile 104 | false 105 | 106 | 107 | False 108 | .NET Framework 3.5 SP1 109 | false 110 | 111 | 112 | 113 | 114 | {f7f1f64d-f6cd-42a9-b167-546e4ea31463} 115 | Shellscape.Common 116 | 117 | 118 | 119 | 126 | -------------------------------------------------------------------------------- /Application/Nfs/FileSystem/NfsFileAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Security.Permissions; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Snarf.Nfs.FileSystem { 10 | 11 | // The file attributes that NFS needs. 12 | // 13 | // From: 14 | // Sun Microsystems, Inc. [Page 15] 15 | // RFC 1094 NFS: Network File System March 1989 16 | // 17 | public class NfsFileAttributes { 18 | 19 | private FileType type; 20 | private FilePermissions mode; 21 | 22 | private uint nlink; 23 | private uint uid; 24 | private uint gid; 25 | private uint size; 26 | private uint blocksize; 27 | private uint rdev; 28 | private uint blocks; 29 | private uint fsid; 30 | private uint fileid; 31 | 32 | private NfsTime lastAccessed; 33 | private NfsTime lastModified; 34 | private NfsTime lastChanged; 35 | 36 | [System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)] 37 | public static extern bool GetFileInformationByHandle(IntPtr hFile, out BY_HANDLE_FILE_INFORMATION lpFileInformation); 38 | 39 | [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] 40 | public struct FILETIME { 41 | public uint DateTimeLow; 42 | public uint DateTimeHigh; 43 | } 44 | 45 | public struct BY_HANDLE_FILE_INFORMATION { 46 | public uint FileAttributes; 47 | public FILETIME CreationTime; 48 | public FILETIME LastAccessTime; 49 | public FILETIME LastWriteTime; 50 | public uint VolumeSerialNumber; 51 | public uint FileSizeHigh; 52 | public uint FileSizeLow; 53 | public uint NumberOfLinks; 54 | public uint FileIndexHigh; 55 | public uint FileIndexLow; 56 | } 57 | 58 | public NfsFileAttributes() { } 59 | 60 | public NfsFileAttributes(String path) { 61 | Load(path); 62 | } 63 | 64 | public bool Read(NfsPacket packet) { 65 | type = (FileType)packet.GetUInt(); 66 | mode = (FilePermissions)packet.GetUInt(); 67 | nlink = packet.GetUInt(); 68 | uid = packet.GetUInt(); 69 | gid = packet.GetUInt(); 70 | size = packet.GetUInt(); 71 | blocksize = packet.GetUInt(); 72 | rdev = packet.GetUInt(); 73 | blocks = packet.GetUInt(); 74 | fsid = packet.GetUInt(); 75 | fileid = packet.GetUInt(); 76 | 77 | if (!lastAccessed.Read(packet)) { 78 | return false; 79 | } 80 | if (!lastModified.Read(packet)) { 81 | return false; 82 | } 83 | if (!lastChanged.Read(packet)) { 84 | return false; 85 | } 86 | 87 | return true; 88 | } 89 | 90 | public bool Emit(ref NfsPacket packet) { 91 | packet.SetUInt((uint)type); 92 | packet.SetUInt((uint)mode); 93 | packet.SetUInt(nlink); 94 | packet.SetUInt(uid); 95 | packet.SetUInt(gid); 96 | packet.SetUInt(size); 97 | packet.SetUInt(blocksize); 98 | packet.SetUInt(rdev); 99 | packet.SetUInt(blocks); 100 | packet.SetUInt(fsid); 101 | packet.SetUInt(fileid); 102 | 103 | if (!lastAccessed.Emit(ref packet)) { 104 | return false; 105 | } 106 | if (!lastModified.Emit(ref packet)) { 107 | return false; 108 | } 109 | if (!lastChanged.Emit(ref packet)) { 110 | return false; 111 | } 112 | 113 | return true; 114 | } 115 | 116 | public uint Load(String path) { 117 | 118 | if (!File.Exists(path) && !Directory.Exists(path)) { 119 | throw new FileNotFoundException(); 120 | } 121 | 122 | mode = 0; 123 | 124 | FileAttributes fileAttributes = File.GetAttributes(path); 125 | Boolean isDirectory = fileAttributes.Is(FileAttributes.Directory); 126 | Boolean canRead = NfsFileAttributes.CanRead(path); 127 | Boolean canWrite = NfsFileAttributes.CanWrite(path); 128 | 129 | if (!isDirectory) { 130 | type = FileType.NFREG; 131 | mode = FilePermissions.UPFILE; 132 | } 133 | else if (isDirectory) { 134 | type = FileType.NFDIR; 135 | mode = FilePermissions.UPDIR; 136 | } 137 | else { 138 | Console.Error.WriteLine("NfsFileAttributes.Load: " + path + " has unknown type"); 139 | type = FileType.NFNON; 140 | mode = 0; // don't know what kind of file system object this is 141 | } 142 | 143 | //if (!isDirectory) { 144 | // using (var fs = new FileStream(path, FileMode.Open)) { 145 | // canRead = fs.CanRead; 146 | // canWrite = fs.CanWrite; 147 | // } 148 | //} 149 | //else { 150 | // canRead = true; 151 | // canWrite = !fileAttributes.Is(FileAttributes.ReadOnly); 152 | //} 153 | 154 | if (canRead) { 155 | mode |= FilePermissions.UP_OREAD | FilePermissions.UP_GREAD | FilePermissions.UP_WREAD; 156 | mode |= FilePermissions.UP_OEXEC | FilePermissions.UP_GEXEC | FilePermissions.UP_WEXEC; 157 | } 158 | if (canWrite) { 159 | mode |= FilePermissions.UP_OWRITE | FilePermissions.UP_GWRITE | FilePermissions.UP_WWRITE; 160 | } 161 | 162 | // from now on assume either file or directory 163 | if (!isDirectory) { 164 | nlink = 1; 165 | } 166 | else { // directories always have 2 links 167 | nlink = 2; 168 | } 169 | 170 | FileInfo file = new FileInfo(path); 171 | 172 | uid = 0; 173 | gid = 0; 174 | size = isDirectory ? 512 : (uint)(new FileInfo(path).Length); 175 | blocksize = 512; // XXX common value, how do I get this in java? 176 | rdev = 0; 177 | blocks = (size + blocksize - 1) / blocksize; 178 | fsid = isDirectory ? 0 : GetFileSystemId(file); 179 | fileid = (uint)HandleManager.Current.GetHandle(path); 180 | 181 | lastAccessed = new NfsTime(file.LastAccessTime); 182 | lastChanged = new NfsTime(file.LastWriteTime); 183 | lastModified = new NfsTime(file.LastWriteTime); 184 | 185 | return (uint)NfsReply.OK; 186 | } 187 | 188 | public uint GetFileSystemId(FileInfo file) { 189 | BY_HANDLE_FILE_INFORMATION objectFileInfo = new BY_HANDLE_FILE_INFORMATION(); 190 | 191 | try { 192 | using (FileStream fs = file.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { 193 | GetFileInformationByHandle(fs.SafeFileHandle.DangerousGetHandle(), out objectFileInfo); 194 | } 195 | } 196 | catch (Exception) { 197 | return 0; 198 | } 199 | 200 | uint fileIndex = (objectFileInfo.FileIndexHigh << 32) + objectFileInfo.FileIndexLow; 201 | 202 | return fileIndex; 203 | } 204 | 205 | public static bool IsDirectory(string path) { 206 | FileAttributes fileAttributes = File.GetAttributes(path); 207 | return fileAttributes.Is(FileAttributes.Directory); 208 | } 209 | 210 | private static Boolean Can(String path, FileIOPermissionAccess value) { 211 | FileIOPermission fp = new FileIOPermission(value, path); 212 | 213 | try { 214 | fp.Demand(); 215 | } 216 | catch (System.Security.SecurityException) { 217 | return false; 218 | } 219 | 220 | return true; 221 | } 222 | 223 | public static Boolean CanRead(String path) { 224 | return Can(path, FileIOPermissionAccess.Read); 225 | } 226 | 227 | public static Boolean CanWrite(String path) { 228 | return Can(path, FileIOPermissionAccess.Write); 229 | } 230 | 231 | } 232 | 233 | } 234 | -------------------------------------------------------------------------------- /Application/Udp/UdpListener.cs: -------------------------------------------------------------------------------- 1 | // resused from http://growl-for-windows.googlecode.com/svn-history/r125/trunk/Growl/Growl.UDPLegacy/UdpListener.cs 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.NetworkInformation; 7 | using System.Net.Sockets; 8 | using System.Text; 9 | 10 | namespace Snarf.Udp { 11 | 12 | /// 13 | /// Simple class to represent state when used with a UdpListener 14 | /// 15 | public class UdpState { 16 | public UdpClient Udp; 17 | public IPEndPoint Endpoint; 18 | public AsyncCallback Callback; 19 | } 20 | 21 | /// 22 | /// A basic listener that listens for incoming UDP messages on the specified port 23 | /// and passes the event on to application code whenever a message is received. 24 | /// 25 | public class UdpListener : IDisposable { 26 | 27 | private static object syncLock = new object(); 28 | private static Dictionary masks; 29 | 30 | private Boolean _started = false; 31 | 32 | /// 33 | /// The port to listen for messages on 34 | /// 35 | protected int port; 36 | /// 37 | /// Indicates if messages from remote machines should be allowed or not 38 | /// 39 | protected bool localMessagesOnly = true; 40 | /// 41 | /// The underlying 42 | /// 43 | protected UdpClient udp; 44 | /// 45 | /// Event handlder for the event 46 | /// 47 | /// The raw packet data 48 | /// The host that sent the message 49 | /// Indicates if the request came from the local machine 50 | /// Indicates if the request came from the LAN 51 | public delegate void PacketHandler(byte[] bytes, IPEndPoint receivedFrom); 52 | /// 53 | /// Fires when a message is received 54 | /// 55 | public event PacketHandler PacketReceived; 56 | 57 | /// 58 | /// Creates a new 59 | /// 60 | /// The port to listen for messages on 61 | /// true to only listen for messages from the local machine;false to listen for messages from any source 62 | public UdpListener(int port, bool localMessagesOnly) { 63 | this.port = port; 64 | this.localMessagesOnly = localMessagesOnly; 65 | } 66 | 67 | public int Port { get { return port; } } 68 | 69 | public UdpClient Client { get { return udp; } } 70 | 71 | public void Start() { 72 | Start(null); 73 | } 74 | 75 | /// 76 | /// Starts listening for messages on the specified port 77 | /// 78 | public void Start(AsyncCallback callback) { 79 | 80 | if (_started) { 81 | return; 82 | } 83 | 84 | _started = true; 85 | 86 | IPAddress address = (this.localMessagesOnly ? IPAddress.Loopback : IPAddress.Any); 87 | IPEndPoint endpoint = new IPEndPoint(address, this.port); 88 | this.udp = new UdpClient(endpoint); 89 | this.port = ((IPEndPoint)udp.Client.LocalEndPoint).Port; // if 0 is passed, the system will assign a port. 90 | 91 | if (callback == null) { 92 | callback = new AsyncCallback(this.ProcessPacket); 93 | } 94 | 95 | UdpState state = new UdpState(); 96 | state.Udp = udp; 97 | state.Endpoint = endpoint; 98 | state.Callback = callback; 99 | 100 | udp.BeginReceive(callback, state); 101 | } 102 | 103 | /// 104 | /// Stops listening for messages and frees the port 105 | /// 106 | public void Stop() { 107 | if (!_started) { 108 | return; 109 | } 110 | 111 | _started = false; 112 | 113 | try { 114 | this.udp.Close(); 115 | this.udp = null; 116 | } 117 | finally { 118 | } 119 | } 120 | 121 | /// 122 | /// When a message is received by the listener, the raw data is read from the packet 123 | /// and the event is fired. 124 | /// 125 | /// 126 | private void ProcessPacket(IAsyncResult ar) { 127 | try { 128 | UdpClient udp = (UdpClient)((UdpState)(ar.AsyncState)).Udp; 129 | IPEndPoint endpoint = (IPEndPoint)((UdpState)(ar.AsyncState)).Endpoint; 130 | AsyncCallback callback = (AsyncCallback)((UdpState)(ar.AsyncState)).Callback; 131 | 132 | byte[] bytes = udp.EndReceive(ar, ref endpoint); 133 | 134 | IPAddress localAddress = IPAddress.Loopback; 135 | IPEndPoint localEndpoint = (IPEndPoint)udp.Client.LocalEndPoint; 136 | if (localEndpoint != null) localAddress = localEndpoint.Address; 137 | 138 | //bool isLocal = IPAddress.IsLoopback(endpoint.Address); 139 | //bool isLAN = IsInSameSubnet(localAddress, endpoint.Address); 140 | //string receivedFrom = endpoint.ToString(); 141 | 142 | // start listening again 143 | udp.BeginReceive(callback, ar.AsyncState); 144 | 145 | // bubble up the event 146 | if (this.PacketReceived != null) this.PacketReceived(bytes, endpoint); 147 | } 148 | catch (Exception e) { 149 | // swallow any exceptions (this handles the case when Growl is stopped while still listening for network notifications) 150 | } 151 | } 152 | 153 | public static bool IsInSameSubnet(IPAddress localAddress, IPAddress otherAddress) { 154 | try { 155 | // handle loopback addresses and IPv6 local addresses 156 | if (IPAddress.IsLoopback(otherAddress) 157 | || otherAddress.IsIPv6LinkLocal 158 | || otherAddress.IsIPv6SiteLocal) 159 | return true; 160 | 161 | IPAddress subnetMask = GetLocalSubnetMask(localAddress); 162 | IPAddress network1 = GetNetworkAddress(localAddress, subnetMask); 163 | IPAddress network2 = GetNetworkAddress(otherAddress, subnetMask); 164 | return network1.Equals(network2); 165 | } 166 | catch { 167 | Console.WriteLine(String.Format("Could not determine subnet. Local address: {0} - Remote Address: {1}", localAddress, otherAddress)); 168 | } 169 | return false; 170 | } 171 | 172 | public static IPAddress GetLocalSubnetMask(IPAddress ipaddress) { 173 | lock (syncLock) { 174 | if (masks == null) { 175 | masks = new Dictionary(); 176 | 177 | NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces(); 178 | foreach (NetworkInterface ni in interfaces) { 179 | //Console.WriteLine(ni.Description); 180 | 181 | UnicastIPAddressInformationCollection unicastAddresses = ni.GetIPProperties().UnicastAddresses; 182 | foreach (UnicastIPAddressInformation unicastAddress in unicastAddresses) { 183 | IPAddress mask = (unicastAddress.IPv4Mask != null ? unicastAddress.IPv4Mask : IPAddress.None); 184 | masks.Add(unicastAddress.Address, mask); 185 | 186 | //Console.WriteLine("\tIP Address is {0}", unicastAddress.Address); 187 | //Console.WriteLine("\tSubnet Mask is {0}", mask); 188 | } 189 | } 190 | } 191 | } 192 | 193 | if (masks.ContainsKey(ipaddress)) 194 | return masks[ipaddress]; 195 | else 196 | return IPAddress.None; 197 | } 198 | 199 | public static IPAddress GetNetworkAddress(IPAddress address, IPAddress subnetMask) { 200 | byte[] ipAdressBytes = address.GetAddressBytes(); 201 | byte[] subnetMaskBytes = subnetMask.GetAddressBytes(); 202 | 203 | if (ipAdressBytes.Length != subnetMaskBytes.Length) 204 | throw new ArgumentException("Lengths of IP address and subnet mask do not match."); 205 | 206 | byte[] broadcastAddress = new byte[ipAdressBytes.Length]; 207 | for (int i = 0; i < broadcastAddress.Length; i++) { 208 | broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i])); 209 | } 210 | return new IPAddress(broadcastAddress); 211 | } 212 | 213 | #region IDisposable Members 214 | 215 | public void Dispose() { 216 | this.Dispose(true); 217 | GC.SuppressFinalize(this); 218 | } 219 | 220 | protected void Dispose(bool disposing) { 221 | if (disposing) { 222 | try { 223 | if (this.udp != null) this.udp.Close(); 224 | } 225 | catch { 226 | // suppress 227 | } 228 | } 229 | } 230 | 231 | #endregion 232 | } 233 | } -------------------------------------------------------------------------------- /Application/Nfs/FileSystem/NfsDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Snarf.Nfs.FileSystem { 7 | 8 | internal class NfsDirectory { 9 | 10 | private FileSystemInfo _fsInfo; 11 | 12 | // keep these between calls so subsequent calls getting the rest of the contents of a directory are fast. 13 | private String _cachedDirectories; 14 | private List _cachedFiles; 15 | 16 | public NfsDirectory(FileSystemInfo fsInfo) { 17 | _fsInfo = fsInfo; 18 | } 19 | 20 | public virtual NfsPacket GetAttr(NfsPacket packet) { 21 | try { 22 | FileHandle f = new FileHandle(packet); 23 | NfsFileAttributes fa = new NfsFileAttributes(); 24 | 25 | string file = GetNameFromHandle(f.Handle, packet.XID); 26 | 27 | if (!File.Exists(file) && !Directory.Exists(file)) { 28 | throw new NFSException(packet.XID, (uint)NfsReply.ERR_NOENT); 29 | } 30 | 31 | Console.WriteLine("NfsDirectory.GetAttr: file: " + file); 32 | fa.Load(file); 33 | 34 | NfsPacket reply = new NfsPacket(96); 35 | reply.AddReplyHeader(packet.XID); 36 | reply.SetUInt((uint)NfsReply.OK); 37 | 38 | fa.Emit(ref reply); 39 | 40 | return reply; 41 | } 42 | catch (FileNotFoundException) { 43 | throw new NFSException(packet.XID, (uint)NfsReply.ERR_NOENT); 44 | } 45 | } 46 | 47 | public virtual NfsPacket SetAttr(uint xid, NfsPacket packet) { 48 | try { 49 | FileHandle f = new FileHandle(packet); 50 | String fileName = GetNameFromHandle(f.Handle, xid); 51 | 52 | // the attributes 53 | int mode = (int)packet.GetUInt(); 54 | int uid = (int)packet.GetUInt(); 55 | int gid = (int)packet.GetUInt(); 56 | int size = (int)packet.GetUInt(); 57 | NfsTime atime = new NfsTime(packet); 58 | NfsTime mtime = new NfsTime(packet); 59 | 60 | // the only attribute that can be set is the size can be set to 0 to truncate the file 61 | if (size == 0) { 62 | // truncate by deleting and recreating the file 63 | using (var fs = new FileStream(fileName, FileMode.Truncate, FileAccess.ReadWrite)) { } 64 | } 65 | 66 | NfsPacket reply = new NfsPacket(128); 67 | reply.AddReplyHeader(xid); 68 | reply.SetUInt((uint)NfsReply.OK); 69 | 70 | NfsFileAttributes fa = new NfsFileAttributes(); 71 | fa.Load(fileName); 72 | fa.Emit(ref reply); 73 | 74 | return reply; 75 | } 76 | catch (FileNotFoundException) { 77 | throw new NFSException(xid, (uint)NfsReply.ERR_NOENT); 78 | } 79 | catch (IOException) { 80 | throw new NFSException(xid, (uint)NfsReply.ERR_PERM); 81 | } 82 | } 83 | 84 | public virtual NfsPacket Lookup(NfsPacket packet) { 85 | try { 86 | FileHandle dir = new FileHandle(packet); 87 | String entry = packet.GetString(); 88 | String dirName = GetNameFromHandle(dir.Handle, packet.XID); 89 | String fileName = Path.Combine(dirName, entry); 90 | 91 | Console.WriteLine("Lookup -> " + entry); 92 | 93 | if (!File.Exists(fileName) && !Directory.Exists(fileName)) { 94 | throw new NFSException(packet.XID, (uint)NfsReply.ERR_NOENT); 95 | } 96 | 97 | NfsFileAttributes attributes = new NfsFileAttributes(); 98 | attributes.Load(fileName); 99 | 100 | // make a FileHandle for this new path 101 | uint handleId = HandleManager.Current.GetHandle(fileName); 102 | 103 | FileHandle handle = new FileHandle(); 104 | handle.Set(dir.Root, handleId, dir.ReadOnly); 105 | 106 | // make the reply 107 | NfsPacket reply = new NfsPacket(128); 108 | reply.AddReplyHeader(packet.XID); 109 | reply.SetUInt((uint)NfsReply.OK); 110 | 111 | handle.Emit(ref reply); 112 | attributes.Emit(ref reply); 113 | 114 | return reply; 115 | 116 | } 117 | catch (FileNotFoundException) { 118 | throw new NFSException(packet.XID, (uint)NfsReply.ERR_NOENT); 119 | } 120 | } 121 | 122 | public virtual NfsPacket ReadDirectory(NfsPacket packet) { 123 | 124 | FileHandle fh = new FileHandle(packet); 125 | uint cookie = packet.GetUInt(); 126 | uint count = packet.GetUInt(); 127 | uint xId = packet.XID; 128 | 129 | // if this is a new call to readdir (cookie=0) or it is a new directory to read, replace the cache. 130 | string dirName = GetNameFromHandle(fh.Handle, xId); 131 | 132 | //Console.Write("Reading dir " + dirName + " cookie=" + cookie + " count=" + count + "\n"); 133 | 134 | if (cookie == 0 || (dirName.Equals(_cachedDirectories) == false)) { 135 | 136 | if (!Directory.Exists(dirName)) { 137 | throw new NFSException(xId, (uint)NfsReply.ERR_NOENT); 138 | } 139 | 140 | List dirFiles = Directory.GetFiles(dirName).Select(f => new FileInfo(f).Name).ToList(); 141 | dirFiles.AddRange(Directory.GetDirectories(dirName).Select(d => new DirectoryInfo(d).Name)); 142 | 143 | if (dirFiles == null) { 144 | throw new NFSException(xId, (uint)NfsReply.ERR_NOENT); 145 | } 146 | 147 | //Console.WriteLine("dir has " + dirFiles.Count + " entries"); 148 | 149 | if (dirFiles.Count <= 0) { 150 | throw new NFSException(xId, (uint)NfsReply.ERR_NOENT); 151 | } 152 | 153 | dirFiles.BubbleSort(); 154 | //dirFiles.Insert(0, ".."); 155 | //dirFiles.Insert(0, "."); 156 | 157 | _cachedFiles = dirFiles; 158 | _cachedDirectories = dirName; 159 | } 160 | 161 | // prepare the reply packet. 162 | NfsPacket reply = new NfsPacket((int)count); 163 | reply.AddReplyHeader(xId); 164 | reply.SetUInt((uint)NfsReply.OK); 165 | 166 | // Add files to the list until there are no more files or all of the count bytes have been used. 167 | 168 | int current = reply.Length; 169 | bool more = false; // are there more files to get 170 | 171 | // if there are any files to add 172 | if (_cachedFiles != null && _cachedFiles.Count > 0) { 173 | for (int i = (int)cookie; i < _cachedFiles.Count; i++) { 174 | // see if there is enough room for another file - 3 longs of id, 175 | // the name (rounded up to 4 bytes) and a trailing long 176 | // indicating whether there are more files to get 177 | int needed = 3 * 4 + (_cachedFiles[i].Length + 3) + 8; 178 | if (needed + current >= count) { 179 | more = true; 180 | break; 181 | } 182 | // get the handle for this file 183 | string fileName = Path.Combine(_cachedDirectories, _cachedFiles[i]); 184 | uint handle = HandleManager.Current.GetHandle(fileName); 185 | 186 | Console.WriteLine("Read handle for: " + fileName + " -> " + handle); 187 | 188 | // add an entry to the packet for this file 189 | reply.SetUInt(NfsHandler.NFS_TRUE); 190 | reply.SetUInt(handle); 191 | reply.Set(_cachedFiles[i]); 192 | reply.SetUInt((uint)i + 1); // this is the cookie 193 | 194 | current = reply.Length; 195 | } 196 | } 197 | reply.SetUInt(NfsHandler.NFS_FALSE); // no more entries in this packet 198 | 199 | // tell the client if this packet has returned the last of the files 200 | if (more) { 201 | reply.SetUInt(NfsHandler.NFS_FALSE); 202 | } 203 | else { 204 | reply.SetUInt(NfsHandler.NFS_TRUE); 205 | } 206 | 207 | return reply; 208 | } 209 | 210 | public virtual NfsPacket Create(uint xid, NfsPacket packet) { 211 | try { 212 | FileHandle dirFH = new FileHandle(packet); 213 | string entry = packet.GetString(); 214 | string dirName = GetNameFromHandle(dirFH.Handle, xid); 215 | string path = Path.Combine(dirName, entry); 216 | 217 | // make the file 218 | 219 | if (File.Exists(path)) { 220 | throw new NFSException(xid, (uint)NfsReply.ERR_EXIST); 221 | } 222 | 223 | using (var file = File.Create(path)) { } 224 | 225 | // make a new handle for this file 226 | FileHandle fh = new FileHandle(); 227 | long handle = HandleManager.Current.GetHandle(path); 228 | fh.Set(dirFH.Root, (uint)handle, dirFH.ReadOnly); 229 | 230 | // get the attributes of this new file 231 | NfsFileAttributes fa = new NfsFileAttributes(); 232 | fa.Load(path); 233 | 234 | // create the reply packet 235 | NfsPacket reply = new NfsPacket(128); 236 | reply.AddReplyHeader(xid); 237 | reply.SetUInt((uint)NfsReply.OK); 238 | fh.Emit(ref reply); 239 | fa.Emit(ref reply); 240 | return reply; 241 | 242 | } 243 | catch (FileNotFoundException) { 244 | throw new NFSException(xid, (uint)NfsReply.ERR_IO); 245 | } 246 | catch (IOException) { 247 | throw new NFSException(xid, (uint)NfsReply.ERR_IO); 248 | } 249 | catch (System.Security.SecurityException) { 250 | throw new NFSException(xid, (uint)NfsReply.ERR_PERM); 251 | } 252 | } 253 | 254 | public virtual NfsPacket Remove(uint xid, NfsPacket packet) { 255 | FileHandle fileHandle = new FileHandle(packet); 256 | string entry = packet.GetString(); 257 | 258 | // open and delete the file 259 | string dirName = GetNameFromHandle(fileHandle.Handle, xid); 260 | var fd = new FileInfo(Path.Combine(dirName, entry)); 261 | if (fd.Exists == false) { 262 | throw new NFSException(xid, (uint)NfsReply.ERR_NOENT); 263 | } 264 | try { 265 | fd.Delete(); 266 | } 267 | catch (Exception) { 268 | throw new NFSException(xid, (uint)NfsReply.ERR_IO); 269 | } 270 | 271 | // create the reply packet 272 | NfsPacket reply = new NfsPacket(128); 273 | reply.AddReplyHeader(xid); 274 | reply.SetUInt((uint)NfsReply.OK); 275 | return reply; 276 | } 277 | 278 | public virtual NfsPacket Mkdir(uint xid, NfsPacket packet) { 279 | try { 280 | FileHandle fileHandle = new FileHandle(packet); 281 | string entry = packet.GetString(); 282 | 283 | string dirName = GetNameFromHandle(fileHandle.Handle, xid); 284 | string newdir = Path.Combine(dirName, entry); 285 | 286 | var dir = new DirectoryInfo(newdir); 287 | 288 | if (dir.Exists) { 289 | throw new NFSException(xid, (uint)NfsReply.ERR_EXIST); 290 | } 291 | 292 | dir.Create(); 293 | 294 | // make a FileHandle for this directory 295 | long handle = HandleManager.Current.GetHandle(newdir); 296 | FileHandle newFH = new FileHandle(); 297 | newFH.Set(fileHandle.Root, (uint)handle, fileHandle.ReadOnly); 298 | 299 | // get the attributes 300 | NfsFileAttributes fa = new NfsFileAttributes(); 301 | fa.Load(newdir); 302 | 303 | NfsPacket reply = new NfsPacket(128); 304 | reply.AddReplyHeader(xid); 305 | reply.SetUInt((uint)NfsReply.OK); 306 | newFH.Emit(ref reply); 307 | fa.Emit(ref reply); 308 | return reply; 309 | 310 | } 311 | catch (FileNotFoundException) { 312 | throw new NFSException(xid, (uint)NfsReply.ERR_IO); 313 | } 314 | } 315 | 316 | public virtual NfsPacket Rmdir(uint xid, NfsPacket packet) { 317 | FileHandle fileHandle = new FileHandle(packet); 318 | string name = packet.GetString(); 319 | 320 | string dirname = GetNameFromHandle(fileHandle.Handle, xid); 321 | var fd = new FileInfo(Path.Combine(dirname, name)); 322 | // do some correctness checking 323 | if (fd.Exists == false) { 324 | throw new NFSException(xid, (uint)NfsReply.ERR_NOENT); 325 | } 326 | if (NfsFileAttributes.IsDirectory(fd.FullName) == false) { 327 | throw new NFSException(xid, (uint)NfsReply.ERR_NOTDIR); 328 | } 329 | // try to remove the directory 330 | try { 331 | fd.Delete(); 332 | } 333 | catch (Exception) { 334 | throw new NFSException(xid, (uint)NfsReply.ERR_IO); 335 | } 336 | 337 | NfsPacket reply = new NfsPacket(128); 338 | reply.AddReplyHeader(xid); 339 | reply.SetUInt((uint)NfsReply.OK); 340 | return reply; 341 | } 342 | 343 | public virtual NfsPacket StatFS(NfsPacket packet) { 344 | FileHandle fh = new FileHandle(packet); 345 | // tell the fsinfo the path to get information about 346 | _fsInfo.SetFS(GetNameFromHandle(fh.Handle, packet.XID)); 347 | 348 | NfsPacket reply = new NfsPacket(128); 349 | reply.AddReplyHeader(packet.XID); 350 | reply.SetUInt((uint)NfsReply.OK); 351 | reply.SetUInt(_fsInfo.TransferSize); 352 | reply.SetUInt(_fsInfo.BlockSize); 353 | reply.SetUInt(_fsInfo.TotalBlocks); 354 | reply.SetUInt(_fsInfo.FreeBlocks); 355 | reply.SetUInt(_fsInfo.AvailableBlocks); 356 | return reply; 357 | } 358 | 359 | public virtual NfsPacket Rename(uint xid, NfsPacket packet) { 360 | // collect arguments from RPC packet 361 | FileHandle sourceHandle = new FileHandle(packet); 362 | string srcentry = packet.GetString(); 363 | 364 | FileHandle destHandle = new FileHandle(packet); 365 | string destentry = packet.GetString(); 366 | 367 | // compute the path names specified 368 | String srcdir = GetNameFromHandle(sourceHandle.Handle, xid); 369 | String destdir = GetNameFromHandle(destHandle.Handle, xid); 370 | 371 | FileInfo source = new FileInfo(Path.Combine(srcdir, srcentry)); 372 | FileInfo dest = new FileInfo(Path.Combine(destdir, destentry)); 373 | 374 | if (source.Exists == false) { 375 | throw new NFSException(xid, (uint)NfsReply.ERR_NOENT); 376 | } 377 | 378 | if (dest.Exists) { 379 | throw new NFSException(xid, (uint)NfsReply.ERR_EXIST); 380 | } 381 | 382 | try { 383 | source.MoveTo(dest.FullName); 384 | } 385 | catch (Exception) { 386 | throw new NFSException(xid, (uint)NfsReply.ERR_IO); 387 | } 388 | 389 | NfsPacket reply = new NfsPacket(128); 390 | reply.AddReplyHeader(xid); 391 | reply.SetUInt((uint)NfsReply.OK); 392 | return reply; 393 | } 394 | 395 | // local procedure to get the associated with a handle, throws an exception if there is a problem. 396 | private string GetNameFromHandle(uint handle, uint xid) { 397 | String result = HandleManager.Current.GetName(handle); 398 | if (result == null) { 399 | throw new NFSException(xid, (int)NfsReply.ERR_STALE); 400 | } 401 | return result; 402 | } 403 | } 404 | } --------------------------------------------------------------------------------