From 86ebf4962a1c4a153e16218d63102e4a26abfeae Mon Sep 17 00:00:00 2001 From: alhimik45 Date: Fri, 5 Jan 2018 01:24:49 +0700 Subject: [PATCH] First version --- .gitignore | 291 +++ ChangeLog | 743 +++++++ CreateNativeMap/CreateNativeMap.csproj | 16 + CreateNativeMap/Program.cs | 1942 +++++++++++++++++ LICENSE | 21 + Mono.Fuse.NETStandard.sln | 47 + Mono.Fuse.NETStandard/FileNameMarshaler.cs | 79 + Mono.Fuse.NETStandard/FileSystem.cs | 1749 +++++++++++++++ Mono.Fuse.NETStandard/MapAttribute.cs | 56 + .../Mono.Fuse.NETStandard.csproj | 20 + MonoFuseHelper/mfh.c | 493 +++++ README | 88 + build-libs.sh | 40 + example/HelloFS/HelloFS.cs | 270 +++ example/HelloFS/HelloFS.csproj | 13 + example/RedirectFS-FH/RedirectFS-FH.cs | 421 ++++ example/RedirectFS-FH/RedirectFS-FH.csproj | 19 + example/RedirectFS/RedirectFS.cs | 368 ++++ example/RedirectFS/RedirectFS.csproj | 19 + 19 files changed, 6695 insertions(+) create mode 100644 .gitignore create mode 100644 ChangeLog create mode 100644 CreateNativeMap/CreateNativeMap.csproj create mode 100644 CreateNativeMap/Program.cs create mode 100644 LICENSE create mode 100644 Mono.Fuse.NETStandard.sln create mode 100644 Mono.Fuse.NETStandard/FileNameMarshaler.cs create mode 100644 Mono.Fuse.NETStandard/FileSystem.cs create mode 100644 Mono.Fuse.NETStandard/MapAttribute.cs create mode 100644 Mono.Fuse.NETStandard/Mono.Fuse.NETStandard.csproj create mode 100644 MonoFuseHelper/mfh.c create mode 100644 README create mode 100755 build-libs.sh create mode 100644 example/HelloFS/HelloFS.cs create mode 100644 example/HelloFS/HelloFS.csproj create mode 100644 example/RedirectFS-FH/RedirectFS-FH.cs create mode 100644 example/RedirectFS-FH/RedirectFS-FH.csproj create mode 100644 example/RedirectFS/RedirectFS.cs create mode 100644 example/RedirectFS/RedirectFS.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7cdeafe --- /dev/null +++ b/.gitignore @@ -0,0 +1,291 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +buildlibs/* +nativelibs/linux-x64/* \ No newline at end of file diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..da05cd7 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,743 @@ +2016-06-08 Justin Needham + Migrate from gmcs to mcs for Mono 4.* + * configure.ac: gmcs --> mcs + * doc/en/Mono.Fuse/FileSystem.xml : gmcs --> mcs + +2008-04-01 Jonathan Pryor + + * configure.in: Error if `mono' can't be found (not continue), and continue + if `monodoc' can't be found (not error). Simplify. + +2008-01-30 Jonathan Pryor + + * configure.in: Bump version numbers; genreate mono-fuse.spec. + * Makefile.am: Add `rpm', `prepare-release' targets. + * example/HelloFS/Makefile.am, example/RedirectFS/Makefile.am, + lib/mono-fuse/Makefile.am, src/Mono.Fuse/Makefile.am, + src/MonoFuseHelper/Makefile.am: Enhance so `make distcheck works`. + * .gitignore, doc/.gitignore, example/HelloFS/.gitignore, + example/RedirectFS/.gitignore, lib/.gitignore, lib/mono-fuse/.gitignore, + lib/pkgconfig/.gitignore, src/Mono.Fuse/.gitignore, + src/MonoFuseHelper/.gitignore: Added; ignore generated files. + * INSTALL: Added; install information. + * src/Mono.Fuse/AssemblyInfo.cs.in: Added; contains assembly version info. + * mono-fuse.spec.in: Added; RPM .spec file; thanks to + Jay Miller for the original version. + +2007-06-29 Jonathan Pryor + + * configure.in: My sh-fu needs work. Properly check the return value of + `gmcs' so I can determine whether it compiled successfully. + +2007-06-29 Jonathan Pryor + + * configure.in: Use a better check for the + Mono.Unix.Native.NativeConvert.TryCopy() methods, as relying on + libMonoPosixHelper.so is inadequate. + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: Use NativeConvert.TryCopy(), not + NativeConvert.Copy(). (NativeConvert.Copy() was only proposed, but never + made it into an official Mono.Posix release, while .TryCopy did.) + +2007-04-17 Jonathan Pryor + + * example/HelloFS/hellofs.in, example/RedirectFS/redirectfs.in, + example/RedirectFS/redirects-fh.in: Define DYLD_LIBRARY_PATH so that + example programs work seamlessly on OS X as well as Linux. + * lib/mono-fuse/Mono.Fuse.dll.config: Added; Mono dllmap config file so that + MonoFuseHelper is redirected to libMonoFuseHelper.dylib on Mac OS X. + * src/Mono.Fuse/Makefile.am: Install the Mono.Fuse.dll.mdb and + Mono.Fuse.dll.config files. + +2007-04-17 Jonathan Pryor + + * src/MonoFuseHelper/Makefile.am: Define HAVE_STRUCT_FUSE_ARGS so that + map.c's Mono_Fuse_ToArgs() and Mono_Fuse_FromArgs() will be compiled. + Otherwise, we get an unsatisfied link error when loading MonoFuseHelper. + +2007-04-17 Jonathan Pryor + + * src/Mono.Fuse/create-native-map.exe, src/Mono.Fuse/MapAttribute.cs: Update + to latest versions (fixes buggy overflow checking in MonoFuseHelper). + * lib/Mono.Fuse/Makefile.am: FUSE_USE_VERSION should be in the .c file, not + the .h file. Also fixes Mac OS X compilation, as FUSE_USE_VERSION needs + to be defined *before* including , but is inserted before + the include of lib/map.h -- bad order. + +2007-01-17 Jonathan Pryor + + * src/MonoFuseHelper/Makefile.am: Use $(LIBTOOL), not `libtool', as the + libtool program may go by different names on different platforms (e.g. + glibtool on OS X). + +2006-10-31 Jonathan Pryor + + * example/HelloFS/HelloFS.cs: Add extra Trace statements; remove unused code. + +2006-10-23 Jonathan Pryor + + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: Add [In,Out] to the + FileSystmeOperationContext parameter in mfh_fuse_get_context(). + Otherwise, the data is never marshaled back into managed memory, so all + fields are zero'd out (making the method useless). Oops! + +2006-10-12 Jonathan Pryor + + * doc/mono-fuse-docs.source: Update @basefile to contain the actual + basefile. Allows installed monodoc documentation to work. + +2006-10-12 Jonathan Pryor + + * doc/Makefile.am: Add `-pretty' so monodocer produces nicely indented XML. + * doc/en/Mono.Fuse/DirectoryEntry.xml, doc/en/Mono.Fuse/FileSystem.xml, + doc/en/Mono.Fuse/FileSystemOperationContext.xml, + doc/en/Mono.Fuse/OpenedPathInfo.xml: Flush documentation (largest change + was due to reformatting of XML). Also used svn version of monodocer to + get proper Generics-formatted method declarations, so + FileSystem.OnReadDirectory now lists a parameter of + System.Collections.Generic.IEnumerable instead + of System.Collections.Generic.IEnumerable`1[[Mono.Fuse.DirectoryEntry, + Mono.Fuse, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]. + * src/Mono.Fuse/create-native-map.exe: Commit latest version; fixes #ifndef + name formatting so that SYMBOL in "#ifndef SYMBOL" doesn't contain `/'. + +2006-09-21 Jonathan Pryor + + * configure.in: Remove checks for mint. Mint is dead. + +2006-09-21 Jonathan Pryor + + * doc/en/Mono.Fuse/FileSystem.xml, doc/en/Mono.Fuse/OpenedPathInfo.xml: + Minor doc updates. + +2006-09-20 Jonathan Pryor + + * doc/Makefile.am: Create mono-fuse-docs.* files, not mono-fuse.* files. + This matches Gtk#, and allows for clarity if we ever have + mono-fuse-docs.zip and mono-fuse-VERSION.tar.gz in the same directory. + * doc/en/index.xml, doc/en/Mono.Fuse.xml, doc/en/Mono.Fuse/DirectoryEntry.xml, + doc/en/Mono.Fuse/FileSystem.xml, + doc/en/Mono.Fuse/FileSystemOperationContext.xml, + doc/en/Mono.Fuse/OpenedPathInfo.xml: Add documentation. + * doc/en/Mono.Fuse/FileSystemEntry.xml: Renamed to DirectoryEntry.xml. + * example/HelloFS/HelloFS.cs: Use DirectoryEntry instead of FileSystemEntry; + use OpenedPathInfo.OpenAccess instead of OpenedPathInfo.OpenReadOnly. + * example/RedirectFS/RedirectFS.cs, example/RedirectFS/RedirectFS-FH.cs: Use + DirectoryEntry instead of FileSystemEntry; + s/OnCloseDirectory/OnReleaseDirectory/. + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: Changes by type: + - FileSystemOperationContext: + - Make sealed (this should only be created internally). + - Constructor is now internal. + - FileSystemEntry: + - Renamed to DirectoryEntry. + - Renamed Path property to Name. + - Add argument checking to DirectoryEntry constructor. + - Remove implicit conversion to string (as constructor may generate + exceptions). + - OpenedPathInfo: + - Make seaeled (this should only be created internally). + - Constructor now internal. + - Remove OpenReadOnly, OpenWriteOnly, OpenReadWrite properties + - Add OpenAccess property. + - FileSystem: + - Now `abstract'. There are no abstract methods, but it's pointless + to create instances of this type, as it wouldn't do anything. + - Make constructors protected (to emphasize the above). + - Rename FileSystemName property to Name. + - Rename ImmediateRemoval property to ImmediatePathRemoval. + - Rename DirectIO property to EnableDirectIO. + - Rename Umask property to DefaultUmask. + - Add EnableKernelCache property. + - Rename UserId property to DefaultUserId. + - Rename GroupId property to DefaultGroupId. + - Rename EntryTimeout property to PathTimeout. + - Rename DeletedNameTimeout property to DeletedPathTimeout. + - Change types of Timeout properties to `double' from `int'; changes + PathTimeout, DeletedPathTimeout, AttributeTimeout. + - Rename OnCloseDirectory() method to OnReleaseDirectory() (for + consistency with OnReleaseHandle). + - Rename Exit() method to Stop(). + - Rename parameters of many methods to match the method names -- e.g. + OnReadSymbolicLink() takes a `link' parameter instead of `path', + OnOpenDirectory() takes a `directory' parameter instead of `path', etc. + +2006-09-12 Jonathan Pryor + + * src/Mono.Fuse/Makefile.am: Add FileNameMarshaler to the build. + * src/Mono.Fuse/Mono.Fuse/FileNameMarshaler.cs: Added; marshals filenames + with any encoding to/from managed code (from + Mono.Unix.Native.FileNameMarshaler). + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: + - Use the FileNameMarshaler custom marshaler to manipulate filenames + entering/leaving managed code. This allows manipulation of filenames + with non-UTF8 encodings. + - Reimplement FillEntries(). When mfh_invoke_filler() returns 1, it means + that the current entry was not inserted, and needs to be inserted during + the next call. Previously, if a directory had many files (i.e. + _OnReadDirectory() was called > 2 times), it would "drop" the file that + was passed to mfh_invoke_filler() when it returned 1. Now, when + mfh_invoke_filler() return 1, we remember this and return the current + entry a 2nd time so that nothing is lost. + +2006-09-11 Jonathan Pryor + + * configure.in: Fix test for HAVE_MONO_UNIX_NATIVE_COPY_FUNCS; Add check for + `mdassembler' and `monodocer' (from monodoc module); create doc/Makefile. + * Makefile.am: Add `doc' directory to the build. + * doc/Makefile.am: Added; build the Monodoc documentation. + * doc/README: Added; short overview for adding docs to monodoc. + * doc/mono-fuse.source: Added; monodoc source file (where in the tree the + Mono.Fuse sources should be placed). + * doc/en/index.xml, doc/en/Mono.Fuse.xml, doc/en/Mono.Fuse/FileSystem.xml, + doc/en/Mono.Fuse/FileSystemEntry.xml, + doc/en/Mono.Fuse/FileSystemOperationContext.xml, + doc/en/Mono.Fuse/OpenedPathInfo.xml: Added; API documentation stubs. + * src/MonoFuseHelper/Makefile.am: Remove automake warning message. + +2006-09-11 Jonathan Pryor + + * example/HelloFS/HelloFS.cs: Add extended attribute support for the + `hello' file. + +2006-09-11 Jonathan Pryor + + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: + - Property renaming: s/Options/FuseOptions/g. + - Method renaming: + s/OnSetPathExtendedAttributes/OnSetPathExtendedAttribute/g; + s/OnGetPathExtendedAttributes/OnGetPathExtendedAttribute/g; + - Method argument changing: + s/OnListPathExtendedAttributes(string, byte[], out int)/ + OnListPathExtendedAttributes(string, out string[])/ + * example/HelloFS/HelloFS.cs: s/\.Options/.FuseOptions/g. + * example/HelloFS/Makefile.am: Add `-debug+' to compile line for + HelloFS.exe; add Mono.Fuse.dll.mdb for line numbers in stack traces. + * example/RedirectFS/RedirectFS.cs, example/RedirectFS/RedirectFS-FH.cs: + Update to latest OnSetPathExtendedAttribute(), + OnGetPathExtendedAttribute(), OnListPathExtendedAttributes() methods. + +2006-09-09 Jonathan Pryor + + * src/Mono.Fuse/Mono.Fuse.FileSystem.cs: + - Add new public type FileSystemEntry, which provides both a Path string + and a Mono.Unix.Native.Stat instance that can be passed to FUSE. The + Stat instance will _only_ be used if Stat.st_ino is non-zero. + - Change FileSystemOperationContext to use read-only properties instead of + fields, as this information can't be changed. Change ProcessId to `int' + instead of `long', for consistency with Mono.Posix. + - Change FileSystem.OnReadDirectory() to use IEnumerable + instead of IEnumerable. This allows sub-classes to (optionally) + provide Stat information to FUSE. (Currently FUSE will only use the + st_ino & st_mode fields of the Stat, though I imagine this may be + expanded in the future.) + * src/MonoFuseHelper/mfh.c: Use current field names in + FileSystemOperationContext. + * example/HelloFS/HelloFS.cs: + s/IEnumerable/IEnumerable/g (migrate to latest + Mono.Fuse.FileSystem.OnReadDirectory() prototype). + * example/RedirectFS/RedirectFS.cs, example/RedirectFS/RedirectFS-FH.cs: + s/IEnumerable/IEnumerable/g (migrate to latest + Mono.Fuse.FileSystem.OnReadDirectory() prototype); Fill in + FileSystemEntry.Stat for use by FUSE. + +2006-09-08 Jonathan Pryor + + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: + - Redo OnReadDirectory() implementation to use a IEnumerator instead of a + string[]. The previous approach required reading in the entire + directory all at once, which would slow things down for large + directories. The new approach allows FUSE to invoke _OnReadDirectory() + multiple times to read from the same enumerator, allowing directory + entries to be returned in "chunks" instead of all at once. + Plus, it's damn cool to use a C# iterator to implement + OnReadDirectory() (see HelloFS.cs & RedirectFS-FH.cs). + - Remove check for OnCreateHandle()/OnOpenHandle() & OnReleaseHandle() + consistency -- this broke the build (HelloFS has OnOpenHandle() but not + OnReleaseHandle(), and it doesn't need OnReleaseHandle() either). + * src/MonoFuseHelper/mfh.c: Help implement new OnReadDirectory() interface; + add mfh_invoke_filler() helper method. + * example/HelloFS/HelloFS.cs, example/RedirectFS/RedirectFS.cs, + * example/RedirectFS/RedirectFS-FH.cs: Implement updated OnReadDirectory() + interface. (Using iterators for OnReadDirectory is cool; especially + check out the new RedirectFS-FH.cs implementation.) + +2006-09-07 Jonathan Pryor + + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: + - Add initial validation for method overriding -- some methods come in + sets. For example, if OnOpenDirectory is overridden, OnCloseDirectory() + should be as well (to ensure that any handles are closed if desired). + Perform such sanity checking. + - Correct GetOperations() so that the methods it finds via Reflection + actually override the methods it thinks are being overridden. + +2006-09-07 Jonathan Pryor + + * example/HelloFS/HelloFS.cs: Update help text to show FUSE options. + * example/RedirectFS/RedirectFS.cs, example/RedirectFS/RedirectFS-FH.cs: + Update help text to show FUSE options. Alter command line arguments to be + "program mountpoint basedir" (instead of "program basedir mountpoint") so + that it matches the FUSE-generated help text. + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: Add ShowFuseHelp static method to + display FUSE help options to stderr. + * src/MonoFuseHelper/mfh.c: Add mfh_show_fuse_help() helper. + +2006-09-07 Jonathan Pryor + + * configure.in: Generate Makefile for new directory example/RedirectFS. + * src/Mono.Fuse/MapAttribute.cs: Update to latest mono-tools version. + * src/Mono.Fuse/create-native-map.exe: Update to latest mono-tools version + (removes support for MapAttribute.NativeSymbolPrefix, as this is no longer + useful since MonoPosixHelper doesn't have a public API). + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: + - Rename OpenedPathInfo.FileHandle to OpenedPathInfo.Handle (as the + handle can be used by both files & directories). + - Change ReadSymbolicLinkCb/OnReadSymbolicLink() as Mono can't pass a + StringBuilder from native to managed code (it's a managed->native + construct), so use a workable alternative. The new + ``OnReadSymbolicLink(string path, out string target)'' prototype is also + simpler for subclasses. + - Native code can't pass an OpenedPathInfo into managed code via the + delegates, as the delegates need to modify OpenedPathInfo.Handle and + have this change propogate back to native code. Ideally, this would be + fixed by making the delegates have an "[In, Out] OpenedPathInfo info" + parameter (instead of the current "IntPtr" + manual marshal approach), + but this causes Mono to die -- bugzilla 79298. This is now fixed, but + only in HEAD, so we need the ugly workaround for 1.1.13.x support. + - Add support for parsing the `-f' FUSE argument (use foreground + operation; ignored, as we can only support foreground operation). + - Don't throw Exception if MountPoint can't be mounted or mfh_fuse_new + fails, throw a NotSupportedException. (Perhaps I should use a + different type?) + - Remove some Console.WriteLine()s. + * src/MonoFuseHelper/Makefile.am: $(buildlibdir)/libMonoFuseHelper.so should + be updated when its sources are updated. + * src/MonoFuseHelper/mfh.c: + - Add Mono_Fuse_FromOpenedPathInfo and Mono_Fuse_ToOpenedPathInfo (for + struct fuse_file_info conversion), so it can be invoked by Mono.Fuse.dll. + - Remove support for fuse_file_info->OpenedPathInfo conversion from + MonoFuseHelper, this must now be done in managed code (see #79298). + - mfh_readdir: Check return value of `filler' method to check for errors. + * example/Makefile.am: Add new directory RedirectFS. + * example/RedirectFS/Makefile.am: Added; build RedirectFS programs. + * example/RedirectFS/RedirectFS.cs: Added; FS which forwards ("redirects") + all FileSystem operations to the underlying filesystem. + * example/RedirectFS/redirectfs.in: Added; runs RedirectFS.exe. + Syntax: redirectfs BASEDIR MOUNT-POINT + e.g. ./redirects /tmp t + * example/RedirectFS/RedirectFS-FH.cs: Added; FS which forwards ("redirects") + all FileSystem operations to the underlying filesystem. + This uses OpenedPathInfo.Handle to track file & directory handles instead + of re-opening files & directories all the time (compare to RedirectFS.cs). + * example/RedirectFS/redirectfs-fh.in: Added; runs RedirectFS-FH.exe. + Syntax: redirectfs-fh BASEDIR MOUNT-POINT + e.g. ./redirects-fh /tmp t + +2006-09-05 Jonathan Pryor + + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: Mass API renaming for consistency. + - s/OpenedFileInfo/OpenedPathInfo/g (for consistency with method naming + convention, below, as it's used with both files & directories). + - Remove FileSystemOperationContext.PrivateData (it's used internally). + - Rename properties: s/EnableDebugOutput/EnableFuseDebugOutput/. + - Add MultiThreaded property (default true), remove StartMultithreaded() + method. Start() checks the MultiThreaded property and calls the + appropriate FUSE method for (non-) multithreaded use. + - Make OpenedPathInfo parameter 2nd parameter instead of the (variable) + last parameter (so that `out' parameters can consistently be the last + parameter and paramater placement is consistent). + - Change method names for consistency, ease understanding, and help the + impending documentation effort to include one of the following words: + - Directory: Method deals with directories and not files. + - OnCreateDirectory(), OnRemoveDirectory(), OnOpenDirectory(), + OnReadDirectory(), OnCloseDirectory(), OnSynchronizeDirectory(). + - File: Method deals with files and not directories. + - OnCreateSpecialFile(), OnRemoveFile(), OnTruncateFile(). + - FileSystem: Method deals with the file system as a whole. + - OnGetFileSystemStatus(). + - Link: Method deals with actual links (symbolic & hard links) + - OnCreateHardLink(), OnCreateSymbolicLink(), OnReadSymbolicLink(). + - Handle: Method deals with a "handle" to an opened file. + - OnCreateHandle(), OnOpenHandle(), OnReadHandle(), OnWriteHandle(), + OnFlushHandle(), OnReleaseHandle(), OnSynchronizeHandle(), + OnTruncateHandle(), OnGetHandleStatus(). + - Path: Method works on files and directories. + - OnAccessPath(), OnGetPathStatus(), OnRenamePath(), + OnChangePathPermissions(), OnChangePathOwner(), OnChangePathTimes(), + OnGetPathExtendedAttributes(), OnListPathExtendedAttributes(), + OnSetPathExtendedAttributes(), OnRemovePathExtendedAttribute(). + * src/MonoFuseHelper/mfh.c: Deal with s/OpenedFileInfo/OpenedPathInfo/g. + * example/HelloFS/HelloFS.cs: Deal with FileSystem method name changes. + +2006-09-03 Jonathan Pryor + + * COPYING: Specify license as MIT/X11. + * example/HelloFS/HelloFS.cs, src/Mono.Fuse/FileSystem.cs, + src/MonoFuseHelper/mfh.c, src/MonoFuseHelper/mph.c: Add license. + +2006-09-01 Jonathan Pryor + + * README: Add note about using fusermount to unmount FUSE filesystems. + +2006-09-01 Jonathan Pryor + + * configure.in: Set required Mono version is 1.1.13 (maybe could be + lowered? No idea); add check for presence of Mono_Posix_ToStat in + MonoPosixHelper -- if found, then we should have the new + Mono.Unix.Native.NativeConvert.Copy methods, otherwise we need to roll our + own equivalents. This allows portability to "down-level" mono versions. + * src/Mono.Fuse/create-native-map.exe: Update; this version doesn't skip + prototypes for structs within a different assembly, it generates struct + declarations for all structs referenced (needed to get struct decls for + Stat, Statvfs, etc.) + * src/Mono.Fuse/Makefile.am: Add --rename-member arguments to + create-native-map call so Stat can be used properly. + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: Only use NativeConvert.Copy methods + if HAVE_MONO_UNIX_NATIVE_COPY_FUNCS is defined, otherwise use the + MonoFuseHelper internal methods. + * src/MonoFuseHelper/Makefile.am: Add mph.c to the build. + * src/MonoFuseHelper/mph.c: Added; structure copy functions from + MonoPosixHelper for copying Stat, Statvfs, & Utimbuf between managed & + native code. + +2006-09-01 Jonathan Pryor + + * README: Add comments about hellofs command-line arguments, capturing trace + messages, and FUSE performance timing. + * example/HelloFS/hellofs.in: Modify setting of LD_LIBRARY_PATH so that it + retains the existing $LD_LIBRARY_PATH value. Remove -odebug parameter. + If you want "full" trace output, set MONO_TRACE_LISTENER for the managed + trace output, and add -odebug for FUSE trace output, e.g. + MONO_TRACE_LISTENER=Console.Out:### ./hellofs -odebug mount-point + +2006-08-31 Jonathan Pryor + + * src/Mono.Fuse/Makefile.am: Remove dependency on external + create-native-map.exe and a public Mono.Unix.Native.MapAttribute (as the + latter will no longer be public). Add `update-map-source' target which + uses pkg-config to copy the "reference" MapAttribute.cs & + create-native-map.exe programs from their installed locations. + * src/Mono.Fuse/create-native-map.exe, src/Mono.Fuse/MapAttribute.cs: Added; + cached versions of create-native-map.exe & MapAttribute.cs as provided by + pkg-config and the create-native-map.pc file. + +2006-08-31 Jonathan Pryor + + * configure.in: Remove use of mono-posix-helper.pc, and use glib-2.0 + instead. + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: Change the delegates so that they + use only native types, not managed types. Change all of the _On* + methods to do the native->managed conversion for parameters and the return + value (depends upon addition of Mono.Unix.Native.NativConvert.Copy() + methods to perform structure conversion). Make OpenedFileInfo.flags + internal, so that FileSystem can convert the value when necessary, + as MonoFuseHelper can't do the enum convert. + * src/MonoFuseHelper/Makefile.am: Remove use of MPH, and add GLIB flags. + * src/MonoFuseHelper/mfh.c: Remove use of MonoPosixHelper functions (as + Mono.Fuse.FileSystem does the type conversions now). Don't copy-out + OpenedFileInfo.flags, as this enum must be converted in managed, and I + don't see much point in having another conversion on exit when I can just + skip the copy-out (thus preserving the original value). + +2006-08-31 Jonathan Pryor + + * example/HelloFS/HelloFS.cs: Make creation of data_im_str thread-safe, so + that it's only created once and reading threads don't see an intermediate + state. + +2006-08-28 Jonathan Pryor + + * example/HelloFS/HelloFS.cs: Rename the ``data'' file to ``data.im'' (this + is a text file with 100MB of data), and replace ``data'' with a file which + computes its content at runtime (a time/space tradeoff). The ``data.im'' + file isn't available by default; pass --data.im-in-memory to make it + available (note that this file will be created in-memory the first time + its content is accessed). Add command-line argument parsing. Use + Trace.WriteLine instead of Console.WriteLine. + * lib/pkgconfig/mono-fuse.pc.in: Use @libdir@ instead of + ${prefix}/@reloc_libdir@. I'm not sure what @reloc_libdir@ is supposed to + be, and it isn't currently expanded anyway. + * src/Mono.Fuse/Makefile.am: Add the -d:TRACE define. Add --public-macro + and --impl-header to the create-native-map call (create-native-map no + longer supports MapHeaderAttribute). + * src/Mono.Fuse/Mono.Fuse/FileSystem.cs: Remove use of + Mono.Unix.Native.MapHeaderAttribute (use create-native-map arguments + instead). Add public argument parsing support (so that HelloFS can have + its own command-line args in addition to the FUSE args). Use Trace + instead of Console for trace messages. Add a `catch(Exception)' handler + so we can trace exception messages (helps debugging). + +2006-08-25 Jonathan Pryor + + * src/MonoFuseHelper/mfh.c: Remove debug printf statements. + +2006-08-24 Jonathan Pryor + + * Makefile.am: Build the `lib' directory. + * lib/mono-fuse/Makefile.am: Don't install anything; + src/Mono.Fuse/Makefile.am already handles Mono.Fuse.dll installation. + +2006-08-24 Jonathan Pryor + + * configure.in: Don't forget to generate lib/mono-fuse/Makefile! + +2006-08-24 Jonathan Pryor + + * lib/Makefile.am: Build the mono-fuse directory as well + * lib/mono-fuse/Makefile.am: Added; installs Mono.Fuse.dll. + +2006-08-24 Jonathan Pryor + + * NEWS: Added + * configure.in: Use AM_PROG_LIBTOOL, not AC_PROG_LIBTOOL, to satisfy + autogen.sh's checking. + +2006-08-24 Jonathan Pryor + + * AUTHORS, COPYING, README: Added (for autoconf) + * autogen.sh, configure.in, Makefile.am, example/Makefile.am, + example/HelloFS/Makefile.am, lib/Makefile.am, lib/pkgconfig/Makefile.am, + src/Makefile.am, src/Mono.Fuse/Makefile.am, + src/MonoFuseHelper/Makefile.am: Added (auto* build system files). + * lib/map.c, lib/map.h: Removed (generated by the build; no need to keep + these under version control). + * lib/mfh.c: Renamed to src/MonoFuseHelper/mfh.c + * lib/mph.c: Removed. (Functions added to MonoPosixHelper library.) + * lib/mono-fuse/HelloFS.cs: Renamed to example/HelloFS/HelloFS.cs + * example/HelloFS/hellofs.in: Added; template for `hellofs' program. + * lib/mono-fuse/MakeMap.Test.cs, lib/mono-fuse/make-map.cs, + lib/mono-fuse/test.c.ref, lib/mono-fuse/test.cs.ref, + lib/mono-fuse/test.h.ref, lib/mono-fuse/test.xml.ref: Removed. + make-map.exe renamed to create-native-map.exe, moved into + mcs svn module under mcs/tools/create-native-map. + * lib/pkgconfig/mono-fuse.pc.in: Remove Requires line (managed code doesn't + depend on glib-2.0 or mono), add Mono.Posix.dll to Libs: line. + * lib/mono-fuse/FileSystem.cs: renamed to + src/Mono.Fuse/Mono.Fuse/FileSystem.cs + +2006-08-21 Jonathan Pryor + + * lib/map.c, lib/map.h: Flush (current make-map output for Mono.Fuse.dll). + * lib/mono-fuse/MakeMap.Test.cs: Add additional tests for copying nested + structures. + * lib/mono-fuse/make-map.cs: Add support for copying nested structures. + * lib/mono-fuse/test.c.ref, lib/mono-fuse/test.h.ref: Flush. + +2006-08-21 Jonathan Pryor + + * lib/mono-fuse/MakeMap.Test.cs: Add enum (to test enum conversion code), + class w/o namespace (generated NRE within make-map). + * lib/mono-fuse/make-map.cs: + - s/ExportPrefix/NativeSymbolPrefix/g (current Mono.Posix.dll naming); + - check for t.Namespace==null before looking using + Configuration.NamespaceRenames (Dictionary`2 doesn't like null keys); + - HeaderFileGenerator needs to know about member renames (so it uses the + renamed member in conversion code); + - Clean up enumeration generation for readablility. + - Remove dependency on (include macros within file + with a _cnm prefix). + * lib/mono-fuse/test.c.ref, lib/mono-fuse/test.cs.ref, + lib/mono-fuse/test.h.ref, lib/mono-fuse/test.xml.ref: Flush. + +2006-08-21 Jonathan Pryor + + * lib/mono-fuse/make-map.cs: Remove debug statements. + +2006-08-21 Jonathan Pryor + + * lib/mono-fuse/Makefile: Add --library when generating ../map.c, ../map.h. + * lib/mono-fuse/make-map.cs: Use Mono.Unix.Native.MapAttribute.ExportPrefix + to get the namespace of a member, in addition to --rename-namespace. This + is necessary for easier cross-assembly namespace renames -- Mono.Posix.dll + uses a Mono_Posix C namespace, but the default algorithm would be a + Mono_Unix_Native prefix. Thus, two choices for correct behavior: use + MapAttribute.ExportPrefix within Mono.Posix.dll, allowing all consumers to + get the correct prefix By Default (It Just Works), or require that all + consumers add --rename-namespace arguments (more clunky, more difficult to + diagnose, not as "version proof"). + +2006-08-20 Jonathan Pryor + + * lib/mono-fuse/make-map.cs: Add new arguments: --autoconf-member, + --exclude-native-symbol, --library, --rename-member, --rename-namespace. + These are to remove the necessity of + Mono.Unix.Native.MapAttribute.Optional (--autoconf-member) and + MapAttribute.ExportPrefix (--rename-namespace), while adding much needed + support for the DllImport libraries to generate prototypes for + (--library), symbol exclusion (to remove Mono_Posix_Stdlib_snprintf; + --exclude-native-symbol), and member renaming (curse you glibc making + st_atime a macro! See --rename-member). + * lib/mono-fuse/Makefile: Use new make-map.exe arguments to test their + behavior when building test.c/test.h. + * lib/mono-fuse/MakeMap.Test.cs: Add types/members to test new make-map.cs + arguments. + * lib/mono-fuse/test.c.ref, lib/mono-fuse/test.h.ref: Flush (new expected + +2006-08-20 Jonathan Pryor + + * lib/map.c, lib/map.h: Flush. + * lib/mono-fuse/FileSystem.cs: Use Mono.Unix.Native.MapHeaderAttribute + instead of Mono.Unix.Native.HeaderAttribute. + +2006-08-17 Jonathan Pryor + + * lib/mono-fuse/make-map.cs: Add a newline after the implementation macro section. + +2006-08-17 Jonathan Pryor + + * lib/mono-fuse/MakeMap.Test.cs: Use of + MapHeaderAttribute.ImplementationMacro and + MapHeaderAttribute.ImplementationIncludeFile. + * lib/mono-fuse/make-map.cs: Add support for + MapHeaderAttribute.Implementation* properties within the .c file. + * lib/mono-fuse/test.c.ref, lib/mono-fuse/test.h.ref: Sync with current + make-map.exe output. + +2006-08-16 Jonathan Pryor + + * lib/map.c, lib.map.h: Flush (new formatting due to make-map changes + * lib/mono-fuse/MakeMap.Test.cs: Use MapHeaderAttribute; remove + [Map("struct qux")] from Qux (can't compile this). + * lib/mono-fuse/Makefile: Improve dependencies to ensure things are rebuilt + when needed; add libtest.so target (to make sure test.c is valid C). + * lib/mono-fuse/make-map.cs: Add support for Mono.Unix.Native.MapHeaderAttribute; + formatting improvements. + * lib/mono-fuse/test.c.ref, lib/mono-fuse/test.h.ref: Flush. + +2006-08-14 Jonathan Pryor + + API Cleanup -- return values should only be for reporting errors, not for + reporting additional information (e.g. read(2) & write(2) overload the return + value; we don't want to do that, as it's inconsistent). + + * lib/map.h: Sync with mono-fuse/FileSystem.cs + * lib/mfh.c (mfh_read, mfh_write, mfh_setxattr, mfh_listxattr): Add + additional argument to get number bytes read/written from buffer. + * lib/mono-fuse/FileSystem.cs: Change ReadCb(), WriteCb(), + GetExtendedAttributesCb(), ListExtendedAttributesCb() so that return type + is Errno (consistency) and an extra `out' parameter is added to hold the + number of bytes read/written from the buffer. + * lib/mono-fuse/HelloFS.cs: Sync with latest API. + +2006-08-14 Jonathan Pryor + + Re-enable bounds checking in struct/class conversion functions; API cleanup. + + * lib/map.h: Sync with FileSystem.cs + * lib/mph.c: Add more conversion functions. + * lib/mfh.c: Fully implement all forwarding functions; remove some warnings; + fix segfault in _free_argv(). + * lib/mono-fuse/FileSystem.cs: Use properties, not public fields; add + OpenReadOnly, OpenWriteOnly, OpenReadWrite helpers (to remove ugly + ((OpenFlags) ((int) fi.Flags & 3)) == OpenFlags.O_RDONLY sillyness). Stat + and Statvfs are `out', not `ref'. Correct marshaling of byte[]s within + delegates, as some are `const' and some aren't (so no need to copy-in or + copy-out if not appropriate). Use mfh_fuse_ prefix instead of mfh_. + * lib/mono-fuse/HelloFS.cs: Use latest API. + +2006-08-09 Jonathan Pryor + + * lib/mono-fuse/FileSystem.cs: + - Remove DestroyCb() delegate, OnDestroy() method. It's not needed, as + FileSystem.Dispose() cleans up opsp, and OnDestroy() was only called + from fuse_destroy(), which is also invoked from Dispose(). + - Add wrappers for all protected virtual methods to act as a "backstop" + for exceptions. We cannot allow exceptions to enter native code from + managed code (as this would likely kill the process, with Mono's + exception handler "cleaning up" the native callstack, which can't make + FUSE happy). If an exception is emitted, EIO is returned to FUSE. + These methods can also be used to remove unnecessary parameters, such as + the `WriteCb' `size' parameter (which becomes buf.Length). + * lib/mono-fuse/HelloFS.cs: Add a `data' file, which contains 96MB of plain + text data (useful for timing analysis to see the overhead from + various forms of marshaling). Update to use new OnRead() parameters. + +2006-08-08 Jonathan Pryor + + * lib/mono-fuse/FileSystem.cs: Correct FUSE argument parsing (bad regex used). + GetFuseArgs() needs to prepend the executable name for proper FUSE operation. + We're now able to start an app and pass `-d' to get FUSE debugging messages. + +2006-08-08 Jonathan Pryor + + Flush. + +2006-08-08 Jonathan Pryor + + Get things (somewhat) working -- we're now able to read a file contents + through FUSE, but we're not able to opendir/readdir. :-/ + + * lib/Makefile: Add -g, and libMonoFuseHelper.so should link to + libMonoPosixHelper.so. + * lib/map.h: Synchronize with FileSystem.cs + * lib/mfh.c: Disable some functions so that I don't need to write as many + stub functions in lib/mph.c; fix mfh_read, mfh_write (return value + nonsense; this needs to be fixed later). + * lib/mono-fuse/FileSystem.cs: Fix prototypes of ReadCb and WriteCb + delegates, as we need [MarshalAs] declarations on the byte[] arrays so + that the marshaler knows what to do with them. Update all byte[] usage + within delegates. Mono doesn't like ``[Out] out string[] paths'' (as used + in ReadDirectoryCb()), so replace with `out IntPtr paths' and manually + marshal the string[]. + * lib/mono-fuse/Makefile: Use global Mono.Posix.dll file, not local copy; + * add `hellofs' target to run HelloFS.exe with LD_LIBRARY_PATH set. + * lib/mono-fuse/HelloFS.cs: Add C.WL helpers (for tracing); fix OnOpen() so + that we can read a file (it's O_RDONLY check was wrong); print out parsed + command line arguments (showing that FileSystem.Parse() is broken -- + that's next to fix). + * lib/mono-fuse/make-map.cs: Don't insert types into `structs' table when a + type with [Map] is found, use RecordTypes() instead (so that we record all + nested types as well). Warnings should be prefixed with "warning: ". + Don't always generate Namespace_ToType/Namespace_FromType declarations, + but only if MapAttribute.NativeType was specified. Allows for [Map] on + structures that need to be exported from an assembly but aren't used + within shared methods. + * lib/mph.c: Let this compile. + +2006-08-05 Jonathan Pryor + + Include all correct .o files into libMonoFuseHelper.so + + Use correct return types in fuse_* methods. + +2006-08-05 Jonathan Pryor + + * lib/mono-fuse/FileSystem.cs: Remove [Map] from Mono.Fuse.Operations, as it + cannot be assigned to `struct fuse_operations'. Add necessary DllImport's. + * lib/mono-fuse/HelloFS.cs: Fix to match new OnReadDirectory() prototype. + +2006-08-05 Jonathan Pryor + + * lib/mfh.c: Add implementation of mfh_readdir(). + +2006-08-05 Jonathan Pryor + + * lib/mono-fuse/FileSystem.cs: Add necessary Header attribute so that map.c/map.h are + generated sanely. + +2006-08-05 Jonathan Pryor + + Add unit test for make-map.exe, check & check-update targets to Makefile, and + expected output for unit tests. + +2006-08-02 Jonathan Pryor + + Provide implementations for most of the mfh_* functions. + Remove backup files. + +2006-08-02 Jonathan Pryor + + Build libMonoFuseHelper.so. + +2006-08-01 Jonathan Pryor + + Add files for libMonoFuseHelper.so and Makefile to build libMonoFuseHelper.so. + +2006-08-01 Jonathan Pryor + + Current Mono.Fuse managed code implementation. + +2006-07-22 Jonathan Pryor + + Initial Import. + diff --git a/CreateNativeMap/CreateNativeMap.csproj b/CreateNativeMap/CreateNativeMap.csproj new file mode 100644 index 0000000..2d1cc36 --- /dev/null +++ b/CreateNativeMap/CreateNativeMap.csproj @@ -0,0 +1,16 @@ + + + CreateNativeMap + 1.0.0 + Miguel de Icaza, Jonathan Pryor, Alexey Kolpakov + Port of mono's `create-native-map` tool to NET Standart + Exe + netcoreapp2.0 + + + + + + + + \ No newline at end of file diff --git a/CreateNativeMap/Program.cs b/CreateNativeMap/Program.cs new file mode 100644 index 0000000..17676f8 --- /dev/null +++ b/CreateNativeMap/Program.cs @@ -0,0 +1,1942 @@ +// +// create-native-map.cs: Builds a C map of constants defined on C# land +// +// Authors: +// Miguel de Icaza (miguel@novell.com) +// Jonathan Pryor (jonpryor@vt.edu) +// +// (C) 2003 Novell, Inc. +// (C) 2004-2005 Jonathan Pryor +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Globalization; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using Mono.Fuse.NETStandard; + +delegate void CreateFileHandler (string assembly_name, string file_prefix); +delegate void AssemblyAttributesHandler (Assembly assembly); +delegate void TypeHandler (Type t, string ns, string fn); +delegate void CloseFileHandler (string file_prefix); + +namespace CreateNativeMap +{ + + internal class MakeMap + { + + public static int Main(string[] args) + { + FileGenerator[] generators = new FileGenerator[] + { + new HeaderFileGenerator(), + new SourceFileGenerator(), + new ConvertFileGenerator(), + new ConvertDocFileGenerator(), + }; + + Configuration config = new Configuration(); + bool exit = false; + try + { + exit = !config.Parse(args); + } + catch (Exception e) + { + Console.WriteLine("{0}: error: {1}", + Environment.GetCommandLineArgs()[0], e.Message); + exit = true; + } + + if (exit) + { + Configuration.ShowHelp(); + return 1; + } + + MapUtils.config = config; + + MakeMap composite = new MakeMap(); + foreach (FileGenerator g in generators) + { + g.Configuration = config; + composite.FileCreators += new CreateFileHandler(g.CreateFile); + composite.AssemblyAttributesHandler += + new AssemblyAttributesHandler(g.WriteAssemblyAttributes); + composite.TypeHandler += new TypeHandler(g.WriteType); + composite.FileClosers += new CloseFileHandler(g.CloseFile); + } + + return composite.Run(config); + } + + event CreateFileHandler FileCreators; + event AssemblyAttributesHandler AssemblyAttributesHandler; + event TypeHandler TypeHandler; + event CloseFileHandler FileClosers; + + int Run(Configuration config) + { + FileCreators(config.AssemblyFileName, config.OutputPrefix); + + Assembly assembly = Assembly.LoadFrom(config.AssemblyFileName); + AssemblyAttributesHandler(assembly); + + Type[] exported_types = assembly.GetTypes(); + Array.Sort(exported_types, new TypeFullNameComparer()); + + foreach (Type t in exported_types) + { + string ns = MapUtils.GetNamespace(t); + /* + if (ns == null || !ns.StartsWith ("Mono")) + continue; + */ + string fn = MapUtils.GetManagedType(t); + + TypeHandler(t, ns, fn); + } + + FileClosers(config.OutputPrefix); + + return 0; + } + + private class TypeFullNameComparer : IComparer + { + public int Compare(Type t1, Type t2) + { + if (t1 == t2) + return 0; + if (t1 == null) + return 1; + if (t2 == null) + return -1; + return CultureInfo.InvariantCulture.CompareInfo.Compare( + t1.FullName, t2.FullName, CompareOptions.Ordinal); + } + } + } + + class Configuration + { + Dictionary renameMembers = new Dictionary(); + Dictionary renameNamespaces = new Dictionary(); + List libraries = new List(); + List optionals = new List(); + List excludes = new List(); + List iheaders = new List(); + List pheaders = new List(); + List imacros = new List(); + List pmacros = new List(); + string assembly_name; + string output; + + delegate void ArgumentHandler(Configuration c, string name, string value); + + static Dictionary handlers; + + static Configuration() + { + handlers = new Dictionary(); + handlers["autoconf-header"] = delegate(Configuration c, string name, string value) { c.iheaders.Add("ah:" + name); }; + handlers["autoconf-member"] = delegate(Configuration c, string name, string value) { c.optionals.Add(name); }; + handlers["impl-header"] = delegate(Configuration c, string name, string value) { c.iheaders.Add(name); }; + handlers["impl-macro"] = delegate(Configuration c, string name, string value) + { + if (value != null) + name += "=" + value; + c.imacros.Add(name); + }; + handlers["library"] = delegate(Configuration c, string name, string value) { c.libraries.Add(name); }; + handlers["exclude-native-symbol"] = delegate(Configuration c, string name, string value) { c.excludes.Add(name); }; + handlers["public-header"] = delegate(Configuration c, string name, string value) { c.pheaders.Add(name); }; + handlers["public-macro"] = delegate(Configuration c, string name, string value) + { + if (value != null) + name += "=" + value; + c.pmacros.Add(name); + }; + handlers["rename-member"] = delegate(Configuration c, string name, string value) + { + if (value == null) + { + throw new Exception("missing rename value"); + } + + c.renameMembers[name] = value; + }; + handlers["rename-namespace"] = delegate(Configuration c, string name, string value) + { + if (value == null) + { + throw new Exception("missing rename value"); + } + + value = value.Replace(".", "_"); + c.renameNamespaces[name] = value; + }; + } + + public Configuration() + { + } + + public List NativeLibraries + { + get { return libraries; } + } + + public List AutoconfMembers + { + get { return optionals; } + } + + public List NativeExcludeSymbols + { + get { return excludes; } + } + + public List PublicHeaders + { + get { return pheaders; } + } + + public List PublicMacros + { + get { return pmacros; } + } + + public List ImplementationHeaders + { + get { return iheaders; } + } + + public List ImplementationMacros + { + get { return imacros; } + } + + public IDictionary MemberRenames + { + get { return renameMembers; } + } + + public IDictionary NamespaceRenames + { + get { return renameNamespaces; } + } + + public string AssemblyFileName + { + get { return assembly_name; } + } + + public string OutputPrefix + { + get { return output; } + } + + const string NameValue = @"(?[^=]+)(=(?.*))?"; + const string Argument = @"^--(?[\w-]+)([=:]" + NameValue + ")?$"; + + public bool Parse(string[] args) + { + Regex argRE = new Regex(Argument); + Regex valRE = new Regex(NameValue); + + for (int i = 0; i < args.Length; ++i) + { + Match m = argRE.Match(args[i]); + if (m.Success) + { + string arg = m.Groups["Argument"].Value; + if (arg == "help") + return false; + if (!m.Groups["Name"].Success) + { + if ((i + 1) >= args.Length) + throw new Exception( + string.Format("missing value for argument {0}", args[i])); + m = valRE.Match(args[++i]); + if (!m.Success) + { + throw new Exception( + string.Format("invalid value for argument {0}: {1}", + args[i - 1], args[i])); + } + } + + string name = m.Groups["Name"].Value; + string value = m.Groups["Value"].Success ? m.Groups["Value"].Value : null; + if (handlers.ContainsKey(arg)) + { + handlers[arg](this, name, value); + } + else + { + throw new Exception("invalid argument " + arg); + } + } + else if (assembly_name == null) + { + assembly_name = args[i]; + } + else + { + output = args[i]; + } + } + + if (assembly_name == null) + throw new Exception("missing ASSEMBLY"); + if (output == null) + throw new Exception("missing OUTPUT-PREFIX"); + + libraries.Sort(); + optionals.Sort(); + excludes.Sort(); + + return true; + } + + public static void ShowHelp() + { + Console.WriteLine( + "Usage: create-native-map \n" + + "\t[--autoconf-header=HEADER]* \n" + + "\t[--autoconf-member=MEMBER]* \n" + + "\t[--exclude-native-symbol=SYMBOL]*\n" + + "\t[--impl-header=HEADER]* \n" + + "\t[--impl-macro=MACRO]* \n" + + "\t[--library=LIBRARY]+ \n" + + "\t[--public-header=HEADER]* \n" + + "\t[--public-macro=MACRO]* \n" + + "\t[--rename-member=FROM=TO]* \n" + + "\t[--rename-namespace=FROM=TO]*\n" + + "\tASSEMBLY OUTPUT-PREFIX" + ); + } + } + + static class MapUtils + { + internal static Configuration config; + + public static T GetCustomAttribute(MemberInfo element) where T : Attribute + { + return (T) Attribute.GetCustomAttribute(element, typeof(T), true); + } + + public static T GetCustomAttribute(Assembly assembly) where T : Attribute + { + return (T) Attribute.GetCustomAttribute(assembly, typeof(T), true); + } + + public static T[] GetCustomAttributes(MemberInfo element) where T : Attribute + { + return (T[]) Attribute.GetCustomAttributes(element, typeof(T), true); + } + + public static T[] GetCustomAttributes(Assembly assembly) where T : Attribute + { + return (T[]) Attribute.GetCustomAttributes(assembly, typeof(T), true); + } + + public static MapAttribute GetMapAttribute(ICustomAttributeProvider element) + { + foreach (object o in element.GetCustomAttributes(true)) + { + if (!IsMapAttribute(o)) + continue; + string nativeType = GetPropertyValueAsString(o, "NativeType"); + MapAttribute map = nativeType == null + ? new MapAttribute() + : new MapAttribute(nativeType); + map.SuppressFlags = GetPropertyValueAsString(o, "SuppressFlags"); + return map; + } + + return null; + } + + private static bool IsMapAttribute(object o) + { + Type t = o.GetType(); + do + { + if (t.Name == "MapAttribute") + { + return true; + } + + t = t.BaseType; + } while (t != null); + + return false; + } + + private static string GetPropertyValueAsString(object o, string property) + { + object v = GetPropertyValue(o, property); + string s = v == null ? null : v.ToString(); + if (s != null) + return s.Length == 0 ? null : s; + return null; + } + + private static object GetPropertyValue(object o, string property) + { + PropertyInfo p = o.GetType().GetProperty(property); + if (p == null) + return null; + if (!p.CanRead) + return null; + return p.GetValue(o, new object[0]); + } + + public static bool IsIntegralType(Type t) + { + return t == typeof(byte) || t == typeof(sbyte) || t == typeof(char) || + t == typeof(short) || t == typeof(ushort) || + t == typeof(int) || t == typeof(uint) || + t == typeof(long) || t == typeof(ulong); + } + + public static bool IsBlittableType(Type t) + { + return IsIntegralType(t) || t == typeof(IntPtr) || t == typeof(UIntPtr); + } + + public static string GetNativeType(Type t) + { + Type et = GetElementType(t); + string ut = et.Name; + if (et.IsEnum) + ut = Enum.GetUnderlyingType(et).Name; + + string type = null; + + switch (ut) + { + case "Boolean": + type = "int"; + break; + case "Byte": + type = "unsigned char"; + break; + case "SByte": + type = "signed char"; + break; + case "Int16": + type = "short"; + break; + case "UInt16": + type = "unsigned short"; + break; + case "Int32": + type = "int"; + break; + case "UInt32": + type = "unsigned int"; + break; + case "Int64": + type = "gint64"; + break; + case "UInt64": + type = "guint64"; + break; + case "IntPtr": + type = "void*"; + break; + case "UIntPtr": + type = "void*"; + break; + case "String": + type = "const char"; + break; /* ref type */ + case "StringBuilder": + type = "char"; + break; /* ref type */ + case "Void": + type = "void"; + break; + case "HandleRef": + type = "void*"; + break; + } + + bool isDelegate = IsDelegate(t); + if (type == null) + type = isDelegate ? t.Name : GetStructName(t); + if (!et.IsValueType && !isDelegate) + { + type += "*"; + } + + while (t.HasElementType) + { + t = t.GetElementType(); + type += "*"; + } + + return type; + //return (t.IsByRef || t.IsArray || (!t.IsValueType && !isDelegate)) ? type + "*" : type; + } + + public static bool IsDelegate(Type t) + { + return typeof(Delegate).IsAssignableFrom(t); + } + + private static string GetStructName(Type t) + { + t = GetElementType(t); + return "struct " + GetManagedType(t); + } + + public static Type GetElementType(Type t) + { + while (t.HasElementType) + { + t = t.GetElementType(); + } + + return t; + } + + public static string GetNamespace(Type t) + { + if (t.Namespace == null) + return ""; + if (config.NamespaceRenames.ContainsKey(t.Namespace)) + return config.NamespaceRenames[t.Namespace]; + return t.Namespace.Replace('.', '_'); + } + + public static string GetManagedType(Type t) + { + string ns = GetNamespace(t); + string tn = + (t.DeclaringType != null ? t.DeclaringType.Name + "_" : "") + t.Name; + return ns + "_" + tn; + } + + public static string GetNativeType(FieldInfo field) + { + MapAttribute map = + GetMapAttribute(field) + ?? + GetMapAttribute(field.FieldType); + if (map != null) + return map.NativeType; + return null; + } + + public static string GetFunctionDeclaration(string name, MethodInfo method) + { + StringBuilder sb = new StringBuilder(); +#if false + Console.WriteLine (t); + foreach (object o in t.GetMembers ()) + Console.WriteLine ("\t" + o); +#endif + sb.Append(method.ReturnType == typeof(string) + ? "char*" + : MapUtils.GetNativeType(method.ReturnType)); + sb.Append(" ").Append(name).Append(" ("); + + + ParameterInfo[] parameters = method.GetParameters(); + if (parameters.Length == 0) + { + sb.Append("void"); + } + else + { + if (parameters.Length > 0) + { + WriteParameterDeclaration(sb, parameters[0]); + } + + for (int i = 1; i < parameters.Length; ++i) + { + sb.Append(", "); + WriteParameterDeclaration(sb, parameters[i]); + } + } + + sb.Append(")"); + return sb.ToString(); + } + + private static void WriteParameterDeclaration(StringBuilder sb, ParameterInfo pi) + { + // DumpTypeInfo (pi.ParameterType); + string nt = GetNativeType(pi.ParameterType); + sb.AppendFormat("{0} {1}", nt, pi.Name); + } + + internal class _MemberNameComparer : IComparer, IComparer + { + public int Compare(FieldInfo m1, FieldInfo m2) + { + return Compare((MemberInfo) m1, (MemberInfo) m2); + } + + public int Compare(MemberInfo m1, MemberInfo m2) + { + if (m1 == m2) + return 0; + if (m1 == null) + return 1; + if (m2 == null) + return -1; + return CultureInfo.InvariantCulture.CompareInfo.Compare( + m1.Name, m2.Name, CompareOptions.Ordinal); + } + } + + private class _OrdinalStringComparer : IComparer + { + public int Compare(string s1, string s2) + { + if (object.ReferenceEquals(s1, s2)) + return 0; + if (s1 == null) + return 1; + if (s2 == null) + return -1; + return CultureInfo.InvariantCulture.CompareInfo.Compare(s1, s2, + CompareOptions.OrdinalIgnoreCase); + } + } + + internal static _MemberNameComparer MemberNameComparer = new _MemberNameComparer(); + internal static IComparer OrdinalStringComparer = new _OrdinalStringComparer(); + } + + abstract class FileGenerator + { + private Configuration config; + + public Configuration Configuration + { + get { return config; } + set { config = value; } + } + + public abstract void CreateFile(string assembly_name, string file_prefix); + + public virtual void WriteAssemblyAttributes(Assembly assembly) + { + } + + public abstract void WriteType(Type t, string ns, string fn); + public abstract void CloseFile(string file_prefix); + + protected static void WriteHeader(StreamWriter s, string assembly) + { + WriteHeader(s, assembly, false); + } + + protected static void WriteHeader(StreamWriter s, string assembly, bool noConfig) + { + s.WriteLine( + "/*\n" + + " * This file was automatically generated by create-native-map from {0}.\n" + + " *\n" + + " * DO NOT MODIFY.\n" + + " */", + assembly); + if (!noConfig) + { + s.WriteLine("#ifdef HAVE_CONFIG_H"); + s.WriteLine("#include "); + s.WriteLine("#endif /* ndef HAVE_CONFIG_H */"); + } + + s.WriteLine(); + } + + protected static bool CanMapType(Type t) + { + return MapUtils.GetMapAttribute(t) != null; + } + + protected static bool IsFlagsEnum(Type t) + { + return t.IsEnum && + MapUtils.GetCustomAttributes(t).Length > 0; + } + + protected static void SortFieldsInOffsetOrder(Type t, FieldInfo[] fields) + { + Array.Sort(fields, delegate(FieldInfo f1, FieldInfo f2) + { + long o1 = (long) Marshal.OffsetOf(f1.DeclaringType, f1.Name); + long o2 = (long) Marshal.OffsetOf(f2.DeclaringType, f2.Name); + return o1.CompareTo(o2); + }); + } + + protected static void WriteMacroDefinition(TextWriter writer, string macro) + { + if (macro == null || macro.Length == 0) + return; + string[] val = macro.Split('='); + writer.WriteLine("#ifndef {0}", val[0]); + writer.WriteLine("#define {0}{1}", val[0], + val.Length > 1 ? " " + val[1] : ""); + writer.WriteLine("#endif /* ndef {0} */", val[0]); + writer.WriteLine(); + } + + private static Regex includeRegex = new Regex(@"^(?ah:)?(?(""|<)(?.*)(""|>))$"); + + protected static void WriteIncludeDeclaration(TextWriter writer, string inc) + { + if (inc == null || inc.Length == 0) + return; + Match m = includeRegex.Match(inc); + if (!m.Groups["Include"].Success) + { + Console.WriteLine("warning: invalid PublicIncludeFile: {0}", inc); + return; + } + + if (m.Success && m.Groups["AutoHeader"].Success) + { + string i = m.Groups["IncludeFile"].Value; + string def = "HAVE_" + i.ToUpper().Replace("/", "_").Replace(".", "_"); + writer.WriteLine("#ifdef {0}", def); + writer.WriteLine("#include {0}", m.Groups["Include"]); + writer.WriteLine("#endif /* ndef {0} */", def); + } + else + writer.WriteLine("#include {0}", m.Groups["Include"]); + } + + protected string GetNativeMemberName(FieldInfo field) + { + if (!Configuration.MemberRenames.ContainsKey(field.Name)) + return field.Name; + return Configuration.MemberRenames[field.Name]; + } + } + + class HeaderFileGenerator : FileGenerator + { + StreamWriter sh; + string assembly_file; + Dictionary methods = new Dictionary(); + Dictionary structs = new Dictionary(); + Dictionary delegates = new Dictionary(); + List decls = new List(); + + public override void CreateFile(string assembly_name, string file_prefix) + { + sh = File.CreateText(file_prefix + ".h"); + file_prefix = file_prefix.Replace("../", "").Replace("/", "_"); + this.assembly_file = assembly_name = Path.GetFileName(assembly_name); + WriteHeader(sh, assembly_name, true); + assembly_name = assembly_name.Replace(".dll", "").Replace(".", "_"); + sh.WriteLine("#ifndef INC_" + assembly_name + "_" + file_prefix + "_H"); + sh.WriteLine("#define INC_" + assembly_name + "_" + file_prefix + "_H\n"); + sh.WriteLine("#include \n"); + sh.WriteLine("G_BEGIN_DECLS\n"); + + // Kill warning about unused method + DumpTypeInfo(null); + } + + public override void WriteAssemblyAttributes(Assembly assembly) + { + sh.WriteLine("/*\n * Public Macros\n */"); + foreach (string def in Configuration.PublicMacros) + { + WriteMacroDefinition(sh, def); + } + + sh.WriteLine(); + + sh.WriteLine("/*\n * Public Includes\n */"); + foreach (string inc in Configuration.PublicHeaders) + { + WriteIncludeDeclaration(sh, inc); + } + + sh.WriteLine(); + + sh.WriteLine("/*\n * Enumerations\n */"); + } + + public override void WriteType(Type t, string ns, string fn) + { + WriteEnum(t, ns, fn); + CacheStructs(t, ns, fn); + CacheExternalMethods(t, ns, fn); + } + + private void WriteEnum(Type t, string ns, string fn) + { + if (!CanMapType(t) || !t.IsEnum) + return; + + string etype = MapUtils.GetNativeType(t); + + WriteLiteralValues(sh, t, fn); + sh.WriteLine("int {1}_From{2} ({0} x, {0} *r);", etype, ns, t.Name); + sh.WriteLine("int {1}_To{2} ({0} x, {0} *r);", etype, ns, t.Name); + Configuration.NativeExcludeSymbols.Add( + string.Format("{1}_From{2}", etype, ns, t.Name)); + Configuration.NativeExcludeSymbols.Add( + string.Format("{1}_To{2}", etype, ns, t.Name)); + Configuration.NativeExcludeSymbols.Sort(); + sh.WriteLine(); + } + + static void WriteLiteralValues(StreamWriter sh, Type t, string n) + { + object inst = Activator.CreateInstance(t); + int max_field_length = 0; + FieldInfo[] fields = t.GetFields(); + Array.Sort(fields, delegate(FieldInfo f1, FieldInfo f2) + { + max_field_length = Math.Max(max_field_length, f1.Name.Length); + max_field_length = Math.Max(max_field_length, f2.Name.Length); + return MapUtils.MemberNameComparer.Compare(f1, f2); + }); + max_field_length += 1 + n.Length; + sh.WriteLine("enum {0} {{", n); + foreach (FieldInfo fi in fields) + { + if (!fi.IsLiteral) + continue; + string e = n + "_" + fi.Name; + sh.WriteLine("\t{0,-" + max_field_length + "} = 0x{1:x},", + e, fi.GetValue(inst)); + sh.WriteLine("\t#define {0,-" + max_field_length + "} {0}", e); + } + + sh.WriteLine("};"); + } + + + private void CacheStructs(Type t, string ns, string fn) + { + if (t.IsEnum) + return; + MapAttribute map = MapUtils.GetMapAttribute(t); + if (map != null) + { + if (map.NativeType != null && map.NativeType.Length > 0) + decls.Add(map.NativeType); + RecordTypes(t); + } + } + + private void CacheExternalMethods(Type t, string ns, string fn) + { + BindingFlags bf = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + foreach (MethodInfo m in t.GetMethods(bf)) + { + if ((m.Attributes & MethodAttributes.PinvokeImpl) == 0) + continue; + DllImportAttribute dia = GetDllImportInfo(m); + if (dia == null) + { + Console.WriteLine("warning: unable to emit native prototype for P/Invoke " + + "method: {0}", m); + continue; + } + + // we shouldn't declare prototypes for POSIX, etc. functions. + if (Configuration.NativeLibraries.BinarySearch(dia.Value) < 0 || + IsOnExcludeList(dia.EntryPoint)) + continue; + methods[dia.EntryPoint] = m; + RecordTypes(m); + } + } + + private static DllImportAttribute GetDllImportInfo(MethodInfo method) + { + // .NET 2.0 synthesizes pseudo-attributes such as DllImport + DllImportAttribute dia = MapUtils.GetCustomAttribute(method); + if (dia != null) + return dia; + + // We're not on .NET 2.0; assume we're on Mono and use some internal + // methods... + Type MonoMethod = Type.GetType("System.Reflection.MonoMethod", false); + if (MonoMethod == null) + { + Console.WriteLine("warning: cannot find MonoMethod"); + return null; + } + + MethodInfo GetDllImportAttribute = + MonoMethod.GetMethod("GetDllImportAttribute", + BindingFlags.Static | BindingFlags.NonPublic); + if (GetDllImportAttribute == null) + { + Console.WriteLine("warning: cannot find GetDllImportAttribute"); + return null; + } + + IntPtr mhandle = method.MethodHandle.Value; + return (DllImportAttribute) GetDllImportAttribute.Invoke(null, + new object[] {mhandle}); + } + + private bool IsOnExcludeList(string method) + { + int idx = Configuration.NativeExcludeSymbols.BinarySearch(method); + return (idx < 0) ? false : true; + } + + private void RecordTypes(MethodInfo method) + { + ParameterInfo[] parameters = method.GetParameters(); + foreach (ParameterInfo pi in parameters) + { + RecordTypes(pi.ParameterType); + } + } + + private void RecordTypes(Type st) + { + if (typeof(Delegate).IsAssignableFrom(st) && !delegates.ContainsKey(st.Name)) + { + MethodInfo mi = st.GetMethod("Invoke"); + delegates[st.Name] = mi; + RecordTypes(mi); + return; + } + + Type et = MapUtils.GetElementType(st); + string s = MapUtils.GetNativeType(et); + if (s.StartsWith("struct ") && !structs.ContainsKey(et.FullName)) + { + structs[et.FullName] = et; + foreach (FieldInfo fi in et.GetFields(BindingFlags.Instance | + BindingFlags.Public | BindingFlags.NonPublic)) + { + RecordTypes(fi.FieldType); + } + } + } + + public override void CloseFile(string file_prefix) + { + IEnumerable structures = Sort(structs.Keys); + sh.WriteLine(); + sh.WriteLine("/*\n * Managed Structure Declarations\n */\n"); + foreach (string s in structures) + { + sh.WriteLine("struct {0};", MapUtils.GetManagedType(structs[s])); + } + + sh.WriteLine(); + + sh.WriteLine("/*\n * Inferred Structure Declarations\n */\n"); + foreach (string s in decls) + { + sh.WriteLine("{0};", s); + } + + sh.WriteLine(); + + sh.WriteLine("/*\n * Delegate Declarations\n */\n"); + foreach (string s in Sort(delegates.Keys)) + { + sh.WriteLine("typedef {0};", + MapUtils.GetFunctionDeclaration("(*" + s + ")", delegates[s])); + } + + sh.WriteLine(); + + sh.WriteLine("/*\n * Structures\n */\n"); + foreach (string s in structures) + { + WriteStructDeclarations(s); + } + + sh.WriteLine(); + + sh.WriteLine("/*\n * Functions\n */"); + foreach (string method in Configuration.NativeExcludeSymbols) + { + if (methods.ContainsKey(method)) + methods.Remove(method); + } + + foreach (string method in Sort(methods.Keys)) + { + WriteMethodDeclaration((MethodInfo) methods[method], method); + } + + sh.WriteLine("\nG_END_DECLS\n"); + sh.WriteLine("#endif /* ndef INC_Mono_Posix_" + file_prefix + "_H */\n"); + sh.Close(); + } + + private static IEnumerable Sort(ICollection c) + { + List al = new List(c); + al.Sort(MapUtils.OrdinalStringComparer); + return al; + } + + private void WriteStructDeclarations(string s) + { + Type t = structs[s]; +#if false + if (!t.Assembly.CodeBase.EndsWith (this.assembly_file)) { + return; + } +#endif + sh.WriteLine("struct {0} {{", MapUtils.GetManagedType(t)); + FieldInfo[] fields = t.GetFields(BindingFlags.Instance | + BindingFlags.Public | BindingFlags.NonPublic); + int max_type_len = 0, max_name_len = 0, max_native_len = 0; + Array.ForEach(fields, delegate(FieldInfo f) + { + max_type_len = Math.Max(max_type_len, HeaderFileGenerator.GetType(f.FieldType).Length); + max_name_len = Math.Max(max_name_len, GetNativeMemberName(f).Length); + string native_type = MapUtils.GetNativeType(f); + if (native_type != null) + max_native_len = Math.Max(max_native_len, native_type.Length); + }); + SortFieldsInOffsetOrder(t, fields); + foreach (FieldInfo field in fields) + { + string fname = GetNativeMemberName(field); + sh.Write("\t{0,-" + max_type_len + "} {1};", + GetType(field.FieldType), fname); + string native_type = MapUtils.GetNativeType(field); + if (native_type != null) + { + sh.Write(new string(' ', max_name_len - fname.Length)); + sh.Write(" /* {0,-" + max_native_len + "} */", native_type); + } + + sh.WriteLine(); + } + + sh.WriteLine("};"); + MapAttribute map = MapUtils.GetMapAttribute(t); + if (map != null && map.NativeType != null && map.NativeType.Length != 0 && + t.Assembly.CodeBase.EndsWith(this.assembly_file)) + { + sh.WriteLine(); + sh.WriteLine( + "int\n{0}_From{1} ({3}{4} from, {2} *to);\n" + + "int\n{0}_To{1} ({2} *from, {3}{4} to);\n", + MapUtils.GetNamespace(t), t.Name, map.NativeType, + MapUtils.GetNativeType(t), t.IsValueType ? "*" : ""); + Configuration.NativeExcludeSymbols.Add( + string.Format("{0}_From{1}", MapUtils.GetNamespace(t), t.Name)); + Configuration.NativeExcludeSymbols.Add( + string.Format("{0}_To{1}", MapUtils.GetNamespace(t), t.Name)); + Configuration.NativeExcludeSymbols.Sort(); + } + + sh.WriteLine(); + } + + private static string GetType(Type t) + { + if (typeof(Delegate).IsAssignableFrom(t)) + return t.Name; + return MapUtils.GetNativeType(t); + } + + private void WriteMethodDeclaration(MethodInfo method, string entryPoint) + { + if (method.ReturnType.IsClass) + { + Console.WriteLine("warning: {0} has a return type of {1}, which is a reference type", + entryPoint, method.ReturnType.FullName); + } + + sh.Write(MapUtils.GetFunctionDeclaration(entryPoint, method)); + sh.WriteLine(";"); + } + + private void DumpTypeInfo(Type t) + { + if (t == null) + return; + + sh.WriteLine("\t\t/* Type Info for " + t.FullName + ":"); + foreach (MemberInfo mi in typeof(Type).GetMembers()) + { + sh.WriteLine("\t\t\t{0}={1}", mi.Name, GetMemberValue(mi, t)); + } + + sh.WriteLine("\t\t */"); + } + + private static string GetMemberValue(MemberInfo mi, Type t) + { + try + { + switch (mi.MemberType) + { + case MemberTypes.Constructor: + case MemberTypes.Method: + { + MethodBase b = (MethodBase) mi; + if (b.GetParameters().Length == 0) + return b.Invoke(t, new object[] { }).ToString(); + return "<>"; + } + case MemberTypes.Field: + return ((FieldInfo) mi).GetValue(t).ToString(); + case MemberTypes.Property: + { + PropertyInfo pi = (PropertyInfo) mi; + if (!pi.CanRead) + return "<>"; + return pi.GetValue(t, null).ToString(); + } + default: + return "<>"; + } + } + catch (Exception e) + { + return "<>"; + } + } + } + + class SourceFileGenerator : FileGenerator + { + StreamWriter sc; + string file_prefix; + + public override void CreateFile(string assembly_name, string file_prefix) + { + sc = File.CreateText(file_prefix + ".c"); + WriteHeader(sc, assembly_name); + + if (file_prefix.IndexOf("/") != -1) + file_prefix = file_prefix.Substring(file_prefix.IndexOf("/") + 1); + this.file_prefix = file_prefix; + sc.WriteLine("#include "); + sc.WriteLine("#include "); + sc.WriteLine(); + } + + public override void WriteAssemblyAttributes(Assembly assembly) + { + sc.WriteLine("/*\n * Implementation Macros\n */"); + foreach (string def in Configuration.ImplementationMacros) + { + WriteMacroDefinition(sc, def); + } + + sc.WriteLine(); + + sc.WriteLine("/*\n * Implementation Includes\n */"); + foreach (string inc in Configuration.ImplementationHeaders) + { + WriteIncludeDeclaration(sc, inc); + } + + sc.WriteLine(); + + sc.WriteLine("#include \"{0}.h\"", file_prefix); + + sc.WriteLine(@" +#include /* errno, EOVERFLOW */ +#include /* g* types, g_assert_not_reached() */"); + + WriteFallbackMacro("CNM_MININT8", "G_MININT8", sbyte.MinValue.ToString()); + WriteFallbackMacro("CNM_MAXINT8", "G_MAXINT8", sbyte.MaxValue.ToString()); + WriteFallbackMacro("CNM_MAXUINT8", "G_MAXUINT8", byte.MaxValue.ToString()); + WriteFallbackMacro("CNM_MININT16", "G_MININT16", short.MinValue.ToString()); + WriteFallbackMacro("CNM_MAXINT16", "G_MAXINT16", short.MaxValue.ToString()); + WriteFallbackMacro("CNM_MAXUINT16", "G_MAXUINT16", ushort.MaxValue.ToString()); + WriteFallbackMacro("CNM_MININT32", "G_MININT32", int.MinValue.ToString()); + WriteFallbackMacro("CNM_MAXINT32", "G_MAXINT32", int.MaxValue.ToString()); + WriteFallbackMacro("CNM_MAXUINT32", "G_MAXUINT32", uint.MaxValue.ToString() + "U"); + WriteFallbackMacro("CNM_MININT64", "G_MININT64", long.MinValue.ToString() + "LL"); + WriteFallbackMacro("CNM_MAXINT64", "G_MAXINT64", long.MaxValue.ToString() + "LL"); + WriteFallbackMacro("CNM_MAXUINT64", "G_MAXUINT64", ulong.MaxValue.ToString() + "ULL"); + + sc.WriteLine(@" + +/* returns TRUE if @type is an unsigned type */ +#define _cnm_integral_type_is_unsigned(type) \ + (sizeof(type) == sizeof(gint8) \ + ? (((type)-1) > CNM_MAXINT8) \ + : sizeof(type) == sizeof(gint16) \ + ? (((type)-1) > CNM_MAXINT16) \ + : sizeof(type) == sizeof(gint32) \ + ? (((type)-1) > CNM_MAXINT32) \ + : sizeof(type) == sizeof(gint64) \ + ? (((type)-1) > CNM_MAXINT64) \ + : (g_assert_not_reached (), 0)) + +/* returns the minimum value of @type as a gint64 */ +#define _cnm_integral_type_min(type) \ + (_cnm_integral_type_is_unsigned (type) \ + ? 0 \ + : sizeof(type) == sizeof(gint8) \ + ? CNM_MININT8 \ + : sizeof(type) == sizeof(gint16) \ + ? CNM_MININT16 \ + : sizeof(type) == sizeof(gint32) \ + ? CNM_MININT32 \ + : sizeof(type) == sizeof(gint64) \ + ? CNM_MININT64 \ + : (g_assert_not_reached (), 0)) + +/* returns the maximum value of @type as a guint64 */ +#define _cnm_integral_type_max(type) \ + (_cnm_integral_type_is_unsigned (type) \ + ? sizeof(type) == sizeof(gint8) \ + ? CNM_MAXUINT8 \ + : sizeof(type) == sizeof(gint16) \ + ? CNM_MAXUINT16 \ + : sizeof(type) == sizeof(gint32) \ + ? CNM_MAXUINT32 \ + : sizeof(type) == sizeof(gint64) \ + ? CNM_MAXUINT64 \ + : (g_assert_not_reached (), 0) \ + : sizeof(type) == sizeof(gint8) \ + ? CNM_MAXINT8 \ + : sizeof(type) == sizeof(gint16) \ + ? CNM_MAXINT16 \ + : sizeof(type) == sizeof(gint32) \ + ? CNM_MAXINT32 \ + : sizeof(type) == sizeof(gint64) \ + ? CNM_MAXINT64 \ + : (g_assert_not_reached (), 0)) + +#ifdef _CNM_DUMP +#define _cnm_dump(to_t,from) \ + printf (""# %s -> %s: uns=%i; min=%llx; max=%llx; value=%llx; lt=%i; l0=%i; gt=%i; e=%i\n"", \ + #from, #to_t, \ + (int) _cnm_integral_type_is_unsigned (to_t), \ + (gint64) (_cnm_integral_type_min (to_t)), \ + (gint64) (_cnm_integral_type_max (to_t)), \ + (gint64) (from), \ + (((gint64) _cnm_integral_type_min (to_t)) <= (gint64) from), \ + (from < 0), \ + (((guint64) from) <= (guint64) _cnm_integral_type_max (to_t)), \ + !((int) _cnm_integral_type_is_unsigned (to_t) \ + ? ((0 <= from) && \ + ((guint64) from <= (guint64) _cnm_integral_type_max (to_t))) \ + : ((gint64) _cnm_integral_type_min(to_t) <= (gint64) from && \ + (guint64) from <= (guint64) _cnm_integral_type_max (to_t))) \ + ) +#else /* ndef _CNM_DUMP */ +#define _cnm_dump(to_t, from) do {} while (0) +#endif /* def _CNM_DUMP */ + +#ifdef DEBUG +#define _cnm_return_val_if_overflow(to_t,from,val) G_STMT_START { \ + int uns = _cnm_integral_type_is_unsigned (to_t); \ + gint64 min = (gint64) _cnm_integral_type_min (to_t); \ + guint64 max = (guint64) _cnm_integral_type_max (to_t); \ + gint64 sf = (gint64) from; \ + guint64 uf = (guint64) from; \ + if (!(uns ? ((0 <= from) && (uf <= max)) \ + : (min <= sf && (from < 0 || uf <= max)))) { \ + _cnm_dump(to_t, from); \ + errno = EOVERFLOW; \ + return (val); \ + } \ + } G_STMT_END +#else /* !def DEBUG */ +/* don't do any overflow checking */ +#define _cnm_return_val_if_overflow(to_t,from,val) G_STMT_START { \ + } G_STMT_END +#endif /* def DEBUG */ +"); + } + + private void WriteFallbackMacro(string target, string glib, string def) + { + sc.WriteLine(@" +#if defined ({1}) +#define {0} {1} +#else +#define {0} ({2}) +#endif", target, glib, def); + } + + public override void WriteType(Type t, string ns, string fn) + { + if (!CanMapType(t)) + return; + + string etype = MapUtils.GetNativeType(t); + + if (t.IsEnum) + { + bool bits = IsFlagsEnum(t); + + WriteFromManagedEnum(t, ns, fn, etype, bits); + WriteToManagedEnum(t, ns, fn, etype, bits); + } + else + { + WriteFromManagedClass(t, ns, fn, etype); + WriteToManagedClass(t, ns, fn, etype); + } + } + + private void WriteFromManagedEnum(Type t, string ns, string fn, string etype, bool bits) + { + sc.WriteLine("int {1}_From{2} ({0} x, {0} *r)", etype, ns, t.Name); + sc.WriteLine("{"); + sc.WriteLine("\t*r = 0;"); + FieldInfo[] fields = t.GetFields(); + Array.Sort(fields, MapUtils.MemberNameComparer); + Array values = Enum.GetValues(t); + foreach (FieldInfo fi in fields) + { + if (!fi.IsLiteral) + continue; + if (MapUtils.GetCustomAttribute(fi) != null) + { + sc.WriteLine("\t/* {0}_{1} is obsolete or optional; ignoring */", fn, fi.Name); + continue; + } + + MapAttribute map = MapUtils.GetMapAttribute(fi); + bool is_bits = bits && (map != null ? map.SuppressFlags == null : true); + if (is_bits) + // properly handle case where [Flags] enumeration has helper + // synonyms. e.g. DEFFILEMODE and ACCESSPERMS for mode_t. + sc.WriteLine("\tif ((x & {0}_{1}) == {0}_{1})", fn, fi.Name); + else if (GetSuppressFlags(map) == null) + sc.WriteLine("\tif (x == {0}_{1})", fn, fi.Name); + else + sc.WriteLine("\tif ((x & {0}_{1}) == {0}_{2})", fn, map.SuppressFlags, fi.Name); + sc.WriteLine("#ifdef {0}", fi.Name); + if (is_bits || GetSuppressFlags(map) != null) + sc.WriteLine("\t\t*r |= {1};", fn, fi.Name); + else + sc.WriteLine("\t\t{{*r = {1}; return 0;}}", fn, fi.Name); + sc.WriteLine("#else /* def {0} */", fi.Name); + if (is_bits && IsRedundant(t, fi, values)) + { + sc.WriteLine("\t\t{{/* Ignoring {0}_{1}, as it is constructed from other values */}}", + fn, fi.Name); + } + else + { + sc.WriteLine("\t\t{errno = EINVAL; return -1;}"); + } + + sc.WriteLine("#endif /* ndef {0} */", fi.Name); + } + + // For many values, 0 is a valid value, but doesn't have it's own symbol. + // Examples: Error (0 means "no error"), WaitOptions (0 means "no options"). + // Make 0 valid for all conversions. + sc.WriteLine("\tif (x == 0)\n\t\treturn 0;"); + if (bits) + sc.WriteLine("\treturn 0;"); + else + sc.WriteLine("\terrno = EINVAL; return -1;"); // return error if not matched + sc.WriteLine("}\n"); + } + + private static string GetSuppressFlags(MapAttribute map) + { + if (map != null) + { + return map.SuppressFlags == null + ? null + : map.SuppressFlags.Length == 0 + ? null + : map.SuppressFlags; + } + + return null; + } + + private static bool IsRedundant(Type t, FieldInfo fi, Array values) + { + long v = Convert.ToInt64(fi.GetValue(null)); + long d = v; + if (v == 0) + return false; + foreach (object o in values) + { + long e = Convert.ToInt64(o); + if (((d & e) != 0) && (e < d)) + { + v &= ~e; + } + } + + if (v == 0) + { + return true; + } + + return false; + } + + private void WriteToManagedEnum(Type t, string ns, string fn, string etype, bool bits) + { + sc.WriteLine("int {1}_To{2} ({0} x, {0} *r)", etype, ns, t.Name); + sc.WriteLine("{"); + sc.WriteLine("\t*r = 0;", etype); + // For many values, 0 is a valid value, but doesn't have it's own symbol. + // Examples: Error (0 means "no error"), WaitOptions (0 means "no options"). + // Make 0 valid for all conversions. + sc.WriteLine("\tif (x == 0)\n\t\treturn 0;"); + FieldInfo[] fields = t.GetFields(); + Array.Sort(fields, MapUtils.MemberNameComparer); + foreach (FieldInfo fi in fields) + { + if (!fi.IsLiteral) + continue; + MapAttribute map = MapUtils.GetMapAttribute(fi); + bool is_bits = bits && (map != null ? map.SuppressFlags == null : true); + sc.WriteLine("#ifdef {0}", fi.Name); + if (is_bits) + // properly handle case where [Flags] enumeration has helper + // synonyms. e.g. DEFFILEMODE and ACCESSPERMS for mode_t. + sc.WriteLine("\tif ((x & {1}) == {1})\n\t\t*r |= {0}_{1};", fn, fi.Name); + else if (GetSuppressFlags(map) == null) + sc.WriteLine("\tif (x == {1})\n\t\t{{*r = {0}_{1}; return 0;}}", fn, fi.Name); + else + sc.WriteLine("\tif ((x & {2}) == {1})\n\t\t*r |= {0}_{1};", fn, fi.Name, map.SuppressFlags); + sc.WriteLine("#endif /* ndef {0} */", fi.Name); + } + + if (bits) + sc.WriteLine("\treturn 0;"); + else + sc.WriteLine("\terrno = EINVAL; return -1;"); + sc.WriteLine("}\n"); + } + + private void WriteFromManagedClass(Type t, string ns, string fn, string etype) + { + MapAttribute map = MapUtils.GetMapAttribute(t); + if (map == null || map.NativeType == null || map.NativeType.Length == 0) + return; + string nativeMacro = GetAutoconfDefine(map.NativeType); + sc.WriteLine("#ifdef {0}", nativeMacro); + sc.WriteLine("int\n{0}_From{1} (struct {0}_{1} *from, {2} *to)", + MapUtils.GetNamespace(t), t.Name, map.NativeType); + WriteManagedClassConversion(t, delegate(FieldInfo field) + { + MapAttribute ft = MapUtils.GetMapAttribute(field); + if (ft != null) + return ft.NativeType; + return MapUtils.GetNativeType(field.FieldType); + }, + delegate(FieldInfo field) { return GetNativeMemberName(field); }, + delegate(FieldInfo field) { return field.Name; }, + delegate(FieldInfo field) + { + return string.Format("{0}_From{1}", + MapUtils.GetNamespace(field.FieldType), + field.FieldType.Name); + } + ); + sc.WriteLine("#endif /* ndef {0} */\n\n", nativeMacro); + } + + private static string GetAutoconfDefine(string nativeType) + { + return string.Format("HAVE_{0}", + nativeType.ToUpperInvariant().Replace(" ", "_")); + } + + private delegate string GetFromType(FieldInfo field); + + private delegate string GetToFieldName(FieldInfo field); + + private delegate string GetFromFieldName(FieldInfo field); + + private delegate string GetFieldCopyMethod(FieldInfo field); + + private void WriteManagedClassConversion(Type t, GetFromType gft, + GetFromFieldName gffn, GetToFieldName gtfn, GetFieldCopyMethod gfc) + { + MapAttribute map = MapUtils.GetMapAttribute(t); + sc.WriteLine("{"); + FieldInfo[] fields = GetFieldsToCopy(t); + SortFieldsInOffsetOrder(t, fields); + int max_len = 0; + foreach (FieldInfo f in fields) + { + max_len = Math.Max(max_len, f.Name.Length); + if (!MapUtils.IsIntegralType(f.FieldType)) + continue; + string d = GetAutoconfDefine(map, f); + if (d != null) + sc.WriteLine("#ifdef " + d); + sc.WriteLine("\t_cnm_return_val_if_overflow ({0}, from->{1}, -1);", + gft(f), gffn(f)); + if (d != null) + sc.WriteLine("#endif /* ndef " + d + " */"); + } + + sc.WriteLine("\n\tmemset (to, 0, sizeof(*to));\n"); + foreach (FieldInfo f in fields) + { + string d = GetAutoconfDefine(map, f); + if (d != null) + sc.WriteLine("#ifdef " + d); + if (MapUtils.IsBlittableType(f.FieldType)) + { + sc.WriteLine("\tto->{0,-" + max_len + "} = from->{1};", + gtfn(f), gffn(f)); + } + else if (f.FieldType.IsEnum) + { + sc.WriteLine("\tif ({0} (from->{1}, &to->{2}) != 0) {{", gfc(f), + gffn(f), gtfn(f)); + sc.WriteLine("\t\treturn -1;"); + sc.WriteLine("\t}"); + } + else if (f.FieldType.IsValueType) + { + sc.WriteLine("\tif ({0} (&from->{1}, &to->{2}) != 0) {{", gfc(f), + gffn(f), gtfn(f)); + sc.WriteLine("\t\treturn -1;"); + sc.WriteLine("\t}"); + } + + if (d != null) + sc.WriteLine("#endif /* ndef " + d + " */"); + } + + sc.WriteLine(); + sc.WriteLine("\treturn 0;"); + sc.WriteLine("}"); + } + + private void WriteToManagedClass(Type t, string ns, string fn, string etype) + { + MapAttribute map = MapUtils.GetMapAttribute(t); + if (map == null || map.NativeType == null || map.NativeType.Length == 0) + return; + string nativeMacro = GetAutoconfDefine(map.NativeType); + sc.WriteLine("#ifdef {0}", nativeMacro); + sc.WriteLine("int\n{0}_To{1} ({2} *from, struct {0}_{1} *to)", + MapUtils.GetNamespace(t), t.Name, map.NativeType); + WriteManagedClassConversion(t, delegate(FieldInfo field) { return MapUtils.GetNativeType(field.FieldType); }, + delegate(FieldInfo field) { return field.Name; }, + delegate(FieldInfo field) { return GetNativeMemberName(field); }, + delegate(FieldInfo field) + { + return string.Format("{0}_To{1}", + MapUtils.GetNamespace(field.FieldType), + field.FieldType.Name); + } + ); + sc.WriteLine("#endif /* ndef {0} */\n\n", nativeMacro); + } + + private static FieldInfo[] GetFieldsToCopy(Type t) + { + FieldInfo[] fields = t.GetFields(BindingFlags.Instance | + BindingFlags.Public | BindingFlags.NonPublic); + int count = 0; + for (int i = 0; i < fields.Length; ++i) + if (MapUtils.GetCustomAttribute(fields[i]) == null) + ++count; + FieldInfo[] rf = new FieldInfo [count]; + for (int i = 0, j = 0; i < fields.Length; ++i) + { + if (MapUtils.GetCustomAttribute(fields[i]) == null) + rf[j++] = fields[i]; + } + + return rf; + } + + private string GetAutoconfDefine(MapAttribute typeMap, FieldInfo field) + { + if (Configuration.AutoconfMembers.BinarySearch(field.Name) < 0 && + Configuration.AutoconfMembers.BinarySearch(field.DeclaringType.Name + "." + field.Name) < 0) + return null; + return string.Format("HAVE_{0}_{1}", + typeMap.NativeType.ToUpperInvariant().Replace(" ", "_"), + field.Name.ToUpperInvariant()); + } + + public override void CloseFile(string file_prefix) + { + sc.Close(); + } + } + + class ConvertFileGenerator : FileGenerator + { + StreamWriter scs; + + public override void CreateFile(string assembly_name, string file_prefix) + { + scs = File.CreateText(file_prefix + ".cs"); + WriteHeader(scs, assembly_name, true); + scs.WriteLine("using System;"); + scs.WriteLine("using System.Runtime.InteropServices;"); + scs.WriteLine("using Mono.Unix.Native;\n"); + scs.WriteLine("namespace Mono.Unix.Native {\n"); + scs.WriteLine("\tpublic sealed /* static */ partial class NativeConvert"); + scs.WriteLine("\t{"); + scs.WriteLine("\t\tprivate NativeConvert () {}\n"); + scs.WriteLine("\t\tprivate const string LIB = \"{0}\";\n", Configuration.NativeLibraries[0]); + scs.WriteLine("\t\tprivate static void ThrowArgumentException (object value)"); + scs.WriteLine("\t\t{"); + scs.WriteLine("\t\t\tthrow new ArgumentOutOfRangeException (\"value\", value,"); + scs.WriteLine("\t\t\t\tLocale.GetText (\"Current platform doesn't support this value.\"));"); + scs.WriteLine("\t\t}\n"); + } + + public override void WriteType(Type t, string ns, string fn) + { + if (!CanMapType(t)) + return; + if (t.IsEnum) + WriteEnum(t, ns, fn); + else + WriteStruct(t, ns, fn); + } + + private void WriteEnum(Type t, string ns, string fn) + { + string mtype = Enum.GetUnderlyingType(t).Name; + ObsoleteAttribute oa = MapUtils.GetCustomAttribute(t); + string obsolete = ""; + if (oa != null) + { + obsolete = string.Format("[Obsolete (\"{0}\", {1})]\n\t\t", + oa.Message, oa.IsError ? "true" : "false"); + } + + scs.WriteLine( + "\t\t{0}[DllImport (LIB, EntryPoint=\"{1}_From{2}\")]\n" + + "\t\tprivate static extern int From{2} ({2} value, out {3} rval);\n" + + "\n" + + "\t\t{0}public static bool TryFrom{2} ({2} value, out {3} rval)\n" + + "\t\t{{\n" + + "\t\t\treturn From{2} (value, out rval) == 0;\n" + + "\t\t}}\n" + + "\n" + + "\t\t{0}public static {3} From{2} ({2} value)\n" + + "\t\t{{\n" + + "\t\t\t{3} rval;\n" + + "\t\t\tif (From{2} (value, out rval) == -1)\n" + + "\t\t\t\tThrowArgumentException (value);\n" + + "\t\t\treturn rval;\n" + + "\t\t}}\n" + + "\n" + + "\t\t{0}[DllImport (LIB, EntryPoint=\"{1}_To{2}\")]\n" + + "\t\tprivate static extern int To{2} ({3} value, out {2} rval);\n" + + "\n" + + "\t\t{0}public static bool TryTo{2} ({3} value, out {2} rval)\n" + + "\t\t{{\n" + + "\t\t\treturn To{2} (value, out rval) == 0;\n" + + "\t\t}}\n" + + "\n" + + "\t\t{0}public static {2} To{2} ({3} value)\n" + + "\t\t{{\n" + + "\t\t\t{2} rval;\n" + + "\t\t\tif (To{2} (value, out rval) == -1)\n" + + "\t\t\t\tThrowArgumentException (value);\n" + + "\t\t\treturn rval;\n" + + "\t\t}}\n", + obsolete, ns, t.Name, mtype + ); + } + + private void WriteStruct(Type t, string ns, string fn) + { + if (MapUtils.IsDelegate(t)) + return; + MapAttribute map = MapUtils.GetMapAttribute(t); + if (map == null || map.NativeType == null || map.NativeType.Length == 0) + return; + ObsoleteAttribute oa = MapUtils.GetCustomAttribute(t); + string obsolete = ""; + if (oa != null) + { + obsolete = string.Format("[Obsolete (\"{0}\", {1})]\n\t\t", + oa.Message, oa.IsError ? "true" : "false"); + } + + string _ref = t.IsValueType ? "ref " : ""; + string _out = t.IsValueType ? "out " : ""; + scs.WriteLine( + "\t\t{0}[DllImport (LIB, EntryPoint=\"{1}_From{2}\")]\n" + + "\t\tprivate static extern int From{2} ({3}{2} source, IntPtr destination);\n" + + "\n" + + "\t\t{0}public static bool TryCopy ({3}{2} source, IntPtr destination)\n" + + "\t\t{{\n" + + "\t\t\treturn From{2} ({3}source, destination) == 0;\n" + + "\t\t}}\n" + + "\n" + + "\t\t{0}[DllImport (LIB, EntryPoint=\"{1}_To{2}\")]\n" + + "\t\tprivate static extern int To{2} (IntPtr source, {4}{2} destination);\n" + + "\n" + + "\t\t{0}public static bool TryCopy (IntPtr source, {4}{2} destination)\n" + + "\t\t{{\n" + + "\t\t\treturn To{2} (source, {4}destination) == 0;\n" + + "\t\t}}\n", + obsolete, ns, t.Name, _ref, _out + ); + } + + public override void CloseFile(string file_prefix) + { + scs.WriteLine("\t}"); + scs.WriteLine("}\n"); + scs.Close(); + } + } + + class ConvertDocFileGenerator : FileGenerator + { + StreamWriter scs; + + public override void CreateFile(string assembly_name, string file_prefix) + { + scs = File.CreateText(file_prefix + ".xml"); + scs.WriteLine(" "); + } + + public override void WriteType(Type t, string ns, string fn) + { + if (!CanMapType(t) || !t.IsEnum) + return; + + bool bits = IsFlagsEnum(t); + + string type = GetCSharpType(t); + string mtype = Enum.GetUnderlyingType(t).FullName; + string member = t.Name; + string ftype = t.FullName; + + string to_returns = ""; + string to_remarks = ""; + string to_exception = ""; + + if (bits) + { + to_returns = "An approximation of the equivalent managed value."; + to_remarks = @"The current conversion functions are unable to determine + if a value in a [Flags]-marked enumeration does not + exist on the current platform. As such, if + contains a flag value which the current platform doesn't support, it + will not be present in the managed value returned. + This should only be a problem if + was not previously returned by + .\n"; + } + else + { + to_returns = "The equivalent managed value."; + to_exception = @" + + has no equivalent managed value. + +"; + } + + scs.WriteLine(@" + + + Method + + System.Boolean + + + + + + + The managed value to convert. + The OS-specific equivalent value. + Converts a + enumeration value to an OS-specific value. + if the conversion was successful; + otherwise, . + This is an exception-safe alternative to + . + If successful, this method stores the OS-specific equivalent + value of into . + Otherwise, will contain 0. + + + + + + + + + Method + + {3} + + + + + + The managed value to convert. + Converts a + to an OS-specific value. + The equivalent OS-specific value. + + has no equivalent OS-specific value. + + + + + + + + + + Method + + System.Boolean + + + + + + + The OS-specific value to convert. + The managed equivalent value + Converts an OS-specific value to a + . + if the conversion was successful; + otherwise, . + This is an exception-safe alternative to + . + If successful, this method stores the managed equivalent + value of into . + Otherwise, will contain a 0 + cast to a . + " + to_remarks + + @" + + + + + + + + Method + + {0} + + + + + + The OS-specific value to convert. + Converts an OS-specific value to a + . + " + to_returns + "\n" + + to_exception + + @" + " + to_remarks + @" + + + + + + +", ftype, member, type, mtype + ); + } + + private string GetCSharpType(Type t) + { + string ut = t.Name; + if (t.IsEnum) + ut = Enum.GetUnderlyingType(t).Name; + Type et = t.GetElementType(); + if (et != null && et.IsEnum) + ut = Enum.GetUnderlyingType(et).Name; + + string type = null; + + switch (ut) + { + case "Boolean": + type = "bool"; + break; + case "Byte": + type = "byte"; + break; + case "SByte": + type = "sbyte"; + break; + case "Int16": + type = "short"; + break; + case "UInt16": + type = "ushort"; + break; + case "Int32": + type = "int"; + break; + case "UInt32": + type = "uint"; + break; + case "Int64": + type = "long"; + break; + case "UInt64": + type = "ulong"; + break; + } + + return type; + } + + public override void CloseFile(string file_prefix) + { + scs.WriteLine(" "); + scs.Close(); + } + } +} +// vim: noexpandtab \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..07f3ffe --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +Copyright (C) 2006 Jonathan Pryor + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/Mono.Fuse.NETStandard.sln b/Mono.Fuse.NETStandard.sln new file mode 100644 index 0000000..6d6c3ec --- /dev/null +++ b/Mono.Fuse.NETStandard.sln @@ -0,0 +1,47 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Fuse.NETStandard", "Mono.Fuse.NETStandard\Mono.Fuse.NETStandard.csproj", "{247EAF60-7B35-444D-9B7D-13CBD76B7FA7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloFS", "example\HelloFS\HelloFS.csproj", "{FF21B641-C21D-401B-9450-E68DFDA09C07}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CreateNativeMap", "CreateNativeMap\CreateNativeMap.csproj", "{7F34C644-632E-4271-A1F9-114037DF6311}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedirectFS", "example\RedirectFS\RedirectFS.csproj", "{11E71BC7-004B-4CB5-BA03-86E160199D78}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedirectFS-FH", "example\RedirectFS-FH\RedirectFS-FH.csproj", "{1D5F7569-C773-422B-B783-3F1775816D45}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{85A3668F-D9E7-4794-8723-4F3767008CB9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {247EAF60-7B35-444D-9B7D-13CBD76B7FA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {247EAF60-7B35-444D-9B7D-13CBD76B7FA7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {247EAF60-7B35-444D-9B7D-13CBD76B7FA7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {247EAF60-7B35-444D-9B7D-13CBD76B7FA7}.Release|Any CPU.Build.0 = Release|Any CPU + {FF21B641-C21D-401B-9450-E68DFDA09C07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FF21B641-C21D-401B-9450-E68DFDA09C07}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FF21B641-C21D-401B-9450-E68DFDA09C07}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FF21B641-C21D-401B-9450-E68DFDA09C07}.Release|Any CPU.Build.0 = Release|Any CPU + {7F34C644-632E-4271-A1F9-114037DF6311}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F34C644-632E-4271-A1F9-114037DF6311}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F34C644-632E-4271-A1F9-114037DF6311}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F34C644-632E-4271-A1F9-114037DF6311}.Release|Any CPU.Build.0 = Release|Any CPU + {11E71BC7-004B-4CB5-BA03-86E160199D78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11E71BC7-004B-4CB5-BA03-86E160199D78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11E71BC7-004B-4CB5-BA03-86E160199D78}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11E71BC7-004B-4CB5-BA03-86E160199D78}.Release|Any CPU.Build.0 = Release|Any CPU + {1D5F7569-C773-422B-B783-3F1775816D45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D5F7569-C773-422B-B783-3F1775816D45}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D5F7569-C773-422B-B783-3F1775816D45}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D5F7569-C773-422B-B783-3F1775816D45}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {11E71BC7-004B-4CB5-BA03-86E160199D78} = {85A3668F-D9E7-4794-8723-4F3767008CB9} + {1D5F7569-C773-422B-B783-3F1775816D45} = {85A3668F-D9E7-4794-8723-4F3767008CB9} + {FF21B641-C21D-401B-9450-E68DFDA09C07} = {85A3668F-D9E7-4794-8723-4F3767008CB9} + EndGlobalSection +EndGlobal diff --git a/Mono.Fuse.NETStandard/FileNameMarshaler.cs b/Mono.Fuse.NETStandard/FileNameMarshaler.cs new file mode 100644 index 0000000..2c36394 --- /dev/null +++ b/Mono.Fuse.NETStandard/FileNameMarshaler.cs @@ -0,0 +1,79 @@ +// +// Mono.Fuse/FileNameMarshaler.cs +// +// Authors: +// Jonathan Pryor (jonpryor@vt.edu) +// +// (C) 2005 Jonathan Pryor +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Runtime.InteropServices; +using Mono.Unix; + +namespace Mono.Fuse.NETStandard { + + class FileNameMarshaler : ICustomMarshaler { + + private static FileNameMarshaler Instance = new FileNameMarshaler (); + + public static ICustomMarshaler GetInstance (string s) + { + return Instance; + } + + public void CleanUpManagedData (object o) + { + } + + public void CleanUpNativeData (IntPtr pNativeData) + { + // Console.WriteLine ("# FileNameMarshaler.CleanUpManagedData ({0:x})", pNativeData); + UnixMarshal.FreeHeap (pNativeData); + } + + public int GetNativeDataSize () + { + return IntPtr.Size; + } + + public IntPtr MarshalManagedToNative (object obj) + { + string s = obj as string; + if (s == null) + return IntPtr.Zero; + IntPtr p = UnixMarshal.StringToHeap (s, UnixEncoding.Instance); + // Console.WriteLine ("# FileNameMarshaler.MarshalNativeToManaged for `{0}'={1:x}", s, p); + return p; + } + + public object MarshalNativeToManaged (IntPtr pNativeData) + { + string s = UnixMarshal.PtrToString (pNativeData, UnixEncoding.Instance); + // Console.WriteLine ("# FileNameMarshaler.MarshalNativeToManaged ({0:x})=`{1}'", + // pNativeData, s); + return s; + } + } +} + +// vim: noexpandtab diff --git a/Mono.Fuse.NETStandard/FileSystem.cs b/Mono.Fuse.NETStandard/FileSystem.cs new file mode 100644 index 0000000..967062c --- /dev/null +++ b/Mono.Fuse.NETStandard/FileSystem.cs @@ -0,0 +1,1749 @@ +// +// Mono.Fuse/FileSystem.cs +// +// Authors: +// Jonathan Pryor (jonpryor@vt.edu) +// +// (C) 2006-2007 Jonathan Pryor +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; +using Mono.Unix; +using Mono.Unix.Native; + +namespace Mono.Fuse.NETStandard { + + [StructLayout (LayoutKind.Sequential)] + public sealed class FileSystemOperationContext { + internal IntPtr fuse; + [Map ("uid_t")] private long userId; + [Map ("gid_t")] private long groupId; + [Map ("pid_t")] private int processId; + + internal FileSystemOperationContext () + { + } + + public long UserId { + get {return userId;} + } + + public long GroupId { + get {return groupId;} + } + + public int ProcessId { + get {return processId;} + } + } + + public class DirectoryEntry { + private string name; + + public string Name { + get {return name;} + } + + // This is used only if st_ino is non-zero and + // FileSystem.SetsInodes is true + public Stat Stat; + + private static char[] invalidPathChars = new char[]{'/'}; + + public DirectoryEntry (string name) + { + if (name == null) + throw new ArgumentNullException ("name"); + if (name.IndexOfAny (invalidPathChars) != -1) + throw new ArgumentException ( + "name cannot contain directory separator char", "name"); + this.name = name; + } + } + + [Map] + [StructLayout (LayoutKind.Sequential)] + public sealed class OpenedPathInfo { + internal OpenFlags flags; + private int write_page; + private bool direct_io; + private bool keep_cache; + private ulong file_handle; + + internal OpenedPathInfo () + { + } + + public OpenFlags OpenFlags { + get {return flags;} + set {flags = value;} + } + + private const OpenFlags accessMask = + OpenFlags.O_RDONLY | OpenFlags.O_WRONLY | OpenFlags.O_RDWR; + + public OpenFlags OpenAccess { + get {return flags & accessMask;} + } + + public int WritePage { + get {return write_page;} + set {write_page = value;} + } + + public bool DirectIO { + get {return direct_io;} + set {direct_io = value;} + } + + public bool KeepCache { + get {return keep_cache;} + set {keep_cache = value;} + } + + public IntPtr Handle { + get {return (IntPtr) (long) file_handle;} + set {file_handle = (ulong) (long) value;} + } + } + + delegate int GetPathStatusCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr stat); + delegate int ReadSymbolicLinkCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr buf, ulong bufsize); + delegate int CreateSpecialFileCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, uint perms, ulong dev); + delegate int CreateDirectoryCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, uint mode); + delegate int RemoveFileCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path); + delegate int RemoveDirectoryCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path); + delegate int CreateSymbolicLinkCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string oldpath, + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string newpath); + delegate int RenamePathCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string oldpath, + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string newpath); + delegate int CreateHardLinkCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string oldpath, + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string newpath); + delegate int ChangePathPermissionsCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, uint mode); + delegate int ChangePathOwnerCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, long owner, long group); + delegate int TruncateFileb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, long length); + delegate int ChangePathTimesCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr buf); + delegate int OpenHandleCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr info); + delegate int ReadHandleCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, + [Out, MarshalAs (UnmanagedType.LPArray, ArraySubType=UnmanagedType.U1, SizeParamIndex=2)] + byte[] buf, ulong size, long offset, IntPtr info, out int bytesRead); + delegate int WriteHandleCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, + [In, MarshalAs (UnmanagedType.LPArray, ArraySubType=UnmanagedType.U1, SizeParamIndex=2)] + byte[] buf, ulong size, long offset, IntPtr info, out int bytesWritten); + delegate int GetFileSystemStatusCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr buf); + delegate int FlushHandleCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr info); + delegate int ReleaseHandleCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr info); + delegate int SynchronizeHandleCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, bool onlyUserData, IntPtr info); + delegate int SetPathExtendedAttributeCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string name, + [In, MarshalAs (UnmanagedType.LPArray, ArraySubType=UnmanagedType.U1, SizeParamIndex=3)] + byte[] value, ulong size, int flags); + delegate int GetPathExtendedAttributeCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string name, + [Out, MarshalAs (UnmanagedType.LPArray, ArraySubType=UnmanagedType.U1, SizeParamIndex=3)] + byte[] value, ulong size, out int bytesWritten); + delegate int ListPathExtendedAttributesCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, + [Out, MarshalAs (UnmanagedType.LPArray, ArraySubType=UnmanagedType.U1, SizeParamIndex=2)] + byte[] list, ulong size, out int bytesWritten); + delegate int RemovePathExtendedAttributeCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string name); + delegate int OpenDirectoryCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr info); + delegate int ReadDirectoryCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr buf, IntPtr filler, + long offset, IntPtr info, IntPtr stbuf); + delegate int ReleaseDirectoryCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr info); + delegate int SynchronizeDirectoryCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, bool onlyUserData, IntPtr info); + delegate IntPtr InitCb (IntPtr conn); + delegate void DestroyCb (IntPtr conn); + delegate int AccessPathCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, int mode); + delegate int CreateHandleCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, uint mode, IntPtr info); + delegate int TruncateHandleCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, long length, IntPtr info); + delegate int GetHandleStatusCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr buf, IntPtr info); + delegate int LockHandleCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr info, int cmd, IntPtr flockp); + // TODO: utimens + delegate int MapPathLogicalToPhysicalIndexCb ( + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, ulong logical, out ulong physical); + + [Map] + [StructLayout (LayoutKind.Sequential)] + class Operations { + public GetPathStatusCb getattr; + public ReadSymbolicLinkCb readlink; + public CreateSpecialFileCb mknod; + public CreateDirectoryCb mkdir; + public RemoveFileCb unlink; + public RemoveDirectoryCb rmdir; + public CreateSymbolicLinkCb symlink; + public RenamePathCb rename; + public CreateHardLinkCb link; + public ChangePathPermissionsCb chmod; + public ChangePathOwnerCb chown; + public TruncateFileb truncate; + public ChangePathTimesCb utime; + public OpenHandleCb open; + public ReadHandleCb read; + public WriteHandleCb write; + public GetFileSystemStatusCb statfs; + public FlushHandleCb flush; + public ReleaseHandleCb release; + public SynchronizeHandleCb fsync; + public SetPathExtendedAttributeCb setxattr; + public GetPathExtendedAttributeCb getxattr; + public ListPathExtendedAttributesCb listxattr; + public RemovePathExtendedAttributeCb removexattr; + public OpenDirectoryCb opendir; + public ReadDirectoryCb readdir; + public ReleaseDirectoryCb releasedir; + public SynchronizeDirectoryCb fsyncdir; + public InitCb init; + public DestroyCb destroy; + public AccessPathCb access; + public CreateHandleCb create; + public TruncateHandleCb ftruncate; + public GetHandleStatusCb fgetattr; + public LockHandleCb @lock; + public MapPathLogicalToPhysicalIndexCb bmap; + } + + [Map ("struct fuse_args")] + [StructLayout (LayoutKind.Sequential)] + class Args { + public int argc; + public IntPtr argv; + public int allocated; + } + + public class ConnectionInformation { + private IntPtr conn; + + // fuse_conn_info member offsets + const int + ProtMajor = 0, + ProtMinor = 1, + AsyncRead = 2, + MaxWrite = 3, + MaxRead = 4; + + internal ConnectionInformation (IntPtr conn) + { + this.conn = conn; + } + + public uint ProtocolMajorVersion { + get {return (uint) Marshal.ReadInt32 (conn, ProtMajor);} + } + + public uint ProtocolMinorVersion { + get {return (uint) Marshal.ReadInt32 (conn, ProtMinor);} + } + + public bool AsynchronousReadSupported { + get {return Marshal.ReadInt32 (conn, AsyncRead) != 0;} + set {Marshal.WriteInt32 (conn, AsyncRead, value ? 1 : 0);} + } + + public uint MaxWriteBufferSize { + get {return (uint) Marshal.ReadInt32 (conn, MaxWrite);} + set {Marshal.WriteInt32 (conn, MaxWrite, (int) value);} + } + + public uint MaxReadahead { + get {return (uint) Marshal.ReadInt32 (conn, MaxRead);} + set {Marshal.WriteInt32 (conn, MaxRead, (int) value);} + } + } + + public abstract class FileSystem : IDisposable { + + const string LIB = "MonoFuseHelper"; + + [DllImport (LIB, SetLastError=true)] + private static extern int mfh_fuse_main (int argc, IntPtr argv, IntPtr op); + + [DllImport (LIB, SetLastError=true)] + private static extern int mfh_fuse_get_context ([In, Out] FileSystemOperationContext context); + + [DllImport (LIB, SetLastError=true)] + private static extern void mfh_fuse_exit (IntPtr fusep); + + [DllImport (LIB, SetLastError=false)] + private static extern int mfh_invoke_filler (IntPtr filler, IntPtr buf, + [MarshalAs (UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(FileNameMarshaler))] + string path, IntPtr stbuf, long offset); + + [DllImport (LIB, SetLastError=false)] + private static extern void mfh_show_fuse_help (string appname); + + private string mountPoint; + Dictionary opts = new Dictionary (); + private Operations ops; + private IntPtr opsp; + + protected FileSystem (string mountPoint) + { + this.mountPoint = mountPoint; + } + + protected FileSystem () + { + } + + protected FileSystem (string[] args) + { + string[] unhandled = ParseFuseArguments (args); + MountPoint = unhandled [unhandled.Length - 1]; + } + + public IDictionary FuseOptions { + get {return opts;} + } + + public bool EnableFuseDebugOutput { + get {return GetBool ("debug");} + set {Set ("debug", value ? "" : null);} + } + + public bool AllowAccessToOthers { + get {return GetBool ("allow_other");} + set {Set ("allow_other", value ? "" : null);} + } + + public bool AllowAccessToRoot { + get {return GetBool ("allow_root");} + set {Set ("allow_root", value ? "" : null);} + } + + public bool AllowMountOverNonEmptyDirectory { + get {return GetBool ("nonempty");} + set {Set ("nonempty", value ? "" : null);} + } + + public bool EnableKernelPermissionChecking { + get {return GetBool ("default_permissions");} + set {Set ("default_permissions", value ? "" : null);} + } + + public string Name { + get {return GetString ("fsname");} + set {Set ("fsname", value);} + } + + public bool EnableLargeReadRequests { + get {return GetBool ("large_read");} + set {Set ("large_read", value ? "" : null);} + } + + public int MaxReadSize { + get {return (int) GetLong ("max_read");} + set {Set ("max_read", value.ToString ());} + } + + public bool ImmediatePathRemoval { + get {return GetBool ("hard_remove");} + set {Set ("hard_remove", value ? "" : null);} + } + + public bool SetsInodes { + get {return GetBool ("use_ino");} + set {Set ("use_ino", value ? "" : null);} + } + + public bool ReaddirSetsInode { + get {return GetBool ("readdir_ino");} + set {Set ("readdir_ino", value ? "" : null);} + } + + public bool EnableDirectIO { + get {return GetBool ("direct_io");} + set {Set ("direct_io", value ? "" : null);} + } + + public bool EnableKernelCache { + get {return GetBool ("kernel_cache");} + set {Set ("kernel_cache", value ? "" : null);} + } + + public FilePermissions DefaultUmask { + get { + string umask = GetString ("umask") ?? "0000"; + return NativeConvert.FromOctalPermissionString (umask); + } + set { + Set ("umask", NativeConvert.ToOctalPermissionString (value)); + } + } + + public long DefaultUserId { + get {return GetLong ("uid");} + set {Set ("uid", value.ToString ());} + } + + public long DefaultGroupId { + get {return GetLong ("gid");} + set {Set ("gid", value.ToString ());} + } + + public double PathTimeout { + get {return (int) GetDouble ("entry_timeout");} + set {Set ("entry_timeout", value.ToString ());} + } + + public double DeletedPathTimeout { + get {return (int) GetDouble ("negative_timeout");} + set {Set ("negative_timeout", value.ToString ());} + } + + public double AttributeTimeout { + get {return (int) GetDouble ("attr_timeout");} + set {Set ("attr_timeout", value.ToString ());} + } + + private bool GetBool (string key) + { + return opts.ContainsKey (key); + } + + private double GetDouble (string key) + { + if (opts.ContainsKey (key)) + return double.Parse (opts [key]); + return 0.0; + } + + private string GetString (string key) + { + if (opts.ContainsKey (key)) + return opts [key]; + return ""; + } + + private long GetLong (string key) + { + if (opts.ContainsKey (key)) + return long.Parse (opts [key]); + return 0; + } + + private void Set (string key, string value) + { + if (value == null) { + opts.Remove (key); + return; + } + opts [key] = value; + } + + public string MountPoint { + get {return mountPoint;} + set {mountPoint = value;} + } + + private bool multithreaded = true; + public bool MultiThreaded { + get {return multithreaded;} + set {multithreaded = value;} + } + + const string NameValueRegex = @"(?\w+)(\s*=\s*(?.*))?"; + const string OptRegex = @"^-o\s*(" + NameValueRegex + ")?$"; + + public string[] ParseFuseArguments (string[] args) + { + List unhandled = new List (); + Regex o = new Regex (OptRegex); + Regex nv = new Regex (NameValueRegex); + bool interpret = true; + + for (int i = 0; i < args.Length; ++i) { + if (!interpret) { + unhandled.Add (args [i]); + continue; + } + Match m = o.Match (args [i]); + if (m.Success) { + if (!m.Groups ["Name"].Success) { + m = nv.Match (args [++i]); + if (!m.Success) + throw new ArgumentException ("args"); + } + opts [m.Groups ["Name"].Value] = + m.Groups ["Value"].Success ? m.Groups ["Value"].Value : ""; + } + else if (args [i] == "-d") { + opts ["debug"] = ""; + } + else if (args [i] == "-s") { + multithreaded = false; + } + else if (args [i] == "-f") { + // foreground operation; ignore + // (we can only do foreground operation anyway) + } + else if (args [i] == "--") { + interpret = false; + } + else { + unhandled.Add (args [i]); + } + } + return unhandled.ToArray (); + } + + public static void ShowFuseHelp (string appname) + { + mfh_show_fuse_help (appname); + } + + private string[] GetFuseArgs () + { + string[] args = new string [opts.Keys.Count + 3 + (!MultiThreaded ? 1 : 0)]; + int i = 0; + args [i++] = Environment.GetCommandLineArgs () [0]; + foreach (string key in opts.Keys) { + if (key == "debug") { + args [i++] = "-d"; + continue; + } + string v = opts [key]; + string a = "-o" + key; + if (v.Length > 0) { + a += "=" + v.ToString (); + } + args [i++] = a; + } + args [i++] = "-f"; // force foreground operation + if (!MultiThreaded) + args [i++] = "-s"; // disable multi-threaded operation + args [i++] = mountPoint; + return args; + } + + private static IntPtr AllocArgv (string[] args) + { + IntPtr argv = UnixMarshal.AllocHeap ((args.Length+1) * IntPtr.Size); + int i; + for (i = 0; i < args.Length; ++i) { + Marshal.WriteIntPtr (argv, i*IntPtr.Size, + UnixMarshal.StringToHeap (args [i])); + } + Marshal.WriteIntPtr (argv, args.Length*IntPtr.Size, IntPtr.Zero); + return argv; + } + + private static void FreeArgv (int argc, IntPtr argv) + { + if (argv == IntPtr.Zero) + return; + for (int i = 0; i < argc; ++i) { + IntPtr p = Marshal.ReadIntPtr (argv, i * IntPtr.Size); + UnixMarshal.FreeHeap (p); + } + UnixMarshal.FreeHeap (argv); + } + + delegate void CopyOperation (Operations to, FileSystem from); + static readonly Dictionary operations; + + static FileSystem () + { + operations = new Dictionary { + {"OnGetPathStatus", (to, from) => {to.getattr = from._OnGetPathStatus;} }, + {"OnReadSymbolicLink", (to, from) => {to.readlink = from._OnReadSymbolicLink;} }, + {"OnCreateSpecialFile", (to, from) => {to.mknod = from._OnCreateSpecialFile;} }, + {"OnCreateDirectory", (to, from) => {to.mkdir = from._OnCreateDirectory;} }, + {"OnRemoveFile", (to, from) => {to.unlink = from._OnRemoveFile;} }, + {"OnRemoveDirectory", (to, from) => {to.rmdir = from._OnRemoveDirectory;} }, + {"OnCreateSymbolicLink", (to, from) => {to.symlink = from._OnCreateSymbolicLink;} }, + {"OnRenamePath", (to, from) => {to.rename = from._OnRenamePath;} }, + {"OnCreateHardLink", (to, from) => {to.link = from._OnCreateHardLink;} }, + {"OnChangePathPermissions", (to, from) => {to.chmod = from._OnChangePathPermissions;} }, + {"OnChangePathOwner", (to, from) => {to.chown = from._OnChangePathOwner;} }, + {"OnTruncateFile", (to, from) => {to.truncate = from._OnTruncateFile;} }, + {"OnChangePathTimes", (to, from) => {to.utime = from._OnChangePathTimes;} }, + {"OnOpenHandle", (to, from) => {to.open = from._OnOpenHandle;} }, + {"OnReadHandle", (to, from) => {to.read = from._OnReadHandle;} }, + {"OnWriteHandle", (to, from) => {to.write = from._OnWriteHandle;} }, + {"OnGetFileSystemStatus", (to, from) => {to.statfs = from._OnGetFileSystemStatus;} }, + {"OnFlushHandle", (to, from) => {to.flush = from._OnFlushHandle;} }, + {"OnReleaseHandle", (to, from) => {to.release = from._OnReleaseHandle;} }, + {"OnSynchronizeHandle", (to, from) => {to.fsync = from._OnSynchronizeHandle;} }, + {"OnSetPathExtendedAttribute", (to, from) => {to.setxattr = from._OnSetPathExtendedAttribute;} }, + {"OnGetPathExtendedAttribute", (to, from) => {to.getxattr = from._OnGetPathExtendedAttribute;} }, + {"OnListPathExtendedAttributes", (to, from) => {to.listxattr = from._OnListPathExtendedAttributes;} }, + {"OnRemovePathExtendedAttribute", (to, from) => {to.removexattr = from._OnRemovePathExtendedAttribute;} }, + {"OnOpenDirectory", (to, from) => {to.opendir = from._OnOpenDirectory;} }, + {"OnReadDirectory", (to, from) => {to.readdir = from._OnReadDirectory;} }, + {"OnReleaseDirectory", (to, from) => {to.releasedir = from._OnReleaseDirectory;} }, + {"OnSynchronizeDirectory", (to, from) => {to.fsyncdir = from._OnSynchronizeDirectory;} }, + {"OnAccessPath", (to, from) => {to.access = from._OnAccessPath;} }, + {"OnCreateHandle", (to, from) => {to.create = from._OnCreateHandle;} }, + {"OnTruncateHandle", (to, from) => {to.ftruncate = from._OnTruncateHandle;} }, + {"OnGetHandleStatus", (to, from) => {to.fgetattr = from._OnGetHandleStatus;} }, + {"OnLockHandle", (to, from) => {to.@lock = from._OnLockHandle;} }, + {"OnMapPathLogicalToPhysicalIndex", (to, from) => {to.bmap = from._OnMapPathLogicalToPhysicalIndex;} }, + }; + } + + private Operations GetOperations () + { + Operations ops = new Operations (); + + ops.init = _OnInit; + ops.destroy = _OnDestroy; + foreach (string method in operations.Keys) { + MethodInfo m = this.GetType().GetMethod (method, + BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); + MethodInfo bm = m.GetBaseDefinition (); + if (m.DeclaringType == typeof(FileSystem) || + bm == null || bm.DeclaringType != typeof(FileSystem)) + continue; + CopyOperation op = operations [method]; + op (ops, this); + } + + ValidateOperations (ops); + + return ops; + } + + private static void ValidateOperations (Operations ops) + { + // some methods need to be overridden in sets for sane operation + if (ops.opendir != null && ops.releasedir == null) + throw new InvalidOperationException ( + "OnReleaseDirectory() must be overridden if OnOpenDirectory() is overridden."); + } + + public void Dispose () + { + Dispose (true); + GC.SuppressFinalize (this); + } + + protected virtual void Dispose (bool disposing) + { + if (disposing) + ops = null; + } + + ~FileSystem () + { + Dispose (false); + } + + public void Start () + { + if (mountPoint == null) + throw new InvalidOperationException ("MountPoint must not be null"); + string[] args = GetFuseArgs (); + IntPtr argv = AllocArgv (args); + try { + this.ops = GetOperations (); + this.opsp = UnixMarshal.AllocHeap (Marshal.SizeOf (ops)); + Marshal.StructureToPtr (ops, opsp, false); + + int r = mfh_fuse_main (args.Length, argv, opsp); + if (r != 0) + throw new NotSupportedException ( + string.Format ("Unable to mount directory `{0}'; " + + "try running `/sbin/modprobe fuse' as the root user", mountPoint)); + } + finally { + FreeArgv (args.Length, argv); + } + } + + public void Stop () + { + mfh_fuse_exit (GetOperationContext ().fuse); + } + + protected static FileSystemOperationContext GetOperationContext () + { + FileSystemOperationContext context = new FileSystemOperationContext (); + int r = mfh_fuse_get_context (context); + UnixMarshal.ThrowExceptionForLastErrorIf (r); + return context; + } + + [DllImport (LIB, SetLastError=true)] + private static extern int Mono_Fuse_NETStandard_FromOpenedPathInfo (OpenedPathInfo source, IntPtr dest); + + [DllImport (LIB, SetLastError=true)] + private static extern int Mono_Fuse_NETStandard_ToOpenedPathInfo (IntPtr source, [Out] OpenedPathInfo dest); + + private static void CopyFlock (IntPtr source, out Flock dest) + { + if (!NativeConvert.TryCopy (source, out dest)) + throw new ArgumentOutOfRangeException ("Unable to copy `struct flock' into Mono.Unix.Native.Flock."); + } + + private static void CopyFlock (ref Flock source, IntPtr dest) + { + if (!NativeConvert.TryCopy (ref source, dest)) + throw new ArgumentOutOfRangeException ("Unable to copy Mono.Unix.Native.Flock into `struct flock'."); + } + + private static void CopyStat (IntPtr source, out Stat dest) + { + if (!NativeConvert.TryCopy (source, out dest)) + throw new ArgumentOutOfRangeException ("Unable to copy `struct stat' into Mono.Unix.Native.Stat."); + } + + private static void CopyStat (ref Stat source, IntPtr dest) + { + if (!NativeConvert.TryCopy (ref source, dest)) + throw new ArgumentOutOfRangeException ("Unable to copy Mono.Unix.Native.Stat into `struct stat'."); + } + + private static void CopyStatvfs (IntPtr source, out Statvfs dest) + { + if (!NativeConvert.TryCopy (source, out dest)) + throw new ArgumentOutOfRangeException ("Unable to copy `struct statvfs' into Mono.Unix.Native.Statvfs."); + } + + private static void CopyStatvfs (ref Statvfs source, IntPtr dest) + { + if (!NativeConvert.TryCopy (ref source, dest)) + throw new ArgumentOutOfRangeException ("Unable to copy Mono.Unix.Native.Statvfs into `struct statvfs'."); + } + + private static void CopyUtimbuf (IntPtr source, out Utimbuf dest) + { + if (!NativeConvert.TryCopy (source, out dest)) + throw new ArgumentOutOfRangeException ("Unable to copy `struct utimbuf' into Mono.Unix.Native.Utimbuf."); + } + + private static void CopyUtimbuf (ref Utimbuf source, IntPtr dest) + { + if (!NativeConvert.TryCopy (ref source, dest)) + throw new ArgumentOutOfRangeException ("Unable to copy Mono.Unix.Native.Utimbuf into `struct utimbuf'."); + } + + private static void CopyOpenedPathInfo (IntPtr source, OpenedPathInfo dest) + { + Mono_Fuse_NETStandard_ToOpenedPathInfo (source, dest); + dest.flags = NativeConvert.ToOpenFlags ((int) dest.flags); + } + + private static void CopyOpenedPathInfo (OpenedPathInfo source, IntPtr dest) + { + source.flags = (OpenFlags) NativeConvert.FromOpenFlags (source.flags); + Mono_Fuse_NETStandard_FromOpenedPathInfo (source, dest); + } + + private int _OnGetPathStatus (string path, IntPtr stat) + { + Errno errno; + try { + Stat buf; + CopyStat (stat, out buf); + errno = OnGetPathStatus (path, out buf); + if (errno == 0) + CopyStat (ref buf, stat); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + private int ConvertErrno (Errno e) + { + int r; + if (NativeConvert.TryFromErrno (e, out r)) + return -r; + return -1; + } + + protected virtual Errno OnGetPathStatus (string path, out Stat stat) + { + stat = new Stat (); + return Errno.ENOSYS; + } + + private int _OnReadSymbolicLink (string path, IntPtr buf, ulong bufsize) + { + Errno errno; + try { + if (bufsize <= 1) + return ConvertErrno (Errno.EINVAL); + string target; + errno = OnReadSymbolicLink (path, out target); + if (errno == 0 && target != null) { + byte[] b = encoding.GetBytes (target); + if ((bufsize-1) < (ulong) b.Length) { + errno = Errno.EINVAL; + } + else { + Marshal.Copy (b, 0, buf, b.Length); + Marshal.WriteByte (buf, b.Length, (byte) 0); + } + } + else if (errno == 0 && target == null) { + Trace.WriteLine ("OnReadSymbolicLink: error: 0 return value but target is `null'"); + errno = Errno.EIO; + } + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + static Encoding encoding = new UTF8Encoding (false, true); + + protected virtual Errno OnReadSymbolicLink (string link, out string target) + { + target = null; + return Errno.ENOSYS; + } + + private int _OnCreateSpecialFile (string path, uint perms, ulong dev) + { + Errno errno; + try { + FilePermissions _perms = NativeConvert.ToFilePermissions (perms); + errno = OnCreateSpecialFile (path, _perms, dev); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnCreateSpecialFile (string file, FilePermissions perms, ulong dev) + { + return Errno.ENOSYS; + } + + private int _OnCreateDirectory (string path, uint mode) + { + Errno errno; + try { + FilePermissions _mode = NativeConvert.ToFilePermissions (mode); + errno = OnCreateDirectory (path, _mode); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnCreateDirectory (string directory, FilePermissions mode) + { + return Errno.ENOSYS; + } + + private int _OnRemoveFile (string path) + { + Errno errno; + try { + errno = OnRemoveFile (path); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnRemoveFile (string file) + { + return Errno.ENOSYS; + } + + private int _OnRemoveDirectory (string path) + { + Errno errno; + try { + errno = OnRemoveDirectory (path); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnRemoveDirectory (string directory) + { + return Errno.ENOSYS; + } + + private int _OnCreateSymbolicLink (string oldpath, string newpath) + { + Errno errno; + try { + errno = OnCreateSymbolicLink (oldpath, newpath); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnCreateSymbolicLink (string target, string link) + { + return Errno.ENOSYS; + } + + private int _OnRenamePath (string oldpath, string newpath) + { + Errno errno; + try { + errno = OnRenamePath (oldpath, newpath); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnRenamePath (string oldpath, string newpath) + { + return Errno.ENOSYS; + } + + private int _OnCreateHardLink (string oldpath, string newpath) + { + Errno errno; + try { + errno = OnCreateHardLink (oldpath, newpath); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnCreateHardLink (string oldpath, string link) + { + return Errno.ENOSYS; + } + + private int _OnChangePathPermissions (string path, uint mode) + { + Errno errno; + try { + FilePermissions _mode = NativeConvert.ToFilePermissions (mode); + errno = OnChangePathPermissions (path, _mode); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnChangePathPermissions (string path, FilePermissions mode) + { + return Errno.ENOSYS; + } + + private int _OnChangePathOwner (string path, long owner, long group) + { + Errno errno; + try { + errno = OnChangePathOwner (path, owner, group); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnChangePathOwner (string path, long owner, long group) + { + return Errno.ENOSYS; + } + + private int _OnTruncateFile (string path, long length) + { + Errno errno; + try { + errno = OnTruncateFile (path, length); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnTruncateFile (string file, long length) + { + return Errno.ENOSYS; + } + + // TODO: can buf be null? + private int _OnChangePathTimes (string path, IntPtr buf) + { + Errno errno; + try { + Utimbuf b; + CopyUtimbuf (buf, out b); + errno = OnChangePathTimes (path, ref b); + if (errno == 0) + CopyUtimbuf (ref b, buf); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnChangePathTimes (string path, ref Utimbuf buf) + { + return Errno.ENOSYS; + } + + private int _OnOpenHandle (string path, IntPtr fi) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + errno = OnOpenHandle (path, info); + if (errno == 0) + CopyOpenedPathInfo (info, fi); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnOpenHandle (string file, OpenedPathInfo info) + { + return Errno.ENOSYS; + } + + private int _OnReadHandle (string path, byte[] buf, ulong size, long offset, IntPtr fi, out int bytesWritten) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + errno = OnReadHandle (path, info, buf, offset, out bytesWritten); + if (errno == 0) + CopyOpenedPathInfo (info, fi); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + bytesWritten = 0; + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnReadHandle (string file, OpenedPathInfo info, byte[] buf, long offset, out int bytesWritten) + { + bytesWritten = 0; + return Errno.ENOSYS; + } + + private int _OnWriteHandle (string path, byte[] buf, ulong size, long offset, IntPtr fi, out int bytesRead) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + errno = OnWriteHandle (path, info, buf, offset, out bytesRead); + if (errno == 0) + CopyOpenedPathInfo (info, fi); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + bytesRead = 0; + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnWriteHandle (string file, OpenedPathInfo info, byte[] buf, long offset, out int bytesRead) + { + bytesRead = 0; + return Errno.ENOSYS; + } + + private int _OnGetFileSystemStatus (string path, IntPtr buf) + { + Errno errno; + try { + Statvfs b; + CopyStatvfs (buf, out b); + errno = OnGetFileSystemStatus (path, out b); + if (errno == 0) + CopyStatvfs (ref b, buf); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnGetFileSystemStatus (string path, out Statvfs buf) + { + buf = new Statvfs (); + return Errno.ENOSYS; + } + + private int _OnFlushHandle (string path, IntPtr fi) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + errno = OnFlushHandle (path, info); + CopyOpenedPathInfo (info, fi); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnFlushHandle (string file, OpenedPathInfo info) + { + return Errno.ENOSYS; + } + + private int _OnReleaseHandle (string path, IntPtr fi) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + errno = OnReleaseHandle (path, info); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnReleaseHandle (string file, OpenedPathInfo info) + { + return Errno.ENOSYS; + } + + private int _OnSynchronizeHandle (string path, bool onlyUserData, IntPtr fi) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + errno = OnSynchronizeHandle (path, info, onlyUserData); + if (errno == 0) + CopyOpenedPathInfo (info, fi); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnSynchronizeHandle (string file, OpenedPathInfo info, bool onlyUserData) + { + return Errno.ENOSYS; + } + + private int _OnSetPathExtendedAttribute (string path, string name, byte[] value, ulong size, int flags) + { + Errno errno; + try { + XattrFlags f = NativeConvert.ToXattrFlags (flags); + errno = OnSetPathExtendedAttribute (path, name, value, f); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnSetPathExtendedAttribute (string path, string name, byte[] value, XattrFlags flags) + { + return Errno.ENOSYS; + } + + private int _OnGetPathExtendedAttribute (string path, string name, byte[] value, ulong size, out int bytesWritten) + { + Errno errno; + try { + errno = OnGetPathExtendedAttribute (path, name, value, out bytesWritten); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + bytesWritten = 0; + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnGetPathExtendedAttribute (string path, string name, byte[] value, out int bytesWritten) + { + bytesWritten = 0; + return Errno.ENOSYS; + } + + private int _OnListPathExtendedAttributes (string path, byte[] list, ulong size, out int bytesWritten) + { + Errno errno; + try { + bytesWritten = 0; + string[] names; + errno = OnListPathExtendedAttributes (path, out names); + if (errno == 0 && names != null) { + int bytesNeeded = 0; + for (int i = 0; i < names.Length; ++i) { + bytesNeeded += encoding.GetByteCount (names [i]) + 1; + } + if (size == 0) + bytesWritten = bytesNeeded; + if (size < (ulong) bytesNeeded) { + errno = Errno.ERANGE; + } + else { + int dest = 0; + for (int i = 0; i < names.Length; ++i) { + int b = encoding.GetBytes (names [i], 0, names [i].Length, + list, dest); + list[dest+b] = (byte) '\0'; + dest += (b + 1); + } + bytesWritten = dest; + } + } + else + bytesWritten = 0; + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + bytesWritten = 0; + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnListPathExtendedAttributes (string path, out string[] names) + { + names = null; + return Errno.ENOSYS; + } + + private int _OnRemovePathExtendedAttribute (string path, string name) + { + Errno errno; + try { + errno = OnRemovePathExtendedAttribute (path, name); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnRemovePathExtendedAttribute (string path, string name) + { + return Errno.ENOSYS; + } + + private int _OnOpenDirectory (string path, IntPtr fi) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + errno = OnOpenDirectory (path, info); + if (errno == 0) + CopyOpenedPathInfo (info, fi); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnOpenDirectory (string directory, OpenedPathInfo info) + { + return Errno.ENOSYS; + } + + private object directoryLock = new object (); + + private Dictionary directoryReaders = + new Dictionary (); + + private Random directoryKeys = new Random (); + + private int _OnReadDirectory (string path, IntPtr buf, IntPtr filler, + long offset, IntPtr fi, IntPtr stbuf) + { + Errno errno = 0; + try { + if (offset == 0) + GetDirectoryEnumerator (path, fi, out offset, out errno); + if (errno != 0) + return ConvertErrno (errno); + + EntryEnumerator entries = null; + lock (directoryLock) { + string key = offset.ToString (); + if (directoryReaders.ContainsKey (key)) + entries = directoryReaders [key]; + } + + // FUSE will invoke _OnReadDirectory at least twice, but if there were + // very few entries then the enumerator will get cleaned up during the + // first call, so this is (1) expected, and (2) ignorable. + if (entries == null) { + return 0; + } + + bool cleanup = FillEntries (filler, buf, stbuf, offset, entries); + + if (cleanup) { + entries.Dispose (); + lock (directoryLock) { + directoryReaders.Remove (offset.ToString ()); + } + } + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + private void GetDirectoryEnumerator (string path, IntPtr fi, out long offset, out Errno errno) + { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + + offset = -1; + + IEnumerable paths; + errno = OnReadDirectory (path, info, out paths); + if (errno != 0) + return; + if (paths == null) { + Trace.WriteLine ("OnReadDirectory: errno = 0 but paths is null!"); + errno = Errno.EIO; + return; + } + IEnumerator e = paths.GetEnumerator (); + if (e == null) { + Trace.WriteLine ("OnReadDirectory: errno = 0 but enumerator is null!"); + errno = Errno.EIO; + return; + } + int key; + lock (directoryLock) { + do { + key = directoryKeys.Next (1, int.MaxValue); + } while (directoryReaders.ContainsKey (key.ToString())); + directoryReaders [key.ToString()] = new EntryEnumerator (e); + } + + CopyOpenedPathInfo (info, fi); + + offset = key; + errno = 0; + } + + class EntryEnumerator : IEnumerator { + private IEnumerator entries; + bool repeat; + + public EntryEnumerator (IEnumerator entries) + { + this.entries = entries; + } + + public DirectoryEntry Current { + get {return entries.Current;} + } + + object IEnumerator.Current { + get {return Current;} + } + + public bool Repeat { + set {repeat = value;} + } + + public bool MoveNext () + { + if (repeat) { + repeat = false; + return true; + } + return entries.MoveNext (); + } + + public void Reset () + { + throw new InvalidOperationException (); + } + + public void Dispose () + { + entries.Dispose (); + } + } + + private bool FillEntries (IntPtr filler, IntPtr buf, IntPtr stbuf, + long offset, EntryEnumerator entries) + { + while (entries.MoveNext ()) { + DirectoryEntry entry = entries.Current; + IntPtr _stbuf = IntPtr.Zero; + if (entry.Stat.st_ino != 0) { + CopyStat (ref entry.Stat, stbuf); + _stbuf = stbuf; + } + int r = mfh_invoke_filler (filler, buf, entry.Name, _stbuf, offset); + if (r != 0) { + entries.Repeat = true; + return false; + } + } + return true; + } + + protected virtual Errno OnReadDirectory (string directory, OpenedPathInfo info, + out IEnumerable paths) + { + paths = null; + return Errno.ENOSYS; + } + + private int _OnReleaseDirectory (string path, IntPtr fi) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + errno = OnReleaseDirectory (path, info); + if (errno == 0) + CopyOpenedPathInfo (info, fi); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnReleaseDirectory (string directory, OpenedPathInfo info) + { + return Errno.ENOSYS; + } + + private int _OnSynchronizeDirectory (string path, bool onlyUserData, IntPtr fi) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + errno = OnSynchronizeDirectory (path, info, onlyUserData); + if (errno == 0) + CopyOpenedPathInfo (info, fi); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnSynchronizeDirectory (string directory, OpenedPathInfo info, bool onlyUserData) + { + return Errno.ENOSYS; + } + + private IntPtr _OnInit (IntPtr conn) + { + try { + OnInit (new ConnectionInformation (conn)); + return opsp; + } + catch (Exception e) { + return IntPtr.Zero; + } + } + + protected virtual void OnInit (ConnectionInformation connection) + { + } + + private void _OnDestroy (IntPtr opsp) + { + Debug.Assert (opsp == this.opsp); + + Marshal.DestroyStructure (opsp, typeof(Operations)); + UnixMarshal.FreeHeap (opsp); + this.opsp = IntPtr.Zero; + + Dispose (true); + } + + private int _OnAccessPath (string path, int mode) + { + Errno errno; + try { + AccessModes _mode = NativeConvert.ToAccessModes (mode); + errno = OnAccessPath (path, _mode); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnAccessPath (string path, AccessModes mode) + { + return Errno.ENOSYS; + } + + private int _OnCreateHandle (string path, uint mode, IntPtr fi) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + FilePermissions _mode = NativeConvert.ToFilePermissions (mode); + errno = OnCreateHandle (path, info, _mode); + if (errno == 0) + CopyOpenedPathInfo (info, fi); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnCreateHandle (string file, OpenedPathInfo info, FilePermissions mode) + { + return Errno.ENOSYS; + } + + private int _OnTruncateHandle (string path, long length, IntPtr fi) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + errno = OnTruncateHandle (path, info, length); + if (errno == 0) + CopyOpenedPathInfo (info, fi); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnTruncateHandle (string file, OpenedPathInfo info, long length) + { + return Errno.ENOSYS; + } + + private int _OnGetHandleStatus (string path, IntPtr buf, IntPtr fi) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + Stat b; + CopyStat (buf, out b); + errno = OnGetHandleStatus (path, info, out b); + if (errno == 0) { + CopyStat (ref b, buf); + CopyOpenedPathInfo (info, fi); + } + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnGetHandleStatus (string file, OpenedPathInfo info, out Stat buf) + { + buf = new Stat (); + return Errno.ENOSYS; + } + + private int _OnLockHandle (string file, IntPtr fi, int cmd, IntPtr lockp) + { + Errno errno; + try { + OpenedPathInfo info = new OpenedPathInfo (); + CopyOpenedPathInfo (fi, info); + FcntlCommand _cmd = NativeConvert.ToFcntlCommand (cmd); + Flock @lock; + CopyFlock (lockp, out @lock); + errno = OnLockHandle (file, info, _cmd, ref @lock); + if (errno == 0) { + CopyFlock (ref @lock, lockp); + CopyOpenedPathInfo (info, fi); + } + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnLockHandle (string file, OpenedPathInfo info, FcntlCommand cmd, ref Flock @lock) + { + return Errno.ENOSYS; + } + + private int _OnMapPathLogicalToPhysicalIndex (string path, ulong logical, out ulong physical) + { + Errno errno; + try { + errno = OnMapPathLogicalToPhysicalIndex (path, logical, out physical); + } + catch (Exception e) { + Trace.WriteLine (e.ToString()); + physical = ulong.MaxValue; + errno = Errno.EIO; + } + return ConvertErrno (errno); + } + + protected virtual Errno OnMapPathLogicalToPhysicalIndex (string path, ulong logical, out ulong physical) + { + physical = ulong.MaxValue; + return Errno.ENOSYS; + } + } +} + diff --git a/Mono.Fuse.NETStandard/MapAttribute.cs b/Mono.Fuse.NETStandard/MapAttribute.cs new file mode 100644 index 0000000..1d48020 --- /dev/null +++ b/Mono.Fuse.NETStandard/MapAttribute.cs @@ -0,0 +1,56 @@ +// +// MapAttribute.cs +// +// Author: +// Miguel de Icaza (miguel@gnome.org) +// +// (C) Novell, Inc. +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Fuse.NETStandard +{ + [AttributeUsage ( + AttributeTargets.Class | + AttributeTargets.Delegate | + AttributeTargets.Enum | + AttributeTargets.Field | + AttributeTargets.Struct)] + public class MapAttribute : Attribute { + public MapAttribute () + { + } + + public MapAttribute (string nativeType) + { + NativeType = nativeType; + } + + public string NativeType { get; } + + public string SuppressFlags { get; set; } + } +} + diff --git a/Mono.Fuse.NETStandard/Mono.Fuse.NETStandard.csproj b/Mono.Fuse.NETStandard/Mono.Fuse.NETStandard.csproj new file mode 100644 index 0000000..8890762 --- /dev/null +++ b/Mono.Fuse.NETStandard/Mono.Fuse.NETStandard.csproj @@ -0,0 +1,20 @@ + + + Mono.Fuse.NETStandard + 1.0.0-pre1 + Jonathan Pryor, Alexey Kolpakov + Port of Mono.Fuse package to NET Standart: C# bindings for FUSE + mono fuse filesystem linux unix + Mono.Fuse.NETStandard + netstandard2.0 + + + + runtimes/linux-x64/native/libMonoFuseHelper.so + true + + + + + + \ No newline at end of file diff --git a/MonoFuseHelper/mfh.c b/MonoFuseHelper/mfh.c new file mode 100644 index 0000000..5b7eaa3 --- /dev/null +++ b/MonoFuseHelper/mfh.c @@ -0,0 +1,493 @@ +/* + * mfh.c: MonoFuseHelper implementation. + * + * Authors: + * Jonathan Pryor (jonpryor@vt.edu) + * + * Copyright (C) 2006 Jonathan Pryor + */ + +/* + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#define FUSE_USE_VERSION 26 + +#include +#include +#include +#include +#include +#include + +static inline struct Mono_Fuse_NETStandard_Operations* +_mfh_get_private_data () +{ + return (struct Mono_Fuse_NETStandard_Operations*) fuse_get_context()->private_data; +} + +static int +mfh_getattr (const char *path, struct stat *stat) +{ + return _mfh_get_private_data ()->getattr (path, stat); +} + +static int +mfh_readlink (const char *path, char* buf, size_t size) +{ + return _mfh_get_private_data ()->readlink (path, buf, size); +} + +static int +mfh_mknod (const char *path, mode_t mode, dev_t dev) +{ + return _mfh_get_private_data ()->mknod (path, mode, dev); +} + +static int +mfh_mkdir (const char *path, mode_t mode) +{ + return _mfh_get_private_data ()->mkdir (path, mode); +} + +static int +mfh_unlink (const char *path) +{ + return _mfh_get_private_data ()->unlink (path); +} + +static int +mfh_rmdir (const char *path) +{ + return _mfh_get_private_data ()->rmdir (path); +} + +static int +mfh_symlink (const char *oldpath, const char *newpath) +{ + return _mfh_get_private_data ()->symlink (oldpath, newpath); +} + +static int +mfh_rename (const char *oldpath, const char *newpath) +{ + return _mfh_get_private_data ()->rename (oldpath, newpath); +} + +static int +mfh_link (const char *oldpath, const char *newpath) +{ + return _mfh_get_private_data ()->link (oldpath, newpath); +} + +static int +mfh_chmod (const char *path, mode_t mode) +{ + return _mfh_get_private_data ()->chmod (path, mode); +} + +static int +mfh_chown (const char *path, uid_t uid, gid_t gid) +{ + return _mfh_get_private_data ()->chown (path, uid, gid); +} + +static int +mfh_truncate (const char *path, off_t len) +{ + return _mfh_get_private_data ()->truncate (path, len); +} + +static int +mfh_utime (const char *path, struct utimbuf *buf) +{ + return _mfh_get_private_data ()->utime (path, buf); +} + +int +Mono_Fuse_NETStandard_ToOpenedPathInfo (void *_from, struct Mono_Fuse_NETStandard_OpenedPathInfo *to) +{ + struct fuse_file_info *from = _from; + memset (to, 0, sizeof (*to)); + + to->flags = from->flags; + to->write_page = from->writepage; + to->direct_io = from->direct_io; + to->keep_cache = from->keep_cache; + to->file_handle = from->fh; + + return 0; +} + +int +Mono_Fuse_NETStandard_FromOpenedPathInfo (struct Mono_Fuse_NETStandard_OpenedPathInfo *from, void *_to) +{ + struct fuse_file_info *to = _to; + memset (to, 0, sizeof (*to)); + + to->flags = from->flags; + to->writepage = from->write_page; + to->direct_io = from->direct_io ? 1 : 0; + to->keep_cache = from->keep_cache ? 1 : 0; + to->fh = from->file_handle; + + return 0; +} + +static int +mfh_open (const char *path, struct fuse_file_info *info) +{ + int r; + + r = _mfh_get_private_data ()->open (path, info); + + return r; +} + +static int +mfh_read (const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *info) +{ + int r, bytesRead = 0; + + r = _mfh_get_private_data ()->read (path, (unsigned char*) buf, size, offset, + info, &bytesRead); + + if (r == 0 && bytesRead >= 0) + return bytesRead; + return r; +} + +static int +mfh_write (const char *path, const char *buf, size_t size, off_t offset, + struct fuse_file_info *info) +{ + int r, bytesWritten = 0; + + r = _mfh_get_private_data ()->write (path, (unsigned char*) buf, size, offset, + info, &bytesWritten); + + if (r == 0 && bytesWritten >= 0) + return bytesWritten; + return r; +} + +static int +mfh_statfs (const char *path, struct statvfs *buf) +{ + return _mfh_get_private_data ()->statfs (path, buf); +} + +static int +mfh_flush (const char *path, struct fuse_file_info *info) +{ + int r; + + r = _mfh_get_private_data ()->flush (path, info); + + return r; +} + +static int +mfh_release (const char *path, struct fuse_file_info *info) +{ + int r; + + r = _mfh_get_private_data ()->release (path, info); + + return r; +} + +static int +mfh_fsync (const char *path, int onlyUserData, struct fuse_file_info *info) +{ + int r; + + r = _mfh_get_private_data ()->fsync (path, onlyUserData, info); + + return r; +} + +static int +mfh_setxattr (const char *path, const char *name, const char *value, size_t size, int flags) +{ + return _mfh_get_private_data ()->setxattr (path, name, (unsigned char*) value, size, flags); +} + +static int +mfh_getxattr (const char *path, const char *name, char *buf, size_t size) +{ + int r, bytesWritten = 0; + r = _mfh_get_private_data ()->getxattr (path, name, (unsigned char *) buf, + size, &bytesWritten); + if (r == 0 && bytesWritten >= 0) + return bytesWritten; + return r; +} + +static int +mfh_listxattr (const char *path, char *buf, size_t size) +{ + int r, bytesWritten = 0; + r = _mfh_get_private_data ()->listxattr (path, (unsigned char *) buf, size, + &bytesWritten); + if (r == 0 && bytesWritten >= 0) + return bytesWritten; + return r; +} + +static int +mfh_removexattr (const char *path, const char *name) +{ + return _mfh_get_private_data ()->removexattr (path, name); +} + +static int +mfh_opendir (const char *path, struct fuse_file_info *info) +{ + int r; + + r = _mfh_get_private_data ()->opendir (path, info); + + return r; +} + +static int +mfh_readdir (const char *path, void* buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *info) +{ + int r; + struct stat stbuf; + + r = _mfh_get_private_data ()->readdir (path, buf, filler, offset, info, &stbuf); + + return r; +} + +int +mfh_invoke_filler (void *_filler, void *buf, const char *path, void *_stbuf, gint64 offset) +{ + struct stat *stbuf = _stbuf; + fuse_fill_dir_t filler = _filler; + + return filler (buf, path, stbuf, (off_t) offset); +} + +static int +mfh_releasedir (const char *path, struct fuse_file_info *info) +{ + int r; + + r = _mfh_get_private_data ()->releasedir (path, info); + + return r; +} + +static int +mfh_fsyncdir (const char *path, int onlyUserData, struct fuse_file_info *info) +{ + int r; + + r = _mfh_get_private_data ()->fsyncdir (path, onlyUserData, info); + + return r; +} + +void +mfh_destroy (void* user_data) +{ + _mfh_get_private_data ()->destroy (user_data); +} + +static int +mfh_access (const char *path, int flags) +{ + return _mfh_get_private_data ()->access (path, flags); +} + +static int +mfh_create (const char *path, mode_t mode, struct fuse_file_info *info) +{ + int r; + + r = _mfh_get_private_data ()->create (path, mode, info); + + return r; +} + +static int +mfh_ftruncate (const char *path, off_t len, struct fuse_file_info *info) +{ + int r; + + r = _mfh_get_private_data ()->ftruncate (path, len, info); + + return r; +} + +static int +mfh_fgetattr (const char *path, struct stat *stat, struct fuse_file_info *info) +{ + int r; + + r = _mfh_get_private_data ()->fgetattr (path, stat, info); + + return r; +} + +static int +mfh_lock (const char *path, struct fuse_file_info *info, int cmd, struct flock *lock) +{ + int r; + + r = _mfh_get_private_data ()->lock (path, info, cmd, lock); + + return r; +} + +static int +mfh_bmap (const char *path, size_t blocksize, uint64_t *idx) +{ + int r; + + r = _mfh_get_private_data ()->bmap (path, blocksize, idx); + + return r; +} + +static void +_to_fuse_operations (struct Mono_Fuse_NETStandard_Operations *from, struct fuse_operations *to) +{ + memset (to, 0, sizeof(*to)); + + if (from->getattr) to->getattr = mfh_getattr; + if (from->readlink) to->readlink = mfh_readlink; + if (from->mknod) to->mknod = mfh_mknod; + if (from->mkdir) to->mkdir = mfh_mkdir; + if (from->unlink) to->unlink = mfh_unlink; + if (from->rmdir) to->rmdir = mfh_rmdir; + if (from->symlink) to->symlink = mfh_symlink; + if (from->rename) to->rename = mfh_rename; + if (from->link) to->link = mfh_link; + if (from->chmod) to->chmod = mfh_chmod; + if (from->chown) to->chown = mfh_chown; + if (from->truncate) to->truncate = mfh_truncate; + if (from->utime) to->utime = mfh_utime; + if (from->open) to->open = mfh_open; + if (from->read) to->read = mfh_read; + if (from->write) to->write = mfh_write; + if (from->statfs) to->statfs = mfh_statfs; + if (from->flush) to->flush = mfh_flush; + if (from->release) to->release = mfh_release; + if (from->fsync) to->fsync = mfh_fsync; + if (from->setxattr) to->setxattr = mfh_setxattr; + if (from->getxattr) to->getxattr = mfh_getxattr; + if (from->listxattr) to->listxattr = mfh_listxattr; + if (from->removexattr) to->removexattr = mfh_removexattr; + if (from->opendir) to->opendir = mfh_opendir; + if (from->readdir) to->readdir = mfh_readdir; + if (from->releasedir) to->releasedir = mfh_releasedir; + if (from->fsyncdir) to->fsyncdir = mfh_fsyncdir; + if (from->init) to->init = (void* (*)(struct fuse_conn_info*)) from->init; + if (from->destroy) to->destroy = mfh_destroy; + if (from->access) to->access = mfh_access; + if (from->create) to->create = mfh_create; + if (from->ftruncate) to->ftruncate = mfh_ftruncate; + if (from->fgetattr) to->fgetattr = mfh_fgetattr; + if (from->lock) to->lock = mfh_lock; + if (from->bmap) to->bmap = mfh_bmap; +} + +void +mfh_show_fuse_help (const char *appname) +{ + char *help[3]; + char *mountpoint; + int mt, foreground; + struct fuse_args args; + struct fuse_operations ops = {}; + + help [0] = (char*) appname; + help [1] = "-ho"; + help [2] = NULL; + + memset (&args, 0, sizeof(args)); + + args.argc = 2; + args.argv = help; + args.allocated = 0; + + fuse_parse_cmdline (&args, &mountpoint, &mt, &foreground); + fuse_mount ("mountpoint", &args); + fuse_new (NULL, &args, &ops, sizeof(ops), NULL); + + fuse_opt_free_args (&args); +} + +int +mfh_fuse_get_context (struct Mono_Fuse_NETStandard_FileSystemOperationContext* context) +{ + struct fuse_context *from = fuse_get_context (); + if (from == NULL) { + errno = ENOTSUP; + return -1; + } + + context->fuse = from->fuse; + context->userId = from->uid; + context->groupId = from->gid; + context->processId = from->pid; + + return 0; +} + +int +mfh_fuse_main (int argc, void *argv, void* ops) +{ + struct Mono_Fuse_NETStandard_Operations *mops; + struct fuse_operations fops; + int r; + + mops = (struct Mono_Fuse_NETStandard_Operations*) ops; + + _to_fuse_operations (mops, &fops); + + r = fuse_main (argc, argv, &fops, NULL); + + return r; +} + +void +mfh_fuse_exit (void *fusep) +{ + fuse_exit ((struct fuse*) fusep); +} + +int +mfh_fuse_loop (void *fusep) +{ + return fuse_loop ((struct fuse*) fusep); +} + +int +mfh_fuse_loop_mt (void *fusep) +{ + return fuse_loop_mt ((struct fuse*) fusep); +} diff --git a/README b/README new file mode 100644 index 0000000..50b4f2a --- /dev/null +++ b/README @@ -0,0 +1,88 @@ +This is a Mono/.NET binding for FUSE. + +Short HOWTO: + +Within one terminal: + sudo /sbin/modprobe fuse # need FUSE kernel module to use. + ./configure + make + cd example/HelloFS + mkdir t + ./hellofs t + +Within another terminal + cd example/HelloFS + ls t + cat t/hello + +This should display files controlled by HelloFS.exe. + +To unmount the filesystem, you need to use the `fusermount' program: + cd example/HelloFS + fusermount -u t + +KILLING THE FUSE PROGRAM IS NOT ENOUGH. The program will die, but +FUSE and the kernel will still think that the directory is mounted. +Result: you can't remount the directory, and trying to do anything +with it will result in IO errors. + +For full trace output, set the MONO_TRACE_LISTENER environment +variable and add the -odebug command-line argument: + + cd example/HelloFS + MONO_TRACE_LISTENER=Console.Out### ./hellofs -odebug t + +MONO_TRACE_LISTENER controls the Mono.Fuse trace output, while +-odebug controls the libfuse-generated trace output. + +`hellofs' also takes its own command-line aruments. By default, +it exports two files, `data' and `hello'. `hello' contains the +string "Hello World!", while `data' is a 100 MB (base 10) file that +generates its content on-demand. + +If you pass the --data.im-in-memory flag, a `data.im' file will be +available which is a 100MB (base 10) file with the same contents as +`data', but it is backed by a 100MB byte[] instead of generating its +contents on demand: + + cd example/HelloFS + ./hellofs --data.im-in-memory t + +The 100MB byte[] is allocated the first time the contents of +`data.im' are read. + +`data.im' is useful to see the difference in performance between +generate-on-demand and cached behaviors: + + cd example/HelloFS + ./hellofs --data.im-in-memory t + + # On-demand copy + time cp t/data f.txt + real 0m8.469s + user 0m0.020s + sys 0m1.128s + + # In-memory copy + # Note that the first `data.im' access is longer + # because it needed to allocate the array. + # Subsequent calls are shorter: + time \cp -f t/data.im f.txt + real 0m12.393s + user 0m0.004s + sys 0m1.172s + time \cp -f t/data.im f.txt + real 0m5.233s + user 0m0.016s + sys 0m1.136s + + # And for comparison, cp without FUSE: + time cp f.txt f2.txt + real 0m10.252s + user 0m0.016s + sys 0m1.000s + +And yes, on my machine it's faster to go through FUSE than to go +through the filesystem (though usually the filesystem outperforms +the on-demand generation). My machine is probably misconfigured. :-/ +Your mileage will vary. diff --git a/build-libs.sh b/build-libs.sh new file mode 100755 index 0000000..c62aecd --- /dev/null +++ b/build-libs.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +assert_exists () { + if ! type "$1" 2> /dev/null 1>&2 ; then + echo "Error: '$1' not installed" + exit 1 + fi +} + +init_configuration () { + if [ ! -z $1 ] + then + CONFIGURATION="$1" + else + CONFIGURATION="Release" + fi +} + +build_managed () { + dotnet build Mono.Fuse.NETStandard/Mono.Fuse.NETStandard.csproj -c "$CONFIGURATION" +} + +build_native () { + dotnet run -p CreateNativeMap/CreateNativeMap.csproj -c "$CONFIGURATION" --library=MonoFuseHelper "Mono.Fuse.NETStandard/bin/$CONFIGURATION/netstandard2.0/Mono.Fuse.NETStandard.dll" buildlibs/map + INCLUDES="-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/fuse -Ibuildlibs/" + libtool --mode=compile gcc -D_FILE_OFFSET_BITS=64 $INCLUDES -g -O2 -MT buildlibs/mfh.lo -MD -MP -c -o buildlibs/mfh.lo MonoFuseHelper/mfh.c + libtool --mode=compile gcc -D_FILE_OFFSET_BITS=64 $INCLUDES -g -O2 -MT buildlibs/map.lo -MD -MP -c -o buildlibs/map.lo buildlibs/map.c + libtool --mode=link gcc -g -O2 -no-undefined -avoid-version -o buildlibs/libMonoFuseHelper.la -rpath /usr/local/lib buildlibs/mfh.lo buildlibs/map.lo -lglib-2.0 -lfuse -pthread + mv buildlibs/.libs/libMonoFuseHelper.so "nativelibs/linux-x64" +} + +assert_exists dotnet +assert_exists libtool +assert_exists gcc +init_configuration +build_managed +build_native + + + diff --git a/example/HelloFS/HelloFS.cs b/example/HelloFS/HelloFS.cs new file mode 100644 index 0000000..7f77f99 --- /dev/null +++ b/example/HelloFS/HelloFS.cs @@ -0,0 +1,270 @@ +// +// HelloFS.cs +// +// Authors: +// Jonathan Pryor (jonpryor@vt.edu) +// +// (C) 2006 Jonathan Pryor +// +// Mono.Fuse.NETStandard example program +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using Mono.Unix.Native; + +namespace Mono.Fuse.NETStandard.Samples { + internal class HelloFs : FileSystem { + static readonly byte[] hello_str = Encoding.UTF8.GetBytes ("Hello World!\n"); + const string hello_path = "/hello"; + const string data_path = "/data"; + const string data_im_path = "/data.im"; + + const int data_size = 100000000; + + byte[] data_im_str; + bool have_data_im = false; + object data_im_str_lock = new object (); + Dictionary hello_attrs = new Dictionary(); + + public HelloFs () + { + Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); + Trace.WriteLine ("(HelloFS creating)"); + hello_attrs ["foo"] = Encoding.UTF8.GetBytes ("bar"); + } + + protected override Errno OnGetPathStatus (string path, out Stat stbuf) + { + Trace.WriteLine ("(OnGetPathStatus {0})", path); + + stbuf = new Stat (); + switch (path) { + case "/": + stbuf.st_mode = FilePermissions.S_IFDIR | + NativeConvert.FromOctalPermissionString ("0755"); + stbuf.st_nlink = 2; + return 0; + case hello_path: + case data_path: + case data_im_path: + stbuf.st_mode = FilePermissions.S_IFREG | + NativeConvert.FromOctalPermissionString ("0444"); + stbuf.st_nlink = 1; + int size = 0; + switch (path) { + case hello_path: size = hello_str.Length; break; + case data_path: + case data_im_path: size = data_size; break; + } + stbuf.st_size = size; + return 0; + default: + return Errno.ENOENT; + } + } + + protected override Errno OnReadDirectory (string path, OpenedPathInfo fi, + out IEnumerable paths) + { + Trace.WriteLine ("(OnReadDirectory {0})", path); + paths = null; + if (path != "/") + return Errno.ENOENT; + + paths = GetEntries (); + return 0; + } + + private IEnumerable GetEntries () + { + yield return new DirectoryEntry ("."); + yield return new DirectoryEntry (".."); + yield return new DirectoryEntry ("hello"); + yield return new DirectoryEntry ("data"); + if (have_data_im) + yield return new DirectoryEntry ("data.im"); + } + + protected override Errno OnOpenHandle (string path, OpenedPathInfo fi) + { + Trace.WriteLine (string.Format ("(OnOpen {0} Flags={1})", path, fi.OpenFlags)); + if (path != hello_path && path != data_path && path != data_im_path) + return Errno.ENOENT; + if (path == data_im_path && !have_data_im) + return Errno.ENOENT; + if (fi.OpenAccess != OpenFlags.O_RDONLY) + return Errno.EACCES; + return 0; + } + + protected override Errno OnReadHandle (string path, OpenedPathInfo fi, byte[] buf, long offset, out int bytesWritten) + { + Trace.WriteLine ("(OnRead {0})", path); + bytesWritten = 0; + int size = buf.Length; + if (path == data_im_path) + FillData (); + if (path == hello_path || path == data_im_path) { + byte[] source = path == hello_path ? hello_str : data_im_str; + if (offset < (long) source.Length) { + if (offset + (long) size > (long) source.Length) + size = (int) ((long) source.Length - offset); + Buffer.BlockCopy (source, (int) offset, buf, 0, size); + } + else + size = 0; + } + else if (path == data_path) { + int max = System.Math.Min ((int) data_size, (int) (offset + buf.Length)); + for (int i = 0, j = (int) offset; j < max; ++i, ++j) { + if ((j % 27) == 0) + buf [i] = (byte) '\n'; + else + buf [i] = (byte) ((j % 26) + 'a'); + } + } + else + return Errno.ENOENT; + + bytesWritten = size; + return 0; + } + + protected override Errno OnGetPathExtendedAttribute (string path, string name, byte[] value, out int bytesWritten) + { + Trace.WriteLine ("(OnGetPathExtendedAttribute {0})", path); + bytesWritten = 0; + if (path != hello_path) { + return 0; + } + byte[] _value; + lock (hello_attrs) { + if (!hello_attrs.ContainsKey (name)) + return 0; + _value = hello_attrs [name]; + } + if (value.Length < _value.Length) { + return Errno.ERANGE; + } + Array.Copy (_value, value, _value.Length); + bytesWritten = _value.Length; + return 0; + } + + protected override Errno OnSetPathExtendedAttribute (string path, string name, byte[] value, XattrFlags flags) + { + Trace.WriteLine ("(OnSetPathExtendedAttribute {0})", path); + if (path != hello_path) { + return Errno.ENOSPC; + } + lock (hello_attrs) { + hello_attrs [name] = value; + } + return 0; + } + + protected override Errno OnRemovePathExtendedAttribute (string path, string name) + { + Trace.WriteLine ("(OnRemovePathExtendedAttribute {0})", path); + if (path != hello_path) + return Errno.ENODATA; + lock (hello_attrs) { + if (!hello_attrs.ContainsKey (name)) + return Errno.ENODATA; + hello_attrs.Remove (name); + } + return 0; + } + + protected override Errno OnListPathExtendedAttributes (string path, out string[] names) + { + Trace.WriteLine ("(OnListPathExtendedAttributes {0})", path); + if (path != hello_path) { + names = new string[]{}; + return 0; + } + List _names = new List (); + lock (hello_attrs) { + _names.AddRange (hello_attrs.Keys); + } + names = _names.ToArray (); + return 0; + } + + private bool ParseArguments (string[] args) + { + for (int i = 0; i < args.Length; ++i) { + switch (args [i]) { + case "--data.im-in-memory": + have_data_im = true; + break; + case "-h": + case "--help": + Console.Error.WriteLine ("usage: hellofs [options] mountpoint"); + FileSystem.ShowFuseHelp ("hellofs"); + Console.Error.WriteLine ("hellofs options:"); + Console.Error.WriteLine (" --data.im-in-memory Add data.im file"); + return false; + default: + base.MountPoint = args [i]; + break; + } + } + return true; + } + + private void FillData () + { + lock (data_im_str_lock) { + if (data_im_str != null) + return; + data_im_str = new byte [data_size]; + for (int i = 0; i < data_im_str.Length; ++i) { + if ((i % 27) == 0) + data_im_str [i] = (byte) '\n'; + else + data_im_str [i] = (byte) ((i % 26) + 'a'); + } + } + } + + public static void Main (string[] args) + { + using (HelloFs fs = new HelloFs ()) { + string[] unhandled = fs.ParseFuseArguments (args); + foreach (string key in fs.FuseOptions.Keys) { + Console.WriteLine ("Option: {0}={1}", key, fs.FuseOptions [key]); + } + if (!fs.ParseArguments (unhandled)) + return; + // fs.MountAt ("path" /* , args? */); + fs.Start (); + } + } + } +} + diff --git a/example/HelloFS/HelloFS.csproj b/example/HelloFS/HelloFS.csproj new file mode 100644 index 0000000..874e153 --- /dev/null +++ b/example/HelloFS/HelloFS.csproj @@ -0,0 +1,13 @@ + + + Exe + Mono.Fuse.NETStandard.Samples + netcoreapp2.0 + + + + + + + + \ No newline at end of file diff --git a/example/RedirectFS-FH/RedirectFS-FH.cs b/example/RedirectFS-FH/RedirectFS-FH.cs new file mode 100644 index 0000000..b37aa40 --- /dev/null +++ b/example/RedirectFS-FH/RedirectFS-FH.cs @@ -0,0 +1,421 @@ +// +// RedirectFS-FH.cs: Port of +// http://fuse.cvs.sourceforge.net/fuse/fuse/example/fusexmp_fh.c?view=log +// +// Authors: +// Jonathan Pryor (jonpryor@vt.edu) +// +// (C) 2006 Jonathan Pryor +// +// Mono.Fuse.NETStandard example program +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using Mono.Fuse.NETStandard; +using Mono.Unix.Native; + +namespace Mono.Fuse.NETStandard.Samples { + class RedirectFHFS : FileSystem { + + private string basedir; + + public RedirectFHFS () + { + } + + protected override Errno OnGetPathStatus (string path, out Stat buf) + { + int r = Syscall.lstat (basedir+path, out buf); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnGetHandleStatus (string path, OpenedPathInfo info, out Stat buf) + { + int r = Syscall.fstat ((int) info.Handle, out buf); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnAccessPath (string path, AccessModes mask) + { + int r = Syscall.access (basedir+path, mask); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnReadSymbolicLink (string path, out string target) + { + target = null; + StringBuilder buf = new StringBuilder (256); + do { + int r = Syscall.readlink (basedir+path, buf); + if (r < 0) { + return Stdlib.GetLastError (); + } + else if (r == buf.Capacity) { + buf.Capacity *= 2; + } + else { + target = buf.ToString (0, r); + return 0; + } + } while (true); + } + + protected override Errno OnOpenDirectory (string path, OpenedPathInfo info) + { + IntPtr dp = Syscall.opendir (basedir+path); + if (dp == IntPtr.Zero) + return Stdlib.GetLastError (); + + info.Handle = dp; + return 0; + } + + protected override Errno OnReadDirectory (string path, OpenedPathInfo fi, + out IEnumerable paths) + { + IntPtr dp = (IntPtr) fi.Handle; + + paths = ReadDirectory (dp); + + return 0; + } + + private IEnumerable ReadDirectory (IntPtr dp) + { + Dirent de; + while ((de = Syscall.readdir (dp)) != null) { + DirectoryEntry e = new DirectoryEntry (de.d_name); + e.Stat.st_ino = de.d_ino; + e.Stat.st_mode = (FilePermissions) (de.d_type << 12); + yield return e; + } + } + + protected override Errno OnReleaseDirectory (string path, OpenedPathInfo info) + { + IntPtr dp = (IntPtr) info.Handle; + Syscall.closedir (dp); + return 0; + } + + protected override Errno OnCreateSpecialFile (string path, FilePermissions mode, ulong rdev) + { + int r; + + // On Linux, this could just be `mknod(basedir+path, mode, rdev)' but + // this is more portable. + if ((mode & FilePermissions.S_IFMT) == FilePermissions.S_IFREG) { + r = Syscall.open (basedir+path, OpenFlags.O_CREAT | OpenFlags.O_EXCL | + OpenFlags.O_WRONLY, mode); + if (r >= 0) + r = Syscall.close (r); + } + else if ((mode & FilePermissions.S_IFMT) == FilePermissions.S_IFIFO) { + r = Syscall.mkfifo (basedir+path, mode); + } + else { + r = Syscall.mknod (basedir+path, mode, rdev); + } + + if (r == -1) + return Stdlib.GetLastError (); + + return 0; + } + + protected override Errno OnCreateDirectory (string path, FilePermissions mode) + { + int r = Syscall.mkdir (basedir+path, mode); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnRemoveFile (string path) + { + int r = Syscall.unlink (basedir+path); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnRemoveDirectory (string path) + { + int r = Syscall.rmdir (basedir+path); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnCreateSymbolicLink (string from, string to) + { + int r = Syscall.symlink (from, basedir+to); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnRenamePath (string from, string to) + { + int r = Syscall.rename (basedir+from, basedir+to); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnCreateHardLink (string from, string to) + { + int r = Syscall.link (basedir+from, basedir+to); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnChangePathPermissions (string path, FilePermissions mode) + { + int r = Syscall.chmod (basedir+path, mode); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnChangePathOwner (string path, long uid, long gid) + { + int r = Syscall.lchown (basedir+path, (uint) uid, (uint) gid); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnTruncateFile (string path, long size) + { + int r = Syscall.truncate (basedir+path, size); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnTruncateHandle (string path, OpenedPathInfo info, long size) + { + int r = Syscall.ftruncate ((int) info.Handle, size); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnChangePathTimes (string path, ref Utimbuf buf) + { + int r = Syscall.utime (basedir+path, ref buf); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnCreateHandle (string path, OpenedPathInfo info, FilePermissions mode) + { + int fd = Syscall.open (basedir+path, info.OpenFlags, mode); + if (fd == -1) + return Stdlib.GetLastError (); + info.Handle = (IntPtr) fd; + return 0; + } + + protected override Errno OnOpenHandle (string path, OpenedPathInfo info) + { + int fd = Syscall.open (basedir+path, info.OpenFlags); + if (fd == -1) + return Stdlib.GetLastError (); + info.Handle = (IntPtr) fd; + return 0; + } + + protected override unsafe Errno OnReadHandle (string path, OpenedPathInfo info, byte[] buf, + long offset, out int bytesRead) + { + int r; + fixed (byte *pb = buf) { + r = bytesRead = (int) Syscall.pread ((int) info.Handle, + pb, (ulong) buf.Length, offset); + } + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override unsafe Errno OnWriteHandle (string path, OpenedPathInfo info, + byte[] buf, long offset, out int bytesWritten) + { + int r; + fixed (byte *pb = buf) { + r = bytesWritten = (int) Syscall.pwrite ((int) info.Handle, + pb, (ulong) buf.Length, offset); + } + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnGetFileSystemStatus (string path, out Statvfs stbuf) + { + int r = Syscall.statvfs (basedir+path, out stbuf); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnFlushHandle (string path, OpenedPathInfo info) + { + /* This is called from every close on an open file, so call the + close on the underlying filesystem. But since flush may be + called multiple times for an open file, this must not really + close the file. This is important if used on a network + filesystem like NFS which flush the data/metadata on close() */ + int r = Syscall.close (Syscall.dup ((int) info.Handle)); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnReleaseHandle (string path, OpenedPathInfo info) + { + int r = Syscall.close ((int) info.Handle); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnSynchronizeHandle (string path, OpenedPathInfo info, bool onlyUserData) + { + int r; + if (onlyUserData) + r = Syscall.fdatasync ((int) info.Handle); + else + r = Syscall.fsync ((int) info.Handle); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnSetPathExtendedAttribute (string path, string name, byte[] value, XattrFlags flags) + { + int r = Syscall.lsetxattr (basedir+path, name, value, (ulong) value.Length, flags); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnGetPathExtendedAttribute (string path, string name, byte[] value, out int bytesWritten) + { + int r = bytesWritten = (int) Syscall.lgetxattr (basedir+path, name, value, (ulong) value.Length); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnListPathExtendedAttributes (string path, out string[] names) + { + int r = (int) Syscall.llistxattr (basedir+path, out names); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnRemovePathExtendedAttribute (string path, string name) + { + int r = Syscall.lremovexattr (basedir+path, name); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnLockHandle (string file, OpenedPathInfo info, FcntlCommand cmd, ref Flock @lock) + { + int r = Syscall.fcntl ((int) info.Handle, cmd, ref @lock); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + private bool ParseArguments (string[] args) + { + for (int i = 0; i < args.Length; ++i) { + switch (args [i]) { + case "-h": + case "--help": + ShowHelp (); + return false; + default: + if (base.MountPoint == null) + base.MountPoint = args [i]; + else + basedir = args [i]; + break; + } + } + if (base.MountPoint == null) { + return Error ("missing mountpoint"); + } + if (basedir == null) { + return Error ("missing basedir"); + } + return true; + } + + private static void ShowHelp () + { + Console.Error.WriteLine ("usage: redirectfs [options] mountpoint:"); + FileSystem.ShowFuseHelp ("redirectfs-fh"); + Console.Error.WriteLine (); + Console.Error.WriteLine ("redirectfs-fh options"); + Console.Error.WriteLine (" basedir Directory to mirror"); + } + + private static bool Error (string message) + { + Console.Error.WriteLine ("redirectfs-fh: error: {0}", message); + return false; + } + + public static void Main (string[] args) + { + using (RedirectFHFS fs = new RedirectFHFS ()) { + string[] unhandled = fs.ParseFuseArguments (args); + if (!fs.ParseArguments (unhandled)) + return; + fs.Start (); + } + } + } +} + diff --git a/example/RedirectFS-FH/RedirectFS-FH.csproj b/example/RedirectFS-FH/RedirectFS-FH.csproj new file mode 100644 index 0000000..83cbc7b --- /dev/null +++ b/example/RedirectFS-FH/RedirectFS-FH.csproj @@ -0,0 +1,19 @@ + + + Exe + Mono.Fuse.NETStandard.Samples + netcoreapp2.0 + + + true + + + true + + + + + + + + \ No newline at end of file diff --git a/example/RedirectFS/RedirectFS.cs b/example/RedirectFS/RedirectFS.cs new file mode 100644 index 0000000..76e7f8a --- /dev/null +++ b/example/RedirectFS/RedirectFS.cs @@ -0,0 +1,368 @@ +// +// RedirectFS.cs: Port of +// http://fuse.cvs.sourceforge.net/fuse/fuse/example/fusexmp.c?view=log +// +// Authors: +// Jonathan Pryor (jonpryor@vt.edu) +// +// (C) 2006 Jonathan Pryor +// +// Mono.Fuse.NETStandard example program +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using Mono.Fuse.NETStandard; +using Mono.Unix.Native; + +namespace Mono.Fuse.NETStandard.Samples { + class RedirectFS : FileSystem { + + private string basedir; + + public RedirectFS () + { + } + + protected override Errno OnGetPathStatus (string path, out Stat buf) + { + int r = Syscall.lstat (basedir+path, out buf); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnAccessPath (string path, AccessModes mask) + { + int r = Syscall.access (basedir+path, mask); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnReadSymbolicLink (string path, out string target) + { + target = null; + StringBuilder buf = new StringBuilder (256); + do { + int r = Syscall.readlink (basedir+path, buf); + if (r < 0) { + return Stdlib.GetLastError (); + } + else if (r == buf.Capacity) { + buf.Capacity *= 2; + } + else { + target = buf.ToString (0, r); + return 0; + } + } while (true); + } + + protected override Errno OnReadDirectory (string path, OpenedPathInfo fi, + out IEnumerable paths) + { + IntPtr dp = Syscall.opendir (basedir+path); + if (dp == IntPtr.Zero) { + paths = null; + return Stdlib.GetLastError (); + } + + Dirent de; + List entries = new List (); + while ((de = Syscall.readdir (dp)) != null) { + DirectoryEntry e = new DirectoryEntry (de.d_name); + e.Stat.st_ino = de.d_ino; + e.Stat.st_mode = (FilePermissions) (de.d_type << 12); + entries.Add (e); + } + Syscall.closedir (dp); + + paths = entries; + return 0; + } + + protected override Errno OnCreateSpecialFile (string path, FilePermissions mode, ulong rdev) + { + int r; + + // On Linux, this could just be `mknod(basedir+path, mode, rdev)' but this is + // more portable. + if ((mode & FilePermissions.S_IFMT) == FilePermissions.S_IFREG) { + r = Syscall.open (basedir+path, OpenFlags.O_CREAT | OpenFlags.O_EXCL | + OpenFlags.O_WRONLY, mode); + if (r >= 0) + r = Syscall.close (r); + } + else if ((mode & FilePermissions.S_IFMT) == FilePermissions.S_IFIFO) { + r = Syscall.mkfifo (basedir+path, mode); + } + else { + r = Syscall.mknod (basedir+path, mode, rdev); + } + + if (r == -1) + return Stdlib.GetLastError (); + + return 0; + } + + protected override Errno OnCreateDirectory (string path, FilePermissions mode) + { + int r = Syscall.mkdir (basedir+path, mode); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnRemoveFile (string path) + { + int r = Syscall.unlink (basedir+path); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnRemoveDirectory (string path) + { + int r = Syscall.rmdir (basedir+path); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnCreateSymbolicLink (string from, string to) + { + int r = Syscall.symlink (from, basedir+to); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnRenamePath (string from, string to) + { + int r = Syscall.rename (basedir+from, basedir+to); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnCreateHardLink (string from, string to) + { + int r = Syscall.link (basedir+from, basedir+to); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnChangePathPermissions (string path, FilePermissions mode) + { + int r = Syscall.chmod (basedir+path, mode); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnChangePathOwner (string path, long uid, long gid) + { + int r = Syscall.lchown (basedir+path, (uint) uid, (uint) gid); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnTruncateFile (string path, long size) + { + int r = Syscall.truncate (basedir+path, size); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnChangePathTimes (string path, ref Utimbuf buf) + { + int r = Syscall.utime (basedir+path, ref buf); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnOpenHandle (string path, OpenedPathInfo info) + { + return ProcessFile (basedir+path, info.OpenFlags, delegate (int fd) {return 0;}); + } + + private delegate int FdCb (int fd); + private static Errno ProcessFile (string path, OpenFlags flags, FdCb cb) + { + int fd = Syscall.open (path, flags); + if (fd == -1) + return Stdlib.GetLastError (); + int r = cb (fd); + Errno res = 0; + if (r == -1) + res = Stdlib.GetLastError (); + Syscall.close (fd); + return res; + } + + protected override unsafe Errno OnReadHandle (string path, OpenedPathInfo info, byte[] buf, + long offset, out int bytesRead) + { + int br = 0; + Errno e = ProcessFile (basedir+path, OpenFlags.O_RDONLY, delegate (int fd) { + fixed (byte *pb = buf) { + return br = (int) Syscall.pread (fd, pb, (ulong) buf.Length, offset); + } + }); + bytesRead = br; + return e; + } + + protected override unsafe Errno OnWriteHandle (string path, OpenedPathInfo info, + byte[] buf, long offset, out int bytesWritten) + { + int bw = 0; + Errno e = ProcessFile (basedir+path, OpenFlags.O_WRONLY, delegate (int fd) { + fixed (byte *pb = buf) { + return bw = (int) Syscall.pwrite (fd, pb, (ulong) buf.Length, offset); + } + }); + bytesWritten = bw; + return e; + } + + protected override Errno OnGetFileSystemStatus (string path, out Statvfs stbuf) + { + int r = Syscall.statvfs (basedir+path, out stbuf); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnReleaseHandle (string path, OpenedPathInfo info) + { + return 0; + } + + protected override Errno OnSynchronizeHandle (string path, OpenedPathInfo info, bool onlyUserData) + { + return 0; + } + + protected override Errno OnSetPathExtendedAttribute (string path, string name, byte[] value, XattrFlags flags) + { + int r = Syscall.lsetxattr (basedir+path, name, value, (ulong) value.Length, flags); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnGetPathExtendedAttribute (string path, string name, byte[] value, out int bytesWritten) + { + int r = bytesWritten = (int) Syscall.lgetxattr (basedir+path, name, value, (ulong) value.Length); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnListPathExtendedAttributes (string path, out string[] names) + { + int r = (int) Syscall.llistxattr (basedir+path, out names); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnRemovePathExtendedAttribute (string path, string name) + { + int r = Syscall.lremovexattr (basedir+path, name); + if (r == -1) + return Stdlib.GetLastError (); + return 0; + } + + protected override Errno OnLockHandle (string file, OpenedPathInfo info, FcntlCommand cmd, ref Flock @lock) + { + Flock _lock = @lock; + Errno e = ProcessFile (basedir+file, info.OpenFlags, fd => Syscall.fcntl (fd, cmd, ref _lock)); + @lock = _lock; + return e; + } + + private bool ParseArguments (string[] args) + { + for (int i = 0; i < args.Length; ++i) { + switch (args [i]) { + case "-h": + case "--help": + ShowHelp (); + return false; + default: + if (base.MountPoint == null) + base.MountPoint = args [i]; + else + basedir = args [i]; + break; + } + } + if (base.MountPoint == null) { + return Error ("missing mountpoint"); + } + if (basedir == null) { + return Error ("missing basedir"); + } + return true; + } + + private static void ShowHelp () + { + Console.Error.WriteLine ("usage: redirectfs [options] mountpoint basedir:"); + FileSystem.ShowFuseHelp ("redirectfs"); + Console.Error.WriteLine (); + Console.Error.WriteLine ("redirectfs options:"); + Console.Error.WriteLine (" basedir Directory to mirror"); + } + + private static bool Error (string message) + { + Console.Error.WriteLine ("redirectfs: error: {0}", message); + return false; + } + + public static void Main (string[] args) + { + using (RedirectFS fs = new RedirectFS ()) { + string[] unhandled = fs.ParseFuseArguments (args); + if (!fs.ParseArguments (unhandled)) + return; + fs.Start (); + } + } + } +} + diff --git a/example/RedirectFS/RedirectFS.csproj b/example/RedirectFS/RedirectFS.csproj new file mode 100644 index 0000000..83cbc7b --- /dev/null +++ b/example/RedirectFS/RedirectFS.csproj @@ -0,0 +1,19 @@ + + + Exe + Mono.Fuse.NETStandard.Samples + netcoreapp2.0 + + + true + + + true + + + + + + + + \ No newline at end of file