From d30e71d923ba10e7942f6ba3d3a186638e82ac47 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Tue, 11 May 2021 16:37:02 +0200 Subject: [PATCH] Initial commit --- .gitignore | 34 + LoopbackFS-C/LICENSE.txt | 339 ++++++ .../loopback.xcodeproj/project.pbxproj | 308 ++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/loopback.xcscheme | 123 +++ LoopbackFS-C/loopback/loopback.c | 980 ++++++++++++++++++ LoopbackFS-C/loopback/loopback.entitlements | 8 + LoopbackFS-ObjC/LICENSE.txt | 13 + .../LoopbackFS.xcodeproj/project.pbxproj | 360 +++++++ .../xcschemes/LoopbackFS.xcscheme | 96 ++ LoopbackFS-ObjC/LoopbackFS/AppDelegate.h | 33 + LoopbackFS-ObjC/LoopbackFS/AppDelegate.m | 118 +++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 58 ++ .../LoopbackFS/Assets.xcassets/Contents.json | 6 + .../LoopbackFS/Base.lproj/MainMenu.xib | 680 ++++++++++++ LoopbackFS-ObjC/LoopbackFS/Info.plist | 26 + .../LoopbackFS/LoopbackFS.entitlements | 8 + LoopbackFS-ObjC/LoopbackFS/LoopbackFS.h | 37 + LoopbackFS-ObjC/LoopbackFS/LoopbackFS.m | 479 +++++++++ LoopbackFS-ObjC/LoopbackFS/NSError+POSIX.h | 26 + LoopbackFS-ObjC/LoopbackFS/NSError+POSIX.m | 28 + LoopbackFS-ObjC/LoopbackFS/main.m | 30 + LoopbackFS-Swift/LICENSE.txt | 27 + .../LoopbackFS.xcodeproj/project.pbxproj | 360 +++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + LoopbackFS-Swift/LoopbackFS/AppDelegate.swift | 101 ++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 58 ++ .../LoopbackFS/Assets.xcassets/Contents.json | 6 + .../LoopbackFS/Base.lproj/MainMenu.xib | 680 ++++++++++++ LoopbackFS-Swift/LoopbackFS/Bridging-Header.h | 13 + LoopbackFS-Swift/LoopbackFS/Info.plist | 30 + .../LoopbackFS/LoopbackFS.entitlements | 8 + LoopbackFS-Swift/LoopbackFS/LoopbackFS.swift | 359 +++++++ .../LoopbackFS/NSError+POSIX.swift | 15 + 38 files changed, 5499 insertions(+) create mode 100644 .gitignore create mode 100644 LoopbackFS-C/LICENSE.txt create mode 100644 LoopbackFS-C/loopback.xcodeproj/project.pbxproj create mode 100644 LoopbackFS-C/loopback.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 LoopbackFS-C/loopback.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 LoopbackFS-C/loopback.xcodeproj/xcshareddata/xcschemes/loopback.xcscheme create mode 100644 LoopbackFS-C/loopback/loopback.c create mode 100644 LoopbackFS-C/loopback/loopback.entitlements create mode 100644 LoopbackFS-ObjC/LICENSE.txt create mode 100644 LoopbackFS-ObjC/LoopbackFS.xcodeproj/project.pbxproj create mode 100644 LoopbackFS-ObjC/LoopbackFS.xcodeproj/xcshareddata/xcschemes/LoopbackFS.xcscheme create mode 100644 LoopbackFS-ObjC/LoopbackFS/AppDelegate.h create mode 100644 LoopbackFS-ObjC/LoopbackFS/AppDelegate.m create mode 100644 LoopbackFS-ObjC/LoopbackFS/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 LoopbackFS-ObjC/LoopbackFS/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 LoopbackFS-ObjC/LoopbackFS/Assets.xcassets/Contents.json create mode 100644 LoopbackFS-ObjC/LoopbackFS/Base.lproj/MainMenu.xib create mode 100644 LoopbackFS-ObjC/LoopbackFS/Info.plist create mode 100644 LoopbackFS-ObjC/LoopbackFS/LoopbackFS.entitlements create mode 100644 LoopbackFS-ObjC/LoopbackFS/LoopbackFS.h create mode 100644 LoopbackFS-ObjC/LoopbackFS/LoopbackFS.m create mode 100644 LoopbackFS-ObjC/LoopbackFS/NSError+POSIX.h create mode 100644 LoopbackFS-ObjC/LoopbackFS/NSError+POSIX.m create mode 100644 LoopbackFS-ObjC/LoopbackFS/main.m create mode 100644 LoopbackFS-Swift/LICENSE.txt create mode 100644 LoopbackFS-Swift/LoopbackFS.xcodeproj/project.pbxproj create mode 100644 LoopbackFS-Swift/LoopbackFS.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 LoopbackFS-Swift/LoopbackFS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 LoopbackFS-Swift/LoopbackFS/AppDelegate.swift create mode 100644 LoopbackFS-Swift/LoopbackFS/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 LoopbackFS-Swift/LoopbackFS/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 LoopbackFS-Swift/LoopbackFS/Assets.xcassets/Contents.json create mode 100644 LoopbackFS-Swift/LoopbackFS/Base.lproj/MainMenu.xib create mode 100644 LoopbackFS-Swift/LoopbackFS/Bridging-Header.h create mode 100644 LoopbackFS-Swift/LoopbackFS/Info.plist create mode 100644 LoopbackFS-Swift/LoopbackFS/LoopbackFS.entitlements create mode 100644 LoopbackFS-Swift/LoopbackFS/LoopbackFS.swift create mode 100644 LoopbackFS-Swift/LoopbackFS/NSError+POSIX.swift diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a7d963f --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP shares +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Xcode +xcuserdata/ +*.xcodeproj/* +!*.xcodeproj/project.pbxproj +!*.xcodeproj/xcshareddata/ +!*.xcworkspace/contents.xcworkspacedata +**/xcshareddata/WorkspaceSettings.xcsettings + diff --git a/LoopbackFS-C/LICENSE.txt b/LoopbackFS-C/LICENSE.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LoopbackFS-C/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/LoopbackFS-C/loopback.xcodeproj/project.pbxproj b/LoopbackFS-C/loopback.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c005849 --- /dev/null +++ b/LoopbackFS-C/loopback.xcodeproj/project.pbxproj @@ -0,0 +1,308 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 436C6B011C59595E00C4FE10 /* loopback.c in Sources */ = {isa = PBXBuildFile; fileRef = 436C6B001C59595E00C4FE10 /* loopback.c */; }; + 43E954082649F92C009CCB55 /* libfuse.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 43E954072649F92C009CCB55 /* libfuse.2.dylib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 436C6AFD1C59595E00C4FE10 /* loopback */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = loopback; sourceTree = BUILT_PRODUCTS_DIR; }; + 436C6B001C59595E00C4FE10 /* loopback.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = loopback.c; sourceTree = ""; }; + 43ADB58922817D2200B49726 /* loopback.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = loopback.entitlements; sourceTree = ""; }; + 43E953B12646EC9A009CCB55 /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; + 43E954072649F92C009CCB55 /* libfuse.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libfuse.2.dylib; path = ../../../../../../../usr/local/lib/libfuse.2.dylib; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 436C6AFA1C59595E00C4FE10 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 43E954082649F92C009CCB55 /* libfuse.2.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 436C6AF41C59595E00C4FE10 = { + isa = PBXGroup; + children = ( + 43E953B12646EC9A009CCB55 /* LICENSE.txt */, + 436C6AFF1C59595E00C4FE10 /* loopback */, + 43E954062649F92C009CCB55 /* Frameworks */, + 436C6AFE1C59595E00C4FE10 /* Products */, + ); + sourceTree = ""; + }; + 436C6AFE1C59595E00C4FE10 /* Products */ = { + isa = PBXGroup; + children = ( + 436C6AFD1C59595E00C4FE10 /* loopback */, + ); + name = Products; + sourceTree = ""; + }; + 436C6AFF1C59595E00C4FE10 /* loopback */ = { + isa = PBXGroup; + children = ( + 43ADB58922817D2200B49726 /* loopback.entitlements */, + 436C6B001C59595E00C4FE10 /* loopback.c */, + ); + path = loopback; + sourceTree = ""; + }; + 43E954062649F92C009CCB55 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 43E954072649F92C009CCB55 /* libfuse.2.dylib */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 436C6AFC1C59595E00C4FE10 /* loopback */ = { + isa = PBXNativeTarget; + buildConfigurationList = 436C6B041C59595E00C4FE10 /* Build configuration list for PBXNativeTarget "loopback" */; + buildPhases = ( + 436C6AF91C59595E00C4FE10 /* Sources */, + 436C6AFA1C59595E00C4FE10 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = loopback; + productName = loopback; + productReference = 436C6AFD1C59595E00C4FE10 /* loopback */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 436C6AF51C59595E00C4FE10 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1240; + ORGANIZATIONNAME = "Benjamin Fleischer"; + TargetAttributes = { + 436C6AFC1C59595E00C4FE10 = { + CreatedOnToolsVersion = 7.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 436C6AF81C59595E00C4FE10 /* Build configuration list for PBXProject "loopback" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 436C6AF41C59595E00C4FE10; + productRefGroup = 436C6AFE1C59595E00C4FE10 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 436C6AFC1C59595E00C4FE10 /* loopback */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 436C6AF91C59595E00C4FE10 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 436C6B011C59595E00C4FE10 /* loopback.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 436C6B021C59595E00C4FE10 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 436C6B031C59595E00C4FE10 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 436C6B051C59595E00C4FE10 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = loopback/loopback.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "_FILE_OFFSET_BITS=64", + _DARWIN_USE_64_BIT_INODE, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + HEADER_SEARCH_PATHS = "\"/usr/local/include\""; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/"; + LIBRARY_SEARCH_PATHS = "\"/usr/local/lib\""; + MACOSX_DEPLOYMENT_TARGET = 10.12; + OTHER_CODE_SIGN_FLAGS = "--timestamp"; + PRODUCT_BUNDLE_IDENTIFIER = "io.macfuse.demo.loopbackfs-c"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + }; + name = Debug; + }; + 436C6B061C59595E00C4FE10 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = loopback/loopback.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "_FILE_OFFSET_BITS=64", + _DARWIN_USE_64_BIT_INODE, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + HEADER_SEARCH_PATHS = "\"/usr/local/include\""; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/"; + LIBRARY_SEARCH_PATHS = "\"/usr/local/lib\""; + MACOSX_DEPLOYMENT_TARGET = 10.12; + OTHER_CODE_SIGN_FLAGS = "--timestamp"; + PRODUCT_BUNDLE_IDENTIFIER = "io.macfuse.demo.loopbackfs-c"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 436C6AF81C59595E00C4FE10 /* Build configuration list for PBXProject "loopback" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 436C6B021C59595E00C4FE10 /* Debug */, + 436C6B031C59595E00C4FE10 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 436C6B041C59595E00C4FE10 /* Build configuration list for PBXNativeTarget "loopback" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 436C6B051C59595E00C4FE10 /* Debug */, + 436C6B061C59595E00C4FE10 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 436C6AF51C59595E00C4FE10 /* Project object */; +} diff --git a/LoopbackFS-C/loopback.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/LoopbackFS-C/loopback.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..b86bd8a --- /dev/null +++ b/LoopbackFS-C/loopback.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/LoopbackFS-C/loopback.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/LoopbackFS-C/loopback.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/LoopbackFS-C/loopback.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/LoopbackFS-C/loopback.xcodeproj/xcshareddata/xcschemes/loopback.xcscheme b/LoopbackFS-C/loopback.xcodeproj/xcshareddata/xcschemes/loopback.xcscheme new file mode 100644 index 0000000..1a3f19d --- /dev/null +++ b/LoopbackFS-C/loopback.xcodeproj/xcshareddata/xcschemes/loopback.xcscheme @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LoopbackFS-C/loopback/loopback.c b/LoopbackFS-C/loopback/loopback.c new file mode 100644 index 0000000..50a9d7a --- /dev/null +++ b/LoopbackFS-C/loopback/loopback.c @@ -0,0 +1,980 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2007 Miklos Szeredi + + This program can be distributed under the terms of the GNU GPL. + See the file LICENSE.txt. + + */ + +/* + * Loopback macFUSE file system in C. Uses the high-level FUSE API. + * Based on the fusexmp_fh.c example from the Linux FUSE distribution. + * Amit Singh + */ + +#include + +#define HAVE_EXCHANGE 0 +#define HAVE_RENAMEX 1 + +#define FUSE_USE_VERSION 26 + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_POSIX_C_SOURCE) +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; +#endif + +#define G_PREFIX "org" +#define G_KAUTH_FILESEC_XATTR G_PREFIX ".apple.system.Security" +#define A_PREFIX "com" +#define A_KAUTH_FILESEC_XATTR A_PREFIX ".apple.system.Security" +#define XATTR_APPLE_PREFIX "com.apple." + +struct loopback { + int case_insensitive; +}; + +static struct loopback loopback; + +static int +loopback_getattr(const char *path, struct stat *stbuf) +{ + int res; + + res = lstat(path, stbuf); + + /* + * The optimal I/O size can be set on a per-file basis. Setting st_blksize + * to zero will cause the kernel extension to fall back on the global I/O + * size which can be specified at mount-time (option iosize). + */ + stbuf->st_blksize = 0; + + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_fgetattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi) +{ + int res; + + (void)path; + + res = fstat(fi->fh, stbuf); + + // Fall back to global I/O size. See loopback_getattr(). + stbuf->st_blksize = 0; + + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_readlink(const char *path, char *buf, size_t size) +{ + int res; + + res = readlink(path, buf, size - 1); + if (res == -1) { + return -errno; + } + + buf[res] = '\0'; + + return 0; +} + +struct loopback_dirp { + DIR *dp; + struct dirent *entry; + off_t offset; +}; + +static int +loopback_opendir(const char *path, struct fuse_file_info *fi) +{ + int res; + + struct loopback_dirp *d = malloc(sizeof(struct loopback_dirp)); + if (d == NULL) { + return -ENOMEM; + } + + d->dp = opendir(path); + if (d->dp == NULL) { + res = -errno; + free(d); + return res; + } + + d->offset = 0; + d->entry = NULL; + + fi->fh = (unsigned long)d; + + return 0; +} + +static inline struct loopback_dirp * +get_dirp(struct fuse_file_info *fi) +{ + return (struct loopback_dirp *)(uintptr_t)fi->fh; +} + +static int +loopback_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + struct loopback_dirp *d = get_dirp(fi); + + (void)path; + + if (offset == 0) { + rewinddir(d->dp); + d->entry = NULL; + d->offset = 0; + } else if (offset != d->offset) { + seekdir(d->dp, offset); + d->entry = NULL; + d->offset = offset; + } + + while (1) { + struct stat st; + off_t nextoff; + + if (!d->entry) { + d->entry = readdir(d->dp); + if (!d->entry) { + break; + } + } + + memset(&st, 0, sizeof(st)); + st.st_ino = d->entry->d_ino; + st.st_mode = d->entry->d_type << 12; + nextoff = telldir(d->dp); + if (filler(buf, d->entry->d_name, &st, nextoff)) { + break; + } + + d->entry = NULL; + d->offset = nextoff; + } + + return 0; +} + +static int +loopback_releasedir(const char *path, struct fuse_file_info *fi) +{ + struct loopback_dirp *d = get_dirp(fi); + + (void)path; + + closedir(d->dp); + free(d); + + return 0; +} + +static int +loopback_mknod(const char *path, mode_t mode, dev_t rdev) +{ + int res; + + if (S_ISFIFO(mode)) { + res = mkfifo(path, mode); + } else { + res = mknod(path, mode, rdev); + } + + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_mkdir(const char *path, mode_t mode) +{ + int res; + + res = mkdir(path, mode); + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_unlink(const char *path) +{ + int res; + + res = unlink(path); + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_rmdir(const char *path) +{ + int res; + + res = rmdir(path); + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_symlink(const char *from, const char *to) +{ + int res; + + res = symlink(from, to); + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_rename(const char *from, const char *to) +{ + int res; + + res = rename(from, to); + if (res == -1) { + return -errno; + } + + return 0; +} + +#if HAVE_EXCHANGE + +static int +loopback_exchange(const char *path1, const char *path2, unsigned long options) +{ + int res; + + res = exchangedata(path1, path2, options); + if (res == -1) { + return -errno; + } + + return 0; +} + +#endif /* HAVE_EXCHANGE */ + +static int +loopback_link(const char *from, const char *to) +{ + int res; + + res = link(from, to); + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_fsetattr_x(const char *path, struct setattr_x *attr, + struct fuse_file_info *fi) +{ + int res; + uid_t uid = -1; + gid_t gid = -1; + + if (SETATTR_WANTS_MODE(attr)) { + res = fchmod(fi->fh, attr->mode); + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_UID(attr)) { + uid = attr->uid; + } + + if (SETATTR_WANTS_GID(attr)) { + gid = attr->gid; + } + + if ((uid != -1) || (gid != -1)) { + res = fchown(fi->fh, uid, gid); + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_SIZE(attr)) { + res = ftruncate(fi->fh, attr->size); + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_MODTIME(attr)) { + struct timeval tv[2]; + if (!SETATTR_WANTS_ACCTIME(attr)) { + gettimeofday(&tv[0], NULL); + } else { + tv[0].tv_sec = attr->acctime.tv_sec; + tv[0].tv_usec = attr->acctime.tv_nsec / 1000; + } + tv[1].tv_sec = attr->modtime.tv_sec; + tv[1].tv_usec = attr->modtime.tv_nsec / 1000; + res = futimes(fi->fh, tv); + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_CRTIME(attr)) { + struct attrlist attributes; + + attributes.bitmapcount = ATTR_BIT_MAP_COUNT; + attributes.reserved = 0; + attributes.commonattr = ATTR_CMN_CRTIME; + attributes.dirattr = 0; + attributes.fileattr = 0; + attributes.forkattr = 0; + attributes.volattr = 0; + + res = fsetattrlist(fi->fh, &attributes, &attr->crtime, + sizeof(struct timespec), FSOPT_NOFOLLOW); + + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_CHGTIME(attr)) { + struct attrlist attributes; + + attributes.bitmapcount = ATTR_BIT_MAP_COUNT; + attributes.reserved = 0; + attributes.commonattr = ATTR_CMN_CHGTIME; + attributes.dirattr = 0; + attributes.fileattr = 0; + attributes.forkattr = 0; + attributes.volattr = 0; + + res = fsetattrlist(fi->fh, &attributes, &attr->chgtime, + sizeof(struct timespec), FSOPT_NOFOLLOW); + + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_BKUPTIME(attr)) { + struct attrlist attributes; + + attributes.bitmapcount = ATTR_BIT_MAP_COUNT; + attributes.reserved = 0; + attributes.commonattr = ATTR_CMN_BKUPTIME; + attributes.dirattr = 0; + attributes.fileattr = 0; + attributes.forkattr = 0; + attributes.volattr = 0; + + res = fsetattrlist(fi->fh, &attributes, &attr->bkuptime, + sizeof(struct timespec), FSOPT_NOFOLLOW); + + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_FLAGS(attr)) { + res = fchflags(fi->fh, attr->flags); + if (res == -1) { + return -errno; + } + } + + return 0; +} + +static int +loopback_setattr_x(const char *path, struct setattr_x *attr) +{ + int res; + uid_t uid = -1; + gid_t gid = -1; + + if (SETATTR_WANTS_MODE(attr)) { + res = lchmod(path, attr->mode); + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_UID(attr)) { + uid = attr->uid; + } + + if (SETATTR_WANTS_GID(attr)) { + gid = attr->gid; + } + + if ((uid != -1) || (gid != -1)) { + res = lchown(path, uid, gid); + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_SIZE(attr)) { + res = truncate(path, attr->size); + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_MODTIME(attr)) { + struct timeval tv[2]; + if (!SETATTR_WANTS_ACCTIME(attr)) { + gettimeofday(&tv[0], NULL); + } else { + tv[0].tv_sec = attr->acctime.tv_sec; + tv[0].tv_usec = attr->acctime.tv_nsec / 1000; + } + tv[1].tv_sec = attr->modtime.tv_sec; + tv[1].tv_usec = attr->modtime.tv_nsec / 1000; + res = lutimes(path, tv); + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_CRTIME(attr)) { + struct attrlist attributes; + + attributes.bitmapcount = ATTR_BIT_MAP_COUNT; + attributes.reserved = 0; + attributes.commonattr = ATTR_CMN_CRTIME; + attributes.dirattr = 0; + attributes.fileattr = 0; + attributes.forkattr = 0; + attributes.volattr = 0; + + res = setattrlist(path, &attributes, &attr->crtime, + sizeof(struct timespec), FSOPT_NOFOLLOW); + + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_CHGTIME(attr)) { + struct attrlist attributes; + + attributes.bitmapcount = ATTR_BIT_MAP_COUNT; + attributes.reserved = 0; + attributes.commonattr = ATTR_CMN_CHGTIME; + attributes.dirattr = 0; + attributes.fileattr = 0; + attributes.forkattr = 0; + attributes.volattr = 0; + + res = setattrlist(path, &attributes, &attr->chgtime, + sizeof(struct timespec), FSOPT_NOFOLLOW); + + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_BKUPTIME(attr)) { + struct attrlist attributes; + + attributes.bitmapcount = ATTR_BIT_MAP_COUNT; + attributes.reserved = 0; + attributes.commonattr = ATTR_CMN_BKUPTIME; + attributes.dirattr = 0; + attributes.fileattr = 0; + attributes.forkattr = 0; + attributes.volattr = 0; + + res = setattrlist(path, &attributes, &attr->bkuptime, + sizeof(struct timespec), FSOPT_NOFOLLOW); + + if (res == -1) { + return -errno; + } + } + + if (SETATTR_WANTS_FLAGS(attr)) { + res = lchflags(path, attr->flags); + if (res == -1) { + return -errno; + } + } + + return 0; +} + +static int +loopback_getxtimes(const char *path, struct timespec *bkuptime, + struct timespec *crtime) +{ + int res = 0; + struct attrlist attributes; + + attributes.bitmapcount = ATTR_BIT_MAP_COUNT; + attributes.reserved = 0; + attributes.commonattr = 0; + attributes.dirattr = 0; + attributes.fileattr = 0; + attributes.forkattr = 0; + attributes.volattr = 0; + + + + struct xtimeattrbuf { + uint32_t size; + struct timespec xtime; + } __attribute__ ((packed)); + + + struct xtimeattrbuf buf; + + attributes.commonattr = ATTR_CMN_BKUPTIME; + res = getattrlist(path, &attributes, &buf, sizeof(buf), FSOPT_NOFOLLOW); + if (res == 0) { + (void)memcpy(bkuptime, &(buf.xtime), sizeof(struct timespec)); + } else { + (void)memset(bkuptime, 0, sizeof(struct timespec)); + } + + attributes.commonattr = ATTR_CMN_CRTIME; + res = getattrlist(path, &attributes, &buf, sizeof(buf), FSOPT_NOFOLLOW); + if (res == 0) { + (void)memcpy(crtime, &(buf.xtime), sizeof(struct timespec)); + } else { + (void)memset(crtime, 0, sizeof(struct timespec)); + } + + return 0; +} + +static int +loopback_create(const char *path, mode_t mode, struct fuse_file_info *fi) +{ + int fd; + + fd = open(path, fi->flags, mode); + if (fd == -1) { + return -errno; + } + + fi->fh = fd; + return 0; +} + +static int +loopback_open(const char *path, struct fuse_file_info *fi) +{ + int fd; + + fd = open(path, fi->flags); + if (fd == -1) { + return -errno; + } + + fi->fh = fd; + return 0; +} + +static int +loopback_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + int res; + + (void)path; + res = pread(fi->fh, buf, size, offset); + if (res == -1) { + res = -errno; + } + + return res; +} + +static int +loopback_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + int res; + + (void)path; + + res = pwrite(fi->fh, buf, size, offset); + if (res == -1) { + res = -errno; + } + + return res; +} + +static int +loopback_statfs(const char *path, struct statvfs *stbuf) +{ + int res; + + res = statvfs(path, stbuf); + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_flush(const char *path, struct fuse_file_info *fi) +{ + int res; + + (void)path; + + res = close(dup(fi->fh)); + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_release(const char *path, struct fuse_file_info *fi) +{ + (void)path; + + close(fi->fh); + + return 0; +} + +static int +loopback_fsync(const char *path, int isdatasync, struct fuse_file_info *fi) +{ + int res; + + (void)path; + + (void)isdatasync; + + res = fsync(fi->fh); + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_setxattr(const char *path, const char *name, const char *value, + size_t size, int flags, uint32_t position) +{ + int res; + + if (!strncmp(name, XATTR_APPLE_PREFIX, sizeof(XATTR_APPLE_PREFIX) - 1)) { + flags &= ~(XATTR_NOSECURITY); + } + + if (!strcmp(name, A_KAUTH_FILESEC_XATTR)) { + + char new_name[MAXPATHLEN]; + + memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); + memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1); + + res = setxattr(path, new_name, value, size, position, XATTR_NOFOLLOW); + + } else { + res = setxattr(path, name, value, size, position, XATTR_NOFOLLOW); + } + + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_getxattr(const char *path, const char *name, char *value, size_t size, + uint32_t position) +{ + int res; + + if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) { + + char new_name[MAXPATHLEN]; + + memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); + memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1); + + res = getxattr(path, new_name, value, size, position, XATTR_NOFOLLOW); + + } else { + res = getxattr(path, name, value, size, position, XATTR_NOFOLLOW); + } + + if (res == -1) { + return -errno; + } + + return res; +} + +static int +loopback_listxattr(const char *path, char *list, size_t size) +{ + ssize_t res = listxattr(path, list, size, XATTR_NOFOLLOW); + if (res > 0) { + if (list) { + size_t len = 0; + char *curr = list; + do { + size_t thislen = strlen(curr) + 1; + if (strcmp(curr, G_KAUTH_FILESEC_XATTR) == 0) { + memmove(curr, curr + thislen, res - len - thislen); + res -= thislen; + break; + } + curr += thislen; + len += thislen; + } while (len < res); + } else { + /* + ssize_t res2 = getxattr(path, G_KAUTH_FILESEC_XATTR, NULL, 0, 0, + XATTR_NOFOLLOW); + if (res2 >= 0) { + res -= sizeof(G_KAUTH_FILESEC_XATTR); + } + */ + } + } + + if (res == -1) { + return -errno; + } + + return res; +} + +static int +loopback_removexattr(const char *path, const char *name) +{ + int res; + + if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) { + + char new_name[MAXPATHLEN]; + + memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); + memcpy(new_name, G_PREFIX, sizeof(G_PREFIX) - 1); + + res = removexattr(path, new_name, XATTR_NOFOLLOW); + + } else { + res = removexattr(path, name, XATTR_NOFOLLOW); + } + + if (res == -1) { + return -errno; + } + + return 0; +} + +static int +loopback_fallocate(const char *path, int mode, off_t offset, off_t length, + struct fuse_file_info *fi) +{ + fstore_t fstore; + + if (!(mode & PREALLOCATE)) { + return -ENOTSUP; + } + + fstore.fst_flags = 0; + if (mode & ALLOCATECONTIG) { + fstore.fst_flags |= F_ALLOCATECONTIG; + } + if (mode & ALLOCATEALL) { + fstore.fst_flags |= F_ALLOCATEALL; + } + + if (mode & ALLOCATEFROMPEOF) { + fstore.fst_posmode = F_PEOFPOSMODE; + } else if (mode & ALLOCATEFROMVOL) { + fstore.fst_posmode = F_VOLPOSMODE; + } + + fstore.fst_offset = offset; + fstore.fst_length = length; + + if (fcntl(fi->fh, F_PREALLOCATE, &fstore) == -1) { + return -errno; + } else { + return 0; + } +} + +static int +loopback_setvolname(const char *name) +{ + return 0; +} + +#if HAVE_RENAMEX + +static int +loopback_renamex(const char *path1, const char *path2, unsigned int flags) +{ + int res; + + res = renamex_np(path1, path2, flags); + if (res == -1) { + return -errno; + } + + return 0; +} + +#endif /* HAVE_RENAMEX */ + +void * +loopback_init(struct fuse_conn_info *conn) +{ + FUSE_ENABLE_SETVOLNAME(conn); + FUSE_ENABLE_XTIMES(conn); + +#ifdef FUSE_ENABLE_CASE_INSENSITIVE + if (loopback.case_insensitive) { + FUSE_ENABLE_CASE_INSENSITIVE(conn); + } +#endif + + return NULL; +} + +void +loopback_destroy(void *userdata) +{ + /* nothing */ +} + +static struct fuse_operations loopback_oper = { + .init = loopback_init, + .destroy = loopback_destroy, + .getattr = loopback_getattr, + .fgetattr = loopback_fgetattr, + /* .access = loopback_access, */ + .readlink = loopback_readlink, + .opendir = loopback_opendir, + .readdir = loopback_readdir, + .releasedir = loopback_releasedir, + .mknod = loopback_mknod, + .mkdir = loopback_mkdir, + .symlink = loopback_symlink, + .unlink = loopback_unlink, + .rmdir = loopback_rmdir, + .rename = loopback_rename, + .link = loopback_link, + .create = loopback_create, + .open = loopback_open, + .read = loopback_read, + .write = loopback_write, + .statfs = loopback_statfs, + .flush = loopback_flush, + .release = loopback_release, + .fsync = loopback_fsync, + .setxattr = loopback_setxattr, + .getxattr = loopback_getxattr, + .listxattr = loopback_listxattr, + .removexattr = loopback_removexattr, +#if HAVE_EXCHANGE + .exchange = loopback_exchange, +#endif + .getxtimes = loopback_getxtimes, + .setattr_x = loopback_setattr_x, + .fsetattr_x = loopback_fsetattr_x, + .fallocate = loopback_fallocate, + .setvolname = loopback_setvolname, +#if HAVE_RENAMEX + .renamex = loopback_renamex, +#endif + + .flag_nullpath_ok = 1, + .flag_nopath = 1, +}; + +static const struct fuse_opt loopback_opts[] = { + { "case_insensitive", offsetof(struct loopback, case_insensitive), 1 }, + FUSE_OPT_END +}; + +int +main(int argc, char *argv[]) +{ + int res = 0; + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + + loopback.case_insensitive = 0; + if (fuse_opt_parse(&args, &loopback, loopback_opts, NULL) == -1) { + exit(1); + } + + umask(0); + res = fuse_main(args.argc, args.argv, &loopback_oper, NULL); + + fuse_opt_free_args(&args); + return res; +} diff --git a/LoopbackFS-C/loopback/loopback.entitlements b/LoopbackFS-C/loopback/loopback.entitlements new file mode 100644 index 0000000..8cc185a --- /dev/null +++ b/LoopbackFS-C/loopback/loopback.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.disable-library-validation + + + diff --git a/LoopbackFS-ObjC/LICENSE.txt b/LoopbackFS-ObjC/LICENSE.txt new file mode 100644 index 0000000..ac3cc2a --- /dev/null +++ b/LoopbackFS-ObjC/LICENSE.txt @@ -0,0 +1,13 @@ +Copyright 2007-2008 Google Inc. +Copyright 2011-2021 Benjamin Fleischer + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. diff --git a/LoopbackFS-ObjC/LoopbackFS.xcodeproj/project.pbxproj b/LoopbackFS-ObjC/LoopbackFS.xcodeproj/project.pbxproj new file mode 100644 index 0000000..f3c9736 --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS.xcodeproj/project.pbxproj @@ -0,0 +1,360 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 43C857CB264AC68C0021A8BC /* macFUSE.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43C857CA264AC68C0021A8BC /* macFUSE.framework */; }; + 43E953A22646E241009CCB55 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 43E953A02646E241009CCB55 /* MainMenu.xib */; }; + 43E953C62646F170009CCB55 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 43E953C52646F170009CCB55 /* Assets.xcassets */; }; + 8B93BEEF0D3ACD4400466F25 /* NSError+POSIX.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B93BEED0D3ACD4400466F25 /* NSError+POSIX.m */; }; + 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; + FF8358230D24649E002F2357 /* LoopbackFS.m in Sources */ = {isa = PBXBuildFile; fileRef = FF8358220D24649E002F2357 /* LoopbackFS.m */; }; + FF98B8B80D24909700117156 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FF98B8B70D24909700117156 /* AppDelegate.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 431DC7D2227DF080001A3694 /* LoopbackFS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LoopbackFS.entitlements; sourceTree = ""; }; + 43C857CA264AC68C0021A8BC /* macFUSE.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = macFUSE.framework; path = ../../../../../../../Library/Frameworks/macFUSE.framework; sourceTree = ""; }; + 43E9539D2646E1EA009CCB55 /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; + 43E953A12646E241009CCB55 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 43E953C52646F170009CCB55 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8B93BEED0D3ACD4400466F25 /* NSError+POSIX.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = "NSError+POSIX.m"; sourceTree = ""; tabWidth = 2; }; + 8B93BEEE0D3ACD4400466F25 /* NSError+POSIX.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = "NSError+POSIX.h"; sourceTree = ""; tabWidth = 2; }; + 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D1107320486CEB800E47090 /* LoopbackFS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LoopbackFS.app; sourceTree = BUILT_PRODUCTS_DIR; }; + FF8358210D24649E002F2357 /* LoopbackFS.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = LoopbackFS.h; sourceTree = ""; tabWidth = 2; }; + FF8358220D24649E002F2357 /* LoopbackFS.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = LoopbackFS.m; sourceTree = ""; tabWidth = 2; }; + FF98B8B60D24909700117156 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; tabWidth = 2; }; + FF98B8B70D24909700117156 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; tabWidth = 2; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D11072E0486CEB800E47090 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 43C857CB264AC68C0021A8BC /* macFUSE.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* LoopbackFS */ = { + isa = PBXGroup; + children = ( + FF98B8B60D24909700117156 /* AppDelegate.h */, + FF98B8B70D24909700117156 /* AppDelegate.m */, + 43E953C52646F170009CCB55 /* Assets.xcassets */, + 8D1107310486CEB800E47090 /* Info.plist */, + 431DC7D2227DF080001A3694 /* LoopbackFS.entitlements */, + FF8358210D24649E002F2357 /* LoopbackFS.h */, + FF8358220D24649E002F2357 /* LoopbackFS.m */, + 29B97316FDCFA39411CA2CEA /* main.m */, + 43E953A02646E241009CCB55 /* MainMenu.xib */, + 8B93BEEE0D3ACD4400466F25 /* NSError+POSIX.h */, + 8B93BEED0D3ACD4400466F25 /* NSError+POSIX.m */, + ); + path = LoopbackFS; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8D1107320486CEB800E47090 /* LoopbackFS.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* LoopbackFS */ = { + isa = PBXGroup; + children = ( + 43E9539D2646E1EA009CCB55 /* LICENSE.txt */, + 080E96DDFE201D6D7F000001 /* LoopbackFS */, + 43C857C9264AC68C0021A8BC /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = LoopbackFS; + sourceTree = ""; + }; + 43C857C9264AC68C0021A8BC /* Frameworks */ = { + isa = PBXGroup; + children = ( + 43C857CA264AC68C0021A8BC /* macFUSE.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D1107260486CEB800E47090 /* LoopbackFS */ = { + isa = PBXNativeTarget; + buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "LoopbackFS" */; + buildPhases = ( + 8D11072C0486CEB800E47090 /* Sources */, + 8D11072E0486CEB800E47090 /* Frameworks */, + 8D1107290486CEB800E47090 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = LoopbackFS; + productInstallPath = "$(HOME)/Applications"; + productName = LoopbackFS; + productReference = 8D1107320486CEB800E47090 /* LoopbackFS.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1240; + TargetAttributes = { + 8D1107260486CEB800E47090 = { + SystemCapabilities = { + com.apple.HardenedRuntime = { + enabled = 1; + }; + }; + }; + }; + }; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "LoopbackFS" */; + compatibilityVersion = "Xcode 3.1"; + developmentRegion = en; + hasScannedForEncodings = 1; + knownRegions = ( + en, + Base, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* LoopbackFS */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D1107260486CEB800E47090 /* LoopbackFS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D1107290486CEB800E47090 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 43E953C62646F170009CCB55 /* Assets.xcassets in Resources */, + 43E953A22646E241009CCB55 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D11072C0486CEB800E47090 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072D0486CEB800E47090 /* main.m in Sources */, + FF8358230D24649E002F2357 /* LoopbackFS.m in Sources */, + FF98B8B80D24909700117156 /* AppDelegate.m in Sources */, + 8B93BEEF0D3ACD4400466F25 /* NSError+POSIX.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 43E953A02646E241009CCB55 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 43E953A12646E241009CCB55 /* Base */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + C01FCF4B08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_ENTITLEMENTS = LoopbackFS/LoopbackFS.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + FRAMEWORK_SEARCH_PATHS = /Library/Frameworks; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + INFOPLIST_FILE = LoopbackFS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; + PRODUCT_BUNDLE_IDENTIFIER = "io.macfuse.demo.loopbackfs-objc"; + PRODUCT_NAME = LoopbackFS; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + C01FCF4C08A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_OBJC_ARC = NO; + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_ENTITLEMENTS = LoopbackFS/LoopbackFS.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + FRAMEWORK_SEARCH_PATHS = /Library/Frameworks; + INFOPLIST_FILE = LoopbackFS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; + PRODUCT_BUNDLE_IDENTIFIER = "io.macfuse.demo.loopbackfs-objc"; + PRODUCT_NAME = LoopbackFS; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CODE_SIGN_FLAGS = "--timestamp"; + SDKROOT = macosx; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CODE_SIGN_FLAGS = "--timestamp"; + SDKROOT = macosx; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "LoopbackFS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4B08A954540054247B /* Debug */, + C01FCF4C08A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "LoopbackFS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/LoopbackFS-ObjC/LoopbackFS.xcodeproj/xcshareddata/xcschemes/LoopbackFS.xcscheme b/LoopbackFS-ObjC/LoopbackFS.xcodeproj/xcshareddata/xcschemes/LoopbackFS.xcscheme new file mode 100644 index 0000000..b72fcb3 --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS.xcodeproj/xcshareddata/xcschemes/LoopbackFS.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LoopbackFS-ObjC/LoopbackFS/AppDelegate.h b/LoopbackFS-ObjC/LoopbackFS/AppDelegate.h new file mode 100644 index 0000000..dc65176 --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/AppDelegate.h @@ -0,0 +1,33 @@ +// ================================================================ +// Copyright (C) 2007 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ================================================================ +// +// AppDelegate.h +// LoopbackFS +// +// Created by ted on 12/27/07. +// + +#import +#import + +#import "LoopbackFS.h" + +@interface AppDelegate : NSObject { + GMUserFileSystem* fs_; + LoopbackFS* loop_; +} + +@end diff --git a/LoopbackFS-ObjC/LoopbackFS/AppDelegate.m b/LoopbackFS-ObjC/LoopbackFS/AppDelegate.m new file mode 100644 index 0000000..ecb2feb --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/AppDelegate.m @@ -0,0 +1,118 @@ +// ================================================================ +// Copyright (C) 2007 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ================================================================ +// +// AppDelegate.m +// LoopbackFS +// +// Created by ted on 12/27/07. +// +#import "AppDelegate.h" + +#import + +#import + +#import "LoopbackFS.h" + +static NSString *LoopbackMountPath = @"/Volumes/loop"; + +@implementation AppDelegate + +- (void)mountFailed:(NSNotification *)notification { + NSLog(@"Got mountFailed notification."); + + NSDictionary* userInfo = [notification userInfo]; + NSError* error = [userInfo objectForKey:kGMUserFileSystemErrorKey]; + NSLog(@"kGMUserFileSystem Error: %@, userInfo=%@", error, [error userInfo]); + + dispatch_async(dispatch_get_main_queue(), ^{ + NSAlert* alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Mount Failed"]; + [alert setInformativeText:[error localizedDescription] ?: @"Unknown error"]; + [alert runModal]; + + [[NSApplication sharedApplication] terminate:nil]; + }); +} + +- (void)didMount:(NSNotification *)notification { + NSLog(@"Got didMount notification."); + + NSString *parentPath = [LoopbackMountPath stringByDeletingLastPathComponent]; + [[NSWorkspace sharedWorkspace] selectFile:LoopbackMountPath + inFileViewerRootedAtPath:parentPath]; +} + +- (void)didUnmount:(NSNotification*)notification { + NSLog(@"Got didUnmount notification."); + + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSApplication sharedApplication] terminate:nil]; + }); +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification { + NSOpenPanel* panel = [NSOpenPanel openPanel]; + [panel setCanChooseFiles:NO]; + [panel setCanChooseDirectories:YES]; + [panel setAllowsMultipleSelection:NO]; + [panel setDirectoryURL:[NSURL fileURLWithPath:@"/tmp"]]; + NSModalResponse ret = [panel runModal]; + + if ( ret == NSModalResponseCancel ) { + exit(0); + } + NSArray* paths = [panel URLs]; + if ( [paths count] != 1 ) { + exit(0); + } + NSString* rootPath = nil; + rootPath = [[paths objectAtIndex:0] path]; + + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + [center addObserver:self selector:@selector(mountFailed:) + name:kGMUserFileSystemMountFailed object:nil]; + [center addObserver:self selector:@selector(didMount:) + name:kGMUserFileSystemDidMount object:nil]; + [center addObserver:self selector:@selector(didUnmount:) + name:kGMUserFileSystemDidUnmount object:nil]; + + loop_ = [[LoopbackFS alloc] initWithRootPath:rootPath]; + + fs_ = [[GMUserFileSystem alloc] initWithDelegate:loop_ isThreadSafe:NO]; + + NSMutableArray* options = [NSMutableArray array]; + + // Do not use the 'native_xattr' mount-time option unless the underlying + // file system supports native extended attributes. Typically, the user + // would be mounting an HFS+ directory through LoopbackFS, so we do want + // this option in that case. + [options addObject:@"native_xattr"]; + + [options addObject:@"volname=LoopbackFS"]; + [fs_ mountAtPath:LoopbackMountPath + withOptions:options]; +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [fs_ unmount]; + [fs_ release]; + [loop_ release]; + return NSTerminateNow; +} + +@end diff --git a/LoopbackFS-ObjC/LoopbackFS/Assets.xcassets/AccentColor.colorset/Contents.json b/LoopbackFS-ObjC/LoopbackFS/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LoopbackFS-ObjC/LoopbackFS/Assets.xcassets/AppIcon.appiconset/Contents.json b/LoopbackFS-ObjC/LoopbackFS/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..50ab7bd --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/LoopbackFS-ObjC/LoopbackFS/Assets.xcassets/Contents.json b/LoopbackFS-ObjC/LoopbackFS/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LoopbackFS-ObjC/LoopbackFS/Base.lproj/MainMenu.xib b/LoopbackFS-ObjC/LoopbackFS/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..cf79921 --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/Base.lproj/MainMenu.xib @@ -0,0 +1,680 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LoopbackFS-ObjC/LoopbackFS/Info.plist b/LoopbackFS-ObjC/LoopbackFS/Info.plist new file mode 100644 index 0000000..e51ead4 --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/LoopbackFS-ObjC/LoopbackFS/LoopbackFS.entitlements b/LoopbackFS-ObjC/LoopbackFS/LoopbackFS.entitlements new file mode 100644 index 0000000..8cc185a --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/LoopbackFS.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.disable-library-validation + + + diff --git a/LoopbackFS-ObjC/LoopbackFS/LoopbackFS.h b/LoopbackFS-ObjC/LoopbackFS/LoopbackFS.h new file mode 100644 index 0000000..509e4ef --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/LoopbackFS.h @@ -0,0 +1,37 @@ +// ================================================================ +// Copyright (C) 2007 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ================================================================ +// +// LoopbackFS.h +// LoopbackFS +// +// Created by ted on 12/12/07. +// +// This is a simple but complete example filesystem that mounts a local +// directory. You can modify this to see how the Finder reacts to returning +// specific error codes or not implementing a particular GMUserFileSystem +// operation. +// +// For example, you can mount "/tmp" in /Volumes/loop. Note: It is +// probably not a good idea to mount "/" through this filesystem. + +#import + +@interface LoopbackFS : NSObject { + NSString* rootPath_; // The local file-system path to mount. +} +- (id)initWithRootPath:(NSString *)rootPath; + +@end diff --git a/LoopbackFS-ObjC/LoopbackFS/LoopbackFS.m b/LoopbackFS-ObjC/LoopbackFS/LoopbackFS.m new file mode 100644 index 0000000..d9e1c94 --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/LoopbackFS.m @@ -0,0 +1,479 @@ +// ================================================================ +// Copyright (C) 2007 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ================================================================ +// +// LoopbackFS.m +// LoopbackFS +// +// Created by ted on 12/12/07. +// +// This is a simple but complete example filesystem that mounts a local +// directory. You can modify this to see how the Finder reacts to returning +// specific error codes or not implementing a particular GMUserFileSystem +// operation. +// +// For example, you can mount "/tmp" in /Volumes/loop. Note: It is +// probably not a good idea to mount "/" through this filesystem. + +#import "LoopbackFS.h" + +#import + +#import +#import +#import + +#import "NSError+POSIX.h" + +#define HAVE_EXCHANE 0 + +@implementation LoopbackFS + +- (id)initWithRootPath:(NSString *)rootPath { + if ((self = [super init])) { + rootPath_ = [rootPath retain]; + } + return self; +} + +- (void) dealloc { + [rootPath_ release]; + [super dealloc]; +} + +#pragma mark Moving an Item + +- (BOOL)moveItemAtPath:(NSString *)source + toPath:(NSString *)destination + options:(GMUserFileSystemMoveOption)options + error:(NSError **)error { + // We use rename directly here since NSFileManager can sometimes fail to + // rename and return non-posix error codes. + NSString* p_src = [rootPath_ stringByAppendingString:source]; + NSString* p_dst = [rootPath_ stringByAppendingString:destination]; + int ret = 0; + if (options == 0) { + ret = rename([p_src UTF8String], [p_dst UTF8String]); + } else { + unsigned int flags = 0; + if (options & GMUserFileSystemMoveOptionSwap) { + flags |= RENAME_SWAP; + } + if (options & GMUserFileSystemMoveOptionExclusive) { + flags |= RENAME_EXCL; + } + ret = renamex_np([p_src UTF8String], [p_dst UTF8String], flags); + } + if ( ret < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return NO; + } + return YES; +} + +#pragma mark Removing an Item + +- (BOOL)removeDirectoryAtPath:(NSString *)path error:(NSError **)error { + // We need to special-case directories here and use the bsd API since + // NSFileManager will happily do a recursive remove :-( + NSString* p = [rootPath_ stringByAppendingString:path]; + int ret = rmdir([p UTF8String]); + if (ret < 0) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return NO; + } + return YES; +} + +- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error { + // NOTE: If removeDirectoryAtPath is commented out, then this may be called + // with a directory, in which case NSFileManager will recursively remove all + // subdirectories. So be careful! + NSString* p = [rootPath_ stringByAppendingString:path]; + return [[NSFileManager defaultManager] removeItemAtPath:p error:error]; +} + +#pragma mark Creating an Item + +- (BOOL)createDirectoryAtPath:(NSString *)path + attributes:(NSDictionary *)attributes + error:(NSError **)error { + NSString* p = [rootPath_ stringByAppendingString:path]; + return [[NSFileManager defaultManager] createDirectoryAtPath:p + withIntermediateDirectories:NO + attributes:attributes + error:error]; +} + +- (BOOL)createFileAtPath:(NSString *)path + attributes:(NSDictionary *)attributes + flags:(int)flags + userData:(id *)userData + error:(NSError **)error { + NSString* p = [rootPath_ stringByAppendingString:path]; + mode_t mode = [[attributes objectForKey:NSFilePosixPermissions] longValue]; + int fd = open([p UTF8String], flags, mode); + if ( fd < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return NO; + } + *userData = [NSNumber numberWithLong:fd]; + return YES; +} + +#pragma mark Linking an Item + +- (BOOL)linkItemAtPath:(NSString *)path + toPath:(NSString *)otherPath + error:(NSError **)error { + NSString* p_path = [rootPath_ stringByAppendingString:path]; + NSString* p_otherPath = [rootPath_ stringByAppendingString:otherPath]; + + // We use link rather than the NSFileManager equivalent because it will copy + // the file rather than hard link if part of the root path is a symlink. + int rc = link([p_path UTF8String], [p_otherPath UTF8String]); + if ( rc < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return NO; + } + return YES; +} + +#pragma mark Symbolic Links + +- (BOOL)createSymbolicLinkAtPath:(NSString *)path + withDestinationPath:(NSString *)otherPath + error:(NSError **)error { + NSString* p_src = [rootPath_ stringByAppendingString:path]; + return [[NSFileManager defaultManager] createSymbolicLinkAtPath:p_src + withDestinationPath:otherPath + error:error]; +} + +- (NSString *)destinationOfSymbolicLinkAtPath:(NSString *)path + error:(NSError **)error { + NSString* p = [rootPath_ stringByAppendingString:path]; + return [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath:p + error:error]; +} + +#pragma mark File Contents + +- (BOOL)openFileAtPath:(NSString *)path + mode:(int)mode + userData:(id *)userData + error:(NSError **)error { + NSString* p = [rootPath_ stringByAppendingString:path]; + int fd = open([p UTF8String], mode); + if ( fd < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return NO; + } + *userData = [NSNumber numberWithLong:fd]; + return YES; +} + +- (void)releaseFileAtPath:(NSString *)path userData:(id)userData { + NSNumber* num = (NSNumber *)userData; + int fd = [num intValue]; + close(fd); +} + +- (int)readFileAtPath:(NSString *)path + userData:(id)userData + buffer:(char *)buffer + size:(size_t)size + offset:(off_t)offset + error:(NSError **)error { + NSNumber* num = (NSNumber *)userData; + int fd = [num intValue]; + size_t ret = pread(fd, buffer, size, offset); + if ( ret < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return -1; + } + return (int)ret; +} + +- (int)writeFileAtPath:(NSString *)path + userData:(id)userData + buffer:(const char *)buffer + size:(size_t)size + offset:(off_t)offset + error:(NSError **)error { + NSNumber* num = (NSNumber *)userData; + int fd = [num intValue]; + size_t ret = pwrite(fd, buffer, size, offset); + if ( ret < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return -1; + } + return (int)ret; +} + +- (BOOL)preallocateFileAtPath:(NSString *)path + userData:(id)userData + options:(int)options + offset:(off_t)offset + length:(off_t)length + error:(NSError **)error { + NSNumber* num = (NSNumber *)userData; + int fd = [num intValue]; + + fstore_t fstore; + + fstore.fst_flags = 0; + if ( options & ALLOCATECONTIG ) { + fstore.fst_flags |= F_ALLOCATECONTIG; + } + if ( options & ALLOCATEALL ) { + fstore.fst_flags |= F_ALLOCATEALL; + } + + if ( options & ALLOCATEFROMPEOF ) { + fstore.fst_posmode = F_PEOFPOSMODE; + } else if ( options & ALLOCATEFROMVOL ) { + fstore.fst_posmode = F_VOLPOSMODE; + } + + fstore.fst_offset = offset; + fstore.fst_length = length; + + if ( fcntl(fd, F_PREALLOCATE, &fstore) == -1 ) { + *error = [NSError errorWithPOSIXCode:errno]; + return NO; + } + return YES; +} + +#if HAVE_EXCHANE + +- (BOOL)exchangeDataOfItemAtPath:(NSString *)path1 + withItemAtPath:(NSString *)path2 + error:(NSError **)error { + NSString* p1 = [rootPath_ stringByAppendingString:path1]; + NSString* p2 = [rootPath_ stringByAppendingString:path2]; + int ret = exchangedata([p1 UTF8String], [p2 UTF8String], 0); + if ( ret < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return NO; + } + return YES; +} + +#endif /* HAVE_EXCHANGE */ + +#pragma mark Directory Contents + +- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error { + NSString* p = [rootPath_ stringByAppendingString:path]; + return [[NSFileManager defaultManager] contentsOfDirectoryAtPath:p error:error]; +} + +#pragma mark Getting and Setting Attributes + +- (NSDictionary *)attributesOfItemAtPath:(NSString *)path + userData:(id)userData + error:(NSError **)error { + NSString* p = [rootPath_ stringByAppendingString:path]; + NSDictionary* attribs = + [[NSFileManager defaultManager] attributesOfItemAtPath:p error:error]; + return attribs; +} + +- (NSDictionary *)attributesOfFileSystemForPath:(NSString *)path + error:(NSError **)error { + NSString* p = [rootPath_ stringByAppendingString:path]; + NSDictionary* d = + [[NSFileManager defaultManager] attributesOfFileSystemForPath:p error:error]; + if (d) { + NSMutableDictionary* attribs = [NSMutableDictionary dictionaryWithDictionary:d]; + [attribs setObject:[NSNumber numberWithBool:YES] + forKey:kGMUserFileSystemVolumeSupportsExtendedDatesKey]; + + NSURL *URL = [NSURL fileURLWithPath:p isDirectory:YES]; + NSNumber *supportsCaseSensitiveNames = nil; + [URL getResourceValue:&supportsCaseSensitiveNames + forKey:NSURLVolumeSupportsCaseSensitiveNamesKey + error:NULL]; + if (supportsCaseSensitiveNames == nil) { + supportsCaseSensitiveNames = [NSNumber numberWithBool:YES]; + } + [attribs setObject:supportsCaseSensitiveNames + forKey:kGMUserFileSystemVolumeSupportsCaseSensitiveNamesKey]; + + [attribs setObject:[NSNumber numberWithBool:YES] + forKey:kGMUserFileSystemVolumeSupportsSwapRenamingKey]; + [attribs setObject:[NSNumber numberWithBool:YES] + forKey:kGMUserFileSystemVolumeSupportsExclusiveRenamingKey]; + + [attribs setObject:[NSNumber numberWithBool:YES] + forKey:kGMUserFileSystemVolumeSupportsSetVolumeNameKey]; + + return attribs; + } + return nil; +} + +- (BOOL)setAttributes:(NSDictionary *)attributes + ofItemAtPath:(NSString *)path + userData:(id)userData + error:(NSError **)error { + NSString* p = [rootPath_ stringByAppendingString:path]; + + // TODO: Handle other keys not handled by NSFileManager setAttributes call. + + NSNumber* offset = [attributes objectForKey:NSFileSize]; + if ( offset ) { + int ret = truncate([p UTF8String], [offset longLongValue]); + if ( ret < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return NO; + } + } + NSNumber* flags = [attributes objectForKey:kGMUserFileSystemFileFlagsKey]; + if (flags != nil) { + int rc = chflags([p UTF8String], [flags intValue]); + if (rc < 0) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return NO; + } + } + return [[NSFileManager defaultManager] setAttributes:attributes + ofItemAtPath:p + error:error]; +} + +- (BOOL)setAttributes:(NSDictionary *)attributes + ofFileSystemAtPath:(NSString *)path + error:(NSError **)error { + return YES; +} + +#pragma mark Extended Attributes + +- (NSArray *)extendedAttributesOfItemAtPath:(NSString *)path error:(NSError **)error { + NSString* p = [rootPath_ stringByAppendingString:path]; + + ssize_t size = listxattr([p UTF8String], nil, 0, XATTR_NOFOLLOW); + if ( size < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return nil; + } + NSMutableData* data = [NSMutableData dataWithLength:size]; + size = listxattr([p UTF8String], [data mutableBytes], [data length], XATTR_NOFOLLOW); + if ( size < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return nil; + } + NSMutableArray* contents = [NSMutableArray array]; + char* ptr = (char *)[data bytes]; + while ( ptr < ((char *)[data bytes] + size) ) { + NSString* s = [NSString stringWithUTF8String:ptr]; + [contents addObject:s]; + ptr += ([s length] + 1); + } + return contents; +} + +- (NSData *)valueOfExtendedAttribute:(NSString *)name + ofItemAtPath:(NSString *)path + position:(off_t)position + error:(NSError **)error { + NSString* p = [rootPath_ stringByAppendingString:path]; + + ssize_t size = getxattr([p UTF8String], [name UTF8String], nil, 0, + (uint32_t)position, XATTR_NOFOLLOW); + if ( size < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return nil; + } + NSMutableData* data = [NSMutableData dataWithLength:size]; + size = getxattr([p UTF8String], [name UTF8String], [data mutableBytes], + [data length], (uint32_t)position, XATTR_NOFOLLOW); + if ( size < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return nil; + } + return data; +} + +- (BOOL)setExtendedAttribute:(NSString *)name + ofItemAtPath:(NSString *)path + value:(NSData *)value + position:(off_t)position + options:(int)options + error:(NSError **)error { + // Setting com.apple.FinderInfo happens in the kernel, so security related + // bits are set in the options. We need to explicitly remove them or the call + // to setxattr will fail. + // TODO: Why is this necessary? + options &= ~(XATTR_NOSECURITY | XATTR_NODEFAULT); + NSString* p = [rootPath_ stringByAppendingString:path]; + int ret = setxattr([p UTF8String], [name UTF8String], [value bytes], + [value length], (uint32_t)position, + options | XATTR_NOFOLLOW); + if ( ret < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return NO; + } + return YES; +} + +- (BOOL)removeExtendedAttribute:(NSString *)name + ofItemAtPath:(NSString *)path + error:(NSError **)error { + NSString* p = [rootPath_ stringByAppendingString:path]; + int ret = removexattr([p UTF8String], [name UTF8String], XATTR_NOFOLLOW); + if ( ret < 0 ) { + if ( error ) { + *error = [NSError errorWithPOSIXCode:errno]; + } + return NO; + } + return YES; +} + +@end diff --git a/LoopbackFS-ObjC/LoopbackFS/NSError+POSIX.h b/LoopbackFS-ObjC/LoopbackFS/NSError+POSIX.h new file mode 100644 index 0000000..3d29570 --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/NSError+POSIX.h @@ -0,0 +1,26 @@ +// +// NSError+POSIX.h +// +// ================================================================ +// Copyright (C) 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ================================================================ +// + +#import + +// Category on NSError to simplify creating an NSError based on posix errno. +@interface NSError (POSIX) ++ (NSError *)errorWithPOSIXCode:(int)code; +@end diff --git a/LoopbackFS-ObjC/LoopbackFS/NSError+POSIX.m b/LoopbackFS-ObjC/LoopbackFS/NSError+POSIX.m new file mode 100644 index 0000000..77d9155 --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/NSError+POSIX.m @@ -0,0 +1,28 @@ +// +// NSError+POSIX.m +// +// ================================================================ +// Copyright (C) 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ================================================================ +// + +#import "NSError+POSIX.h" + +@implementation NSError (POSIX) ++ (NSError *)errorWithPOSIXCode:(int) code { + return [NSError errorWithDomain:NSPOSIXErrorDomain code:code userInfo:nil]; +} +@end + diff --git a/LoopbackFS-ObjC/LoopbackFS/main.m b/LoopbackFS-ObjC/LoopbackFS/main.m new file mode 100644 index 0000000..477d5e5 --- /dev/null +++ b/LoopbackFS-ObjC/LoopbackFS/main.m @@ -0,0 +1,30 @@ +// ================================================================ +// Copyright (C) 2007 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ================================================================ +// +// main.m +// LoopbackFS +// +// Created by ted on 12/27/07. +// + +#import + +int main(int argc, const char * argv[]) { + @autoreleasepool { + // Setup code that might create autoreleased objects goes here. + } + return NSApplicationMain(argc, argv); +} diff --git a/LoopbackFS-Swift/LICENSE.txt b/LoopbackFS-Swift/LICENSE.txt new file mode 100644 index 0000000..3d709bf --- /dev/null +++ b/LoopbackFS-Swift/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright 2017 KF Interactive GmbH +Copyright 2018-2021 Benjamin Fleischer + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/LoopbackFS-Swift/LoopbackFS.xcodeproj/project.pbxproj b/LoopbackFS-Swift/LoopbackFS.xcodeproj/project.pbxproj new file mode 100644 index 0000000..cd2d117 --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS.xcodeproj/project.pbxproj @@ -0,0 +1,360 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 43C857D8264ACAAE0021A8BC /* macFUSE.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43C857D7264ACAAE0021A8BC /* macFUSE.framework */; }; + 43E9539A2646E19C009CCB55 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 43E953992646E19C009CCB55 /* Assets.xcassets */; }; + F48D00F31E3B6EC000FE033E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48D00F21E3B6EC000FE033E /* AppDelegate.swift */; }; + F48D00F81E3B6EC000FE033E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F48D00F61E3B6EC000FE033E /* MainMenu.xib */; }; + F48D01071E3B74F900FE033E /* LoopbackFS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48D01061E3B74F900FE033E /* LoopbackFS.swift */; }; + F48D01091E3B7DA500FE033E /* NSError+POSIX.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48D01081E3B7DA500FE033E /* NSError+POSIX.swift */; }; + F4D7409B1E65585B007D6E11 /* LICENSE.txt in Resources */ = {isa = PBXBuildFile; fileRef = F4D7409A1E65585B007D6E11 /* LICENSE.txt */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 43C857D7264ACAAE0021A8BC /* macFUSE.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = macFUSE.framework; path = ../../../../../../../Library/Frameworks/macFUSE.framework; sourceTree = ""; }; + 43E953992646E19C009CCB55 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 43EBE3672360F9D300E3A7F5 /* LoopbackFS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LoopbackFS.entitlements; sourceTree = ""; }; + F48D00EF1E3B6EC000FE033E /* LoopbackFS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LoopbackFS.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F48D00F21E3B6EC000FE033E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + F48D00F71E3B6EC000FE033E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + F48D00F91E3B6EC000FE033E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F48D01051E3B716E00FE033E /* Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Bridging-Header.h"; sourceTree = ""; }; + F48D01061E3B74F900FE033E /* LoopbackFS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoopbackFS.swift; sourceTree = ""; }; + F48D01081E3B7DA500FE033E /* NSError+POSIX.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSError+POSIX.swift"; sourceTree = ""; }; + F4D7409A1E65585B007D6E11 /* LICENSE.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + F48D00EC1E3B6EC000FE033E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 43C857D8264ACAAE0021A8BC /* macFUSE.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 43C857D6264ACAAE0021A8BC /* Frameworks */ = { + isa = PBXGroup; + children = ( + 43C857D7264ACAAE0021A8BC /* macFUSE.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + F48D00E61E3B6EC000FE033E = { + isa = PBXGroup; + children = ( + F4D7409A1E65585B007D6E11 /* LICENSE.txt */, + F48D00F11E3B6EC000FE033E /* LoopbackFS */, + 43C857D6264ACAAE0021A8BC /* Frameworks */, + F48D00F01E3B6EC000FE033E /* Products */, + ); + sourceTree = ""; + }; + F48D00F01E3B6EC000FE033E /* Products */ = { + isa = PBXGroup; + children = ( + F48D00EF1E3B6EC000FE033E /* LoopbackFS.app */, + ); + name = Products; + sourceTree = ""; + }; + F48D00F11E3B6EC000FE033E /* LoopbackFS */ = { + isa = PBXGroup; + children = ( + F48D00F21E3B6EC000FE033E /* AppDelegate.swift */, + 43E953992646E19C009CCB55 /* Assets.xcassets */, + F48D01051E3B716E00FE033E /* Bridging-Header.h */, + F48D00F91E3B6EC000FE033E /* Info.plist */, + 43EBE3672360F9D300E3A7F5 /* LoopbackFS.entitlements */, + F48D01061E3B74F900FE033E /* LoopbackFS.swift */, + F48D00F61E3B6EC000FE033E /* MainMenu.xib */, + F48D01081E3B7DA500FE033E /* NSError+POSIX.swift */, + ); + path = LoopbackFS; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F48D00EE1E3B6EC000FE033E /* LoopbackFS */ = { + isa = PBXNativeTarget; + buildConfigurationList = F48D00FC1E3B6EC000FE033E /* Build configuration list for PBXNativeTarget "LoopbackFS" */; + buildPhases = ( + F48D00EB1E3B6EC000FE033E /* Sources */, + F48D00EC1E3B6EC000FE033E /* Frameworks */, + F48D00ED1E3B6EC000FE033E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = LoopbackFS; + productName = LoopbackFS; + productReference = F48D00EF1E3B6EC000FE033E /* LoopbackFS.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F48D00E71E3B6EC000FE033E /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0820; + LastUpgradeCheck = 1240; + ORGANIZATIONNAME = "KF Interactive GmbH"; + TargetAttributes = { + F48D00EE1E3B6EC000FE033E = { + CreatedOnToolsVersion = 8.2.1; + LastSwiftMigration = 1240; + ProvisioningStyle = Manual; + SystemCapabilities = { + com.apple.HardenedRuntime = { + enabled = 1; + }; + }; + }; + }; + }; + buildConfigurationList = F48D00EA1E3B6EC000FE033E /* Build configuration list for PBXProject "LoopbackFS" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = F48D00E61E3B6EC000FE033E; + productRefGroup = F48D00F01E3B6EC000FE033E /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F48D00EE1E3B6EC000FE033E /* LoopbackFS */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + F48D00ED1E3B6EC000FE033E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F48D00F81E3B6EC000FE033E /* MainMenu.xib in Resources */, + 43E9539A2646E19C009CCB55 /* Assets.xcassets in Resources */, + F4D7409B1E65585B007D6E11 /* LICENSE.txt in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + F48D00EB1E3B6EC000FE033E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F48D00F31E3B6EC000FE033E /* AppDelegate.swift in Sources */, + F48D01091E3B7DA500FE033E /* NSError+POSIX.swift in Sources */, + F48D01071E3B74F900FE033E /* LoopbackFS.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + F48D00F61E3B6EC000FE033E /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + F48D00F71E3B6EC000FE033E /* Base */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + F48D00FA1E3B6EC000FE033E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CODE_SIGN_FLAGS = "--timestamp"; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + F48D00FB1E3B6EC000FE033E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CODE_SIGN_FLAGS = "--timestamp"; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + }; + name = Release; + }; + F48D00FD1E3B6EC000FE033E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = LoopbackFS/LoopbackFS.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + FRAMEWORK_SEARCH_PATHS = /Library/Frameworks; + INFOPLIST_FILE = LoopbackFS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; + PRODUCT_BUNDLE_IDENTIFIER = "io.macfuse.demo.loopbackfs-swift"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "LoopbackFS/Bridging-Header.h"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + F48D00FE1E3B6EC000FE033E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = LoopbackFS/LoopbackFS.entitlements; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + FRAMEWORK_SEARCH_PATHS = /Library/Frameworks; + INFOPLIST_FILE = LoopbackFS/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.12; + PRODUCT_BUNDLE_IDENTIFIER = "io.macfuse.demo.loopbackfs-swift"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OBJC_BRIDGING_HEADER = "LoopbackFS/Bridging-Header.h"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + F48D00EA1E3B6EC000FE033E /* Build configuration list for PBXProject "LoopbackFS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F48D00FA1E3B6EC000FE033E /* Debug */, + F48D00FB1E3B6EC000FE033E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F48D00FC1E3B6EC000FE033E /* Build configuration list for PBXNativeTarget "LoopbackFS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F48D00FD1E3B6EC000FE033E /* Debug */, + F48D00FE1E3B6EC000FE033E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F48D00E71E3B6EC000FE033E /* Project object */; +} diff --git a/LoopbackFS-Swift/LoopbackFS.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/LoopbackFS-Swift/LoopbackFS.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..b0bc26d --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/LoopbackFS-Swift/LoopbackFS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/LoopbackFS-Swift/LoopbackFS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/LoopbackFS-Swift/LoopbackFS/AppDelegate.swift b/LoopbackFS-Swift/LoopbackFS/AppDelegate.swift new file mode 100644 index 0000000..d7c58d2 --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS/AppDelegate.swift @@ -0,0 +1,101 @@ +// +// AppDelegate.swift +// LoopbackFS +// +// Created by Gunnar Herzog on 27/01/2017. +// Copyright © 2017 KF Interactive GmbH. All rights reserved. +// Copyright © 2019-2020 Benjamin Fleischer. All rights reserved. +// + +import Cocoa + +let loopbackMountPath = "/Volumes/loop" + +@NSApplicationMain +class AppDelegate: NSObject, NSApplicationDelegate { + + @IBOutlet weak var window: NSWindow! + + private var notificationObservers: [NSObjectProtocol] = [] + private var rootPath: String! + private lazy var loopFileSystem: LoopbackFS = { + return LoopbackFS(rootPath: self.rootPath) + }() + + private var userFileSystem: GMUserFileSystem? + + func applicationDidFinishLaunching(_ aNotification: Notification) { + let panel = NSOpenPanel() + panel.canChooseFiles = false + panel.canChooseDirectories = true + panel.allowsMultipleSelection = false + panel.directoryURL = URL(fileURLWithPath: "/tmp") + let returnValue = panel.runModal() + + guard returnValue.rawValue != NSFileHandlingPanelCancelButton, let rootPath = panel.urls.first?.path else { exit(0) } + + addNotifications() + + self.rootPath = rootPath + + var options: [String] = ["native_xattr", "volname=LoopbackFS"] + + if let volumeIconPath = Bundle.main.path(forResource: "LoopbackFS", ofType: "icns") { + options.insert("volicon=\(volumeIconPath)", at: 0) + } + + userFileSystem = GMUserFileSystem(delegate: self.loopFileSystem, isThreadSafe: false) + + // Do not use the 'native_xattr' mount-time option unless the underlying + // file system supports native extended attributes. Typically, the user + // would be mounting an HFS+ directory through LoopbackFS, so we do want + // this option in that case. + userFileSystem!.mount(atPath: loopbackMountPath, withOptions: options) + } + + func addNotifications() { + let mountObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name(kGMUserFileSystemDidMount), object: nil, queue: nil) { notification in + print("Got didMount notification.") + + let parentPath = (loopbackMountPath as NSString).deletingLastPathComponent + NSWorkspace.shared.selectFile(loopbackMountPath, inFileViewerRootedAtPath: parentPath) + } + + let failedObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name(kGMUserFileSystemMountFailed), object: nil, queue: .main) { notification in + print("Got mountFailed notification.") + + guard let userInfo = notification.userInfo, let error = userInfo[kGMUserFileSystemErrorKey] as? NSError else { return } + + print("kGMUserFileSystem Error: \(error), userInfo=\(error.userInfo)") + let alert = NSAlert() + alert.messageText = "Mount Failed" + alert.informativeText = error.localizedDescription + alert.runModal() + + NSApplication.shared.terminate(nil) + } + + let unmountObserver = NotificationCenter.default.addObserver(forName: NSNotification.Name(kGMUserFileSystemDidUnmount), object: nil, queue: nil) { notification in + print("Got didUnmount notification.") + + DispatchQueue.main.async { + NSApplication.shared.terminate(nil) + } + } + + self.notificationObservers = [mountObserver, failedObserver, unmountObserver] + } + + func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { + notificationObservers.forEach { + NotificationCenter.default.removeObserver($0) + } + notificationObservers.removeAll() + + if let userFileSystem = userFileSystem { + userFileSystem.unmount() + } + return .terminateNow + } +} + diff --git a/LoopbackFS-Swift/LoopbackFS/Assets.xcassets/AccentColor.colorset/Contents.json b/LoopbackFS-Swift/LoopbackFS/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LoopbackFS-Swift/LoopbackFS/Assets.xcassets/AppIcon.appiconset/Contents.json b/LoopbackFS-Swift/LoopbackFS/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..3f00db4 --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LoopbackFS-Swift/LoopbackFS/Assets.xcassets/Contents.json b/LoopbackFS-Swift/LoopbackFS/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LoopbackFS-Swift/LoopbackFS/Base.lproj/MainMenu.xib b/LoopbackFS-Swift/LoopbackFS/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..bd64660 --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS/Base.lproj/MainMenu.xib @@ -0,0 +1,680 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LoopbackFS-Swift/LoopbackFS/Bridging-Header.h b/LoopbackFS-Swift/LoopbackFS/Bridging-Header.h new file mode 100644 index 0000000..0f1c602 --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS/Bridging-Header.h @@ -0,0 +1,13 @@ +// +// Bridging-Header.h +// LoopbackFS +// +// Created by Gunnar Herzog on 27/01/2017. +// Copyright © 2017 KF Interactive GmbH. All rights reserved. +// Copyright © 2019 Benjamin Fleischer. All rights reserved. +// + +#import +#import + +#import diff --git a/LoopbackFS-Swift/LoopbackFS/Info.plist b/LoopbackFS-Swift/LoopbackFS/Info.plist new file mode 100644 index 0000000..a18ef54 --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2017 KF Interactive GmbH. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/LoopbackFS-Swift/LoopbackFS/LoopbackFS.entitlements b/LoopbackFS-Swift/LoopbackFS/LoopbackFS.entitlements new file mode 100644 index 0000000..8cc185a --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS/LoopbackFS.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.cs.disable-library-validation + + + diff --git a/LoopbackFS-Swift/LoopbackFS/LoopbackFS.swift b/LoopbackFS-Swift/LoopbackFS/LoopbackFS.swift new file mode 100644 index 0000000..77034c1 --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS/LoopbackFS.swift @@ -0,0 +1,359 @@ +// +// LoopbackFS.swift +// LoopbackFS +// +// Created by Gunnar Herzog on 27/01/2017. +// Copyright © 2017 KF Interactive GmbH. All rights reserved. +// Copyright © 2019-2020 Benjamin Fleischer. All rights reserved. +// + +import Foundation + +final class LoopbackFS: NSObject { + + let rootPath: String + + init(rootPath: String) { + self.rootPath = rootPath + } + + // MARK: - Moving an Item + + override func moveItem(atPath source: String!, toPath destination: String!, options: GMUserFileSystemMoveOption) throws { + let sourcePath = (rootPath.appending(source) as NSString).utf8String! + let destinationPath = (rootPath.appending(destination) as NSString).utf8String! + + var returnValue: Int32 = 0 + if options.rawValue == 0 { + returnValue = rename(sourcePath, destinationPath) + } else { + if #available(OSX 10.12, *) { + var flags: UInt32 = 0; + if options.rawValue & GMUserFileSystemMoveOption.swap.rawValue != 0 { + flags |= UInt32(RENAME_SWAP); + } + if options.rawValue & GMUserFileSystemMoveOption.exclusive.rawValue != 0 { + flags |= UInt32(RENAME_EXCL); + } + returnValue = renamex_np(sourcePath, destinationPath, flags) + } else { + throw NSError(posixErrorCode: ENOTSUP); + }; + } + if returnValue < 0 { + throw NSError(posixErrorCode: errno) + } + } + + // MARK: - Removing an Item + + override func removeDirectory(atPath path: String!) throws { + // We need to special-case directories here and use the bsd API since + // NSFileManager will happily do a recursive remove :-( + + let originalPath = (rootPath.appending(path) as NSString).utf8String! + + let returnValue = rmdir(originalPath) + if returnValue < 0 { + throw NSError(posixErrorCode: errno) + } + } + + override func removeItem(atPath path: String!) throws { + let originalPath = rootPath.appending(path) + + return try FileManager.default.removeItem(atPath: originalPath) + } + + // MARK: - Creating an Item + + override func createDirectory(atPath path: String!, attributes: [AnyHashable : Any]! = [:]) throws { + guard let attributes = attributes as? [String: Any] else { throw NSError(posixErrorCode: EPERM) } + + let originalPath = rootPath.appending(path) + + try FileManager.default.createDirectory(atPath: originalPath, withIntermediateDirectories: false, attributes: convertToOptionalFileAttributeKeyDictionary(attributes)) + } + + override func createFile(atPath path: String!, attributes: [AnyHashable : Any]! = [:], flags: Int32, userData: AutoreleasingUnsafeMutablePointer!) throws { + + guard let mode = attributes[FileAttributeKey.posixPermissions] as? mode_t else { + throw NSError(posixErrorCode: EPERM) + } + + let originalPath = rootPath.appending(path) + + let fileDescriptor = open((originalPath as NSString).utf8String!, flags, mode) + + if fileDescriptor < 0 { + throw NSError(posixErrorCode: errno) + } + + userData.pointee = NSNumber(value: fileDescriptor) + } + + // MARK: - Linking an Item + + override func linkItem(atPath path: String!, toPath otherPath: String!) throws { + let originalPath = (rootPath.appending(path) as NSString).utf8String! + let originalOtherPath = (rootPath.appending(otherPath) as NSString).utf8String! + + // We use link rather than the NSFileManager equivalent because it will copy + // the file rather than hard link if part of the root path is a symlink. + if link(originalPath, originalOtherPath) < 0 { + throw NSError(posixErrorCode: errno) + } + } + + // MARK: - Symbolic Links + + override func createSymbolicLink(atPath path: String!, withDestinationPath otherPath: String!) throws { + let sourcePath = rootPath.appending(path) + try FileManager.default.createSymbolicLink(atPath: sourcePath, withDestinationPath: otherPath) + } + + override func destinationOfSymbolicLink(atPath path: String!) throws -> String { + let sourcePath = rootPath.appending(path) + return try FileManager.default.destinationOfSymbolicLink(atPath: sourcePath) + } + + // MARK: - File Contents + + override func openFile(atPath path: String!, mode: Int32, userData: AutoreleasingUnsafeMutablePointer!) throws { + let originalPath = (rootPath.appending(path) as NSString).utf8String! + + let fileDescriptor = open(originalPath, mode) + + if fileDescriptor < 0 { + throw NSError(posixErrorCode: errno) + } + + userData.pointee = NSNumber(value: fileDescriptor) + } + + override func releaseFile(atPath path: String!, userData: Any!) { + guard let num = userData as? NSNumber else { + return + } + + let fileDescriptor = num.int32Value + close(fileDescriptor) + } + + override func readFile(atPath path: String!, userData: Any!, buffer: UnsafeMutablePointer!, size: Int, offset: off_t, error: NSErrorPointer) -> Int32 { + guard let num = userData as? NSNumber else { + error?.pointee = NSError(posixErrorCode: EBADF) + return -1 + } + + let fileDescriptor = num.int32Value + let returnValue = Int32(pread(fileDescriptor, buffer, size, offset)) + + if returnValue < 0 { + error?.pointee = NSError(posixErrorCode: errno) + return -1 + } + return returnValue + } + + override func writeFile(atPath path: String!, userData: Any!, buffer: UnsafePointer!, size: Int, offset: off_t, error: NSErrorPointer) -> Int32 { + guard let num = userData as? NSNumber else { + error?.pointee = NSError(posixErrorCode: EBADF) + return -1 + } + + let fileDescriptor = num.int32Value + + let returnValue = pwrite(fileDescriptor, buffer, size, offset) + if returnValue < 0 { + error?.pointee = NSError(posixErrorCode: errno) + } + return Int32(returnValue) + } + + override func preallocateFile(atPath path: String!, userData: Any!, options: Int32, offset: off_t, length: off_t) throws { + guard let num = userData as? NSNumber else { + throw NSError(posixErrorCode: EBADF) + } + + let fileDescriptor = num.int32Value + + var fstore = fstore_t() + if options & ALLOCATECONTIG == 1 { + fstore.fst_flags = UInt32(F_ALLOCATECONTIG) + } + if options & ALLOCATEALL == 1 { + fstore.fst_flags = fstore.fst_flags & UInt32(ALLOCATEALL) + } + if options & ALLOCATEFROMPEOF == 1 { + fstore.fst_posmode = F_PEOFPOSMODE + } else if options & ALLOCATEFROMVOL == 1 { + fstore.fst_posmode = F_VOLPOSMODE + } + fstore.fst_offset = offset + fstore.fst_length = length + if fcntl(fileDescriptor, F_PREALLOCATE, &fstore) == -1 { + throw NSError(posixErrorCode: errno) + } + } + + public override func exchangeDataOfItem(atPath path1: String!, withItemAtPath path2: String!) throws { + let sourcePath = (rootPath.appending(path1) as NSString).utf8String! + let destinationPath = (rootPath.appending(path2) as NSString).utf8String! + + let returnValue = exchangedata(sourcePath, destinationPath, 0) + if returnValue < 0 { + throw NSError(posixErrorCode: errno) + } + } + + // MARK: - Directory Contents + + override func contentsOfDirectory(atPath path: String!) throws -> [Any] { + let originalPath = rootPath.appending(path) + return try FileManager.default.contentsOfDirectory(atPath: originalPath) + } + + // MARK: - Getting and Setting Attributes + + override func attributesOfItem(atPath path: String!, userData: Any!) throws -> [AnyHashable : Any] { + let originalPath = rootPath.appending(path) + return try FileManager.default.attributesOfItem(atPath: originalPath) + } + + override func attributesOfFileSystem(forPath path: String!) throws -> [AnyHashable : Any] { + let originalPath = rootPath.appending(path) + + var attributes = try FileManager.default.attributesOfFileSystem(forPath: originalPath) + attributes[FileAttributeKey(rawValue: kGMUserFileSystemVolumeSupportsExtendedDatesKey)] = true + + let originalUrl = URL(fileURLWithPath: originalPath, isDirectory: true) + + let volumeSupportsCaseSensitiveNames = try originalUrl.resourceValues(forKeys: [.volumeSupportsCaseSensitiveNamesKey]).volumeSupportsCaseSensitiveNames ?? true + attributes[FileAttributeKey(rawValue: kGMUserFileSystemVolumeSupportsCaseSensitiveNamesKey)] = volumeSupportsCaseSensitiveNames + + attributes[FileAttributeKey(rawValue: kGMUserFileSystemVolumeSupportsSwapRenamingKey)] = true + attributes[FileAttributeKey(rawValue: kGMUserFileSystemVolumeSupportsExclusiveRenamingKey)] = true + + attributes[FileAttributeKey(rawValue: kGMUserFileSystemVolumeSupportsSetVolumeNameKey)] = true + + return attributes + } + + override func setAttributes(_ attributes: [AnyHashable : Any]!, ofItemAtPath path: String!, userData: Any!) throws { + guard let attribs = attributes as? [FileAttributeKey: Any] else { throw NSError(posixErrorCode: EINVAL) } + + let originalPath = rootPath.appending(path) + + if let pathPointer = (originalPath as NSString).utf8String { + if let offset = attributes[FileAttributeKey.size.rawValue] as? Int64 { + let ret = truncate(pathPointer, offset) + if ret < 0 { + throw NSError(posixErrorCode: errno) + } + } + + if let flags = attributes[kGMUserFileSystemFileFlagsKey] as? Int32 { + let rc = chflags(pathPointer, UInt32(flags)) + if rc < 0 { + throw NSError(posixErrorCode: errno) + } + } + } + + try FileManager.default.setAttributes(attribs, ofItemAtPath: originalPath) + } + + override func setAttributes(_ attributes: [AnyHashable : Any]!, ofFileSystemAtPath path: String!) throws { + // Needed for kGMUserFileSystemVolumeSupportsSetVolumeNameKey + } + + // MARK: - Extended Attributes + + public override func extendedAttributesOfItem(atPath path: Any!) throws -> [Any] { + guard let path = path as? String else { + throw NSError(posixErrorCode: ENODEV) + } + + let originalUrl = URL(fileURLWithPath: rootPath.appending(path)) + + return try originalUrl.withUnsafeFileSystemRepresentation { fileSystemPath -> [String] in + let length = listxattr(fileSystemPath, nil, 0, 0) + guard length >= 0 else { throw NSError(posixErrorCode: errno) } + + // Create buffer with required size: + var data = Data(count: length) + + // Retrieve attribute list: + let count = data.count + let result = data.withUnsafeMutableBytes { + listxattr(fileSystemPath, $0.baseAddress?.assumingMemoryBound(to: Int8.self), count, XATTR_NOFOLLOW) + } + guard result >= 0 else { throw NSError(posixErrorCode: errno) } + + // Extract attribute names: + let list = data.split(separator: 0).compactMap { + String(data: Data($0), encoding: .utf8) + } + return list + } + } + + public override func value(ofExtendedAttribute name: String!, ofItemAtPath path: String!, position: off_t) throws -> Data { + let originalUrl = URL(fileURLWithPath: rootPath.appending(path)) + + return try originalUrl.withUnsafeFileSystemRepresentation { fileSystemPath -> Data in + + // Determine attribute size: + let length = getxattr(fileSystemPath, name, nil, 0, UInt32(position), XATTR_NOFOLLOW) + guard length >= 0 else { + throw NSError(posixErrorCode: errno) + } + + // Create buffer with required size: + var data = Data(count: length) + + // Retrieve attribute: + let count = data.count + let result = data.withUnsafeMutableBytes { + getxattr(fileSystemPath, name, $0.baseAddress?.assumingMemoryBound(to: Int8.self), count, UInt32(position), XATTR_NOFOLLOW) + } + guard result >= 0 else { + throw NSError(posixErrorCode: errno) + } + return data + } + } + + public override func setExtendedAttribute(_ name: String!, ofItemAtPath path: String!, value: Data!, position: off_t, options: Int32) throws { + let originalUrl = URL(fileURLWithPath: rootPath.appending(path)) + + try originalUrl.withUnsafeFileSystemRepresentation { fileSystemPath in + // Setting com.apple.FinderInfo happens in the kernel, so security related + // bits are set in the options. We need to explicitly remove them or the call + // to setxattr will fail. + // TODO: Why is this necessary? + let newOptions = options & ~(XATTR_NOSECURITY | XATTR_NODEFAULT) + + let result = value.withUnsafeBytes { + setxattr(fileSystemPath, name, $0.baseAddress?.assumingMemoryBound(to: Int8.self), value.count, UInt32(position), newOptions | XATTR_NOFOLLOW) + } + guard result >= 0 else { throw NSError(posixErrorCode: errno) } + } + } + + public override func removeExtendedAttribute(_ name: String!, ofItemAtPath path: String!) throws { + let originalUrl = URL(fileURLWithPath: rootPath.appending(path)) + + try originalUrl.withUnsafeFileSystemRepresentation { fileSystemPath in + let result = removexattr(fileSystemPath, name, XATTR_NOFOLLOW) + guard result >= 0 else { throw NSError(posixErrorCode: errno) } + } + } +} + +// Helper function inserted by Swift 4.2 migrator. +fileprivate func convertToOptionalFileAttributeKeyDictionary(_ input: [String: Any]?) -> [FileAttributeKey: Any]? { + guard let input = input else { return nil } + return Dictionary(uniqueKeysWithValues: input.map { key, value in (FileAttributeKey(rawValue: key), value)}) +} diff --git a/LoopbackFS-Swift/LoopbackFS/NSError+POSIX.swift b/LoopbackFS-Swift/LoopbackFS/NSError+POSIX.swift new file mode 100644 index 0000000..28dd11f --- /dev/null +++ b/LoopbackFS-Swift/LoopbackFS/NSError+POSIX.swift @@ -0,0 +1,15 @@ +// +// NSError+POSIX.swift +// LoopbackFS +// +// Created by Gunnar Herzog on 27/01/2017. +// Copyright © 2017 KF Interactive GmbH. All rights reserved. +// + +import Foundation + +extension NSError { + convenience init(posixErrorCode err: Int32) { + self.init(domain: NSPOSIXErrorDomain, code: Int(err), userInfo: [NSLocalizedDescriptionKey: String(cString: strerror(err))]) + } +}